c语言中malloc函数的一个陷阱(二级指针的使用)

c语言练习63:用malloc开辟二维数组的三种办法 使用malloc函数模拟开辟一个3*5的整型二维数组,开辟好后,使用二维数组的下标访问形式,访问空间。int (*p)[4]:通过指针表示二维数组。 阅读详情

前段时间,学生在使用malloc分配int型数组空间的过程中,遇到了一点问题。

一、正确的malloc使用

看下面的程序:

/* malloc_right.c */
#include <stdio.h>
#include <malloc.h>

int * init_int_arr()
{
    int * temp_arr = (int *)malloc(4 * sizeof(int));
    for(int i = 0; i < 4; i++) {
        *(temp_arr + i) = i * 2;
    }
    return temp_arr;
}

int main(void)
{
    int * int_arr;
    
    int_arr = init_int_arr();
    for(int i = 0; i < 4; i++){
        printf("[%d]: %d\n", i, int_arr[i]);
    }
    
    return 0;
}

编译,运行:

看到,编译过程中,没有错误,而且得到了预期的结果。这种用法是正确的。

二、错误的malloc使用

看下面程序:

/* malloc_error.c */
#include <stdio.h>
#include <malloc.h>

void init_int_arr(int * temp_arr)
{
    temp_arr = (int *)malloc(4 * sizeof(int));
    for(int i = 0; i < 4; i++) {
        *(temp_arr + i) = i * 2;
    }
}

int main(void)
{
    int * int_arr;
    init_int_arr(int_arr);

    for(int i = 0; i < 4; i++){
        printf("[%d]: %d\n", i, int_arr[i]);
    }

    return 0;
}

编译,运行如下:

看到,编译过程没有报错,但是运行时得到了不可知的结果。我们对这个程序进行一下测试。看下面程序:

/* test_malloc_error.c */
#include <stdio.h>
#include <malloc.h>

void init_int_arr(int * temp_arr)
{
    temp_arr = (int *)malloc(4 * sizeof(int));
    printf("in malloc int_arr address: %p\n", temp_arr);
    for(int i = 0; i < 4; i++) {
        *(temp_arr + i) = i * 2;
    }
}

int main(void)
{
    int * int_arr;

    printf("before malloc int_arr address: %p\n", int_arr);
    init_int_arr(int_arr);
    printf("after malloc int_arr address: %p\n", int_arr);

    return 0;
}

编译,运行后为:

可以看到,在对int_arr分配空间前,其地址为:0000000000000010,在初始化函数init_int_arr()中进行空间分配时,操作的地址为:0000000000787510,但是当从init_int_arr()跳出后这个地址并没有被返回给int_arr变量。

这实际是指针运用的一个陷阱,只需要记住一条,指针也是变量。

这有点类似于如下的程序:

/* change_int.c */
#include <stdio.h>

void change_int(int t)
{
    t = 5;
}

int main(void)
{
    int a = 3;
    printf("before change a = %d\n", a);
    change_int(a);
    printf("after change a = %d\n", a);

    return 0;
}

这种方式不会使a的值发生改变。

三、二级指针的使用

那可不可以不使用返回值的方式,对int数组进行空间分配呢,可以,采用二级指针。

如下程序:

/* malloc_pp.c */
#include <stdio.h>
#include <malloc.h>

void init_int_arr(int** temp_arr)
{
    *temp_arr = (int *)malloc(4 * sizeof(int));
    for(int i = 0; i < 4; i++) {
        *(*temp_arr + i) = i * 2;
    }
}

int main(void)
{
    int** int_arr;
    init_int_arr(int_arr);

    /*------------------------- print -------------------------*/
    for(int i = 0; i < 4; i++){
        printf("[%d]: %d\n", i, (*int_arr)[i]);
    }

    return 0;
}

编译,运行如下:

这里采用二级指针的方式,便可以将int_arr在函数中直接初始化了。

四、模拟动态数组

这种方式配合realloc函数可以模拟动态数组。如下程序:

/* relloc_pp.c */
#include <stdio.h>
#include <stdlib.h>

void init_int_arr(int** temp_arr, int size)
{
    *temp_arr = (int *)malloc(size * sizeof(int));
}

void update_int_arr(int ** temp_arr, int size)
{
    *temp_arr = (int *)realloc(* temp_arr, size * sizeof(int));
}

int main(void)
{
    int ** p_int_arr;
    int *int_arr;

    p_int_arr = &int_arr;
    init_int_arr(p_int_arr, 4);
    int_arr[0] = 2;
    int_arr[1] = 4;
    int_arr[2] = 6;
    int_arr[3] = 8;
    for(int i = 0; i < 4; i++){
        printf("[%d]: %d\n", i, int_arr[i]);
    }
    
    printf("---------------- update --------------------\n");
    update_int_arr(p_int_arr, 8);
    int_arr[0] = 12;
    int_arr[1] = 14;
    int_arr[2] = 16;
    int_arr[3] = 18;
    int_arr[4] = 20;
    int_arr[5] = 40;
    int_arr[6] = 60;
    int_arr[7] = 80;
    for(int i = 0; i < 8; i++){
        printf("[%d]: %d\n", i, int_arr[i]);
    }

    return 0;
}

编译,运行如下:

数组长度发生了变化。

五、结论

1、使用malloc分配空间时,要使用返回空间首地址形式;

2、如果在初始化函数中以参数传递形式分配空间,可以使用二级指针;

3、利用二级指针配合realloc函数可以模拟动态数组。

C语言进阶:动态分配内存,二维指针 文章目录动态分配内存1.数组空间申请2.重新调整内存大小3. 数组的整体拷贝、比较、移动二维指针1. 二维指针的用法2. 动态内存创建二维数组 动态分配内存 1.数组空间申请 申请动态分配内存 malloc() stdlib.h 中定义函数 void* malloc(size_t size) 向系统申请大小为 size 的内存空间 返回结果是 void*,使用时转换成需要的指针类型 如果申请失败,返回 NULL 向系统申请一块 n*4 字节的空间 (int*)malloc(n*sizeof(int)) #i 阅读详情

相关推荐

C语言指针从入门到精通:内存地址、数组、函数指针与动态内存管理

在计算机编程中,内存地址是数据存储位置的唯一标识,而指针则是存储这些地址的变量,它实现了对内存的直接访问。其核心原理在于通过取址(&)和解引用(*)操作,程序能够间接读写特定内存单元的数据,这构成了底层系统编程和高效数据处理的基石。从技术价值看,指针极大地提升了程序的灵活性与效率,允许动态内存分配、复杂数据结构(如链表、树)的构建以及函数回调机制的实现。在应用场景上,指针广泛应用于操作系统内核、嵌入式开发、性能敏感的算法库以及需要精细内存管理的场景。本文以C语言为例,深入剖析了指针与数组的紧密关系、函数指针

weixin_34034261的博客 311

一级指针与二级指针

理解指针的内存形式

weixin_42146864的博客 1233

函数与指针

概念核心口诀关键注意事项指针运算加减看类型,相减算元素int *p移动一次是 4 字节,不是 1 字节。常量指针const在 * 左,值不能改:保护数据不被修改。指针常量const在 * 右,指向不能改:保护指针指向不被修改。内存分配malloc 不初始化,calloc 全为 0记得,用完必须free。文件打开w 清空,a 追加,r 必须存在二进制文件读写要用rbwb模式。

weixin_61493641的博客 379

二级指针 malloc和memcpy

二级指针 malloc和memcpy

子歌的博客 812

C语言指针详解:用买房比喻彻底搞懂内存地址与指针操作

在计算机编程中,内存管理是核心基础概念,它决定了程序如何存储和访问数据。其原理在于操作系统为程序分配连续的内存空间,每个存储单元都有唯一地址。这项技术的价值在于实现高效的数据操作和资源利用,是系统编程和性能优化的基石。应用场景广泛,从嵌入式开发到操作系统内核都离不开精细的内存控制。本文以C语言指针为例,通过房产证与房子的生动比喻,深入浅出地解析了**内存地址**的访问机制和**指针变量**的操作逻辑,帮助读者建立直观的内存模型,掌握指针运算、动态内存管理等关键技能,规避野指针和内存泄漏等常见陷阱。

weixin_34080571的博客 386

全面地理解为什么在C语言中使用`malloc`分配内存时需要二级指针。

C语言中使用malloc分配内存时需要二级指针。

weixin_39291964的博客 516

嵌入式C语言二级指针详解:改外部指针、Linus链表、输出型参数实战

摘要: 二级指针在C语言中至关重要,尤其在嵌入式开发中。其本质是存储指针变量地址的指针,常用于函数内修改外部指针、简化链表操作、管理动态内存及实现模块注册机制。典型场景包括: 函数内修改外部指针:通过传递指针地址(二级指针)直接修改原指针,避免值传递失效。 链表操作:消除头节点与中间节点的特殊判断,代码更简洁(如Linus推崇的删除写法)。 **指针数组管理

dozenyaoyida的博客 704

浅谈Mike 3D中制作垂向水温分层边界

本次以水温分层为例进行讲解,可应用于水库的水温分层模拟中

zhuzhujiayoua的博客 1408

指针进阶C语言中多级指针与动态内存管理的深度剖析

多级指针与动态内存管理是C语言编程中兼具威力和复杂性的核心领域。通过多级指针,程序员能够构建灵活高效的动态数据结构,但同时也必须承担起精细管理内存的责任。深入理解每一级指针所代表的内存含义,严格遵守内存分配与释放的配对和顺序原则,是写出健壮、无内存泄露的C程序的关键。这要求开发者不仅掌握语法,更要从计算机系统层面理解内存的运作机制,从而驾驭好这把“双刃剑”。

qq_35442692的博客 570

Android WIFI体系:深入解析WifiLock与MulticastLock的底层实现与性能优化

本文深入解析Android WIFI体系中WifiLock与MulticastLock的底层实现机制与性能优化策略。WifiLock通过阻止Wi-Fi休眠提升网络性能,尤其WIFI_MODE_FULL_HIGH_PERF模式可显著降低延迟;MulticastLock则控制组播数据接收,但会增加功耗。文章详细探讨了其与wpa_supplicant、HAL层的交互,并提供高性能场景下的优化实践与功耗平衡方案。

php55的博客 1079

SAP采购申请详述;以及采购计划协议/交货计划行

SAP采购申请详述;以及采购计划协议/交货计划行

saplakes的博客 4341

《C语言学习:编程例题》C

(2)在现有链表基础上拆分,不需要在每个循环里malloc给新结点p内存,直接定义p并且让p指向*L(头结点),然后再另外定义一个结点next。(4)由于原先单链表里,每个结点(除尾结点外)的next都指向下一个结点,所以需要在建立新链表时让这些next置空,防止新链表最后一个结点指向原链表里的结点。如果非空,说明此时链表里已经有结点,让tail.next=p(未更新的链表尾结点指向p,把p加入),再让tail=p(更新链表的尾结点为p)。(1)传入函数的是**L,其中*L是链表,L是链表头结点地址。

weixin_67601118的博客 252

芯片设计-信号完整性 SI 解析:Unit Interval 与比特周期的关系及其在高速信号中的应用

本文深入解析了芯片设计中信号完整性(SI)的核心概念Unit Interval(UI)与比特周期的关系及其在高速信号中的应用。通过实际案例和计算公式,详细阐述了UI在不同数据速率下的变化、在眼图分析中的关键作用,以及如何应对高速信号设计中的抖动和码间干扰等挑战。文章还提供了PCB布线和芯片设计中的UI优化技巧,帮助工程师提升高速信号设计的实战能力。

weixin_28683391的博客 468

完整且详细的C语言教程,从入门到精通(7.1)--C语言指针

指针是C语言的灵魂,也是让许多人头疼的难点。它本质上是一种变量,但存储的不是普通数据,而是内存地址。

2402_84932195的博客 234

CPU也能跑大模型?手把手教你用vLLM离线部署Qwen2.5-7B-Instruct(附性能优化技巧)

本文详细介绍了如何在CPU服务器上高效部署Qwen2.5-7B-Instruct模型,通过vLLM框架和一系列优化技巧实现离线推理。内容包括环境配置、模型加载优化、性能调优及生产环境部署方案,帮助开发者在资源有限的情况下也能运行大语言模型。

weixin_28691741的博客 489

YOLOv8 交通违规检测各个场景的实际应用

本文介绍了基于YOLOv8的交通违规检测系统在不同场景下的应用实现。系统可检测闯红灯、超速、违停和不系安全带等多种违规行为,针对每种场景提供了具体的技术方案和Python实现代码。其中闯红灯检测结合信号灯状态分析,超速检测通过距离时间计算速度,违停检测采用长时间跟踪策略,安全带检测则需更高精度识别。文章还展示了如何整合各功能构建综合交通监管系统,并总结了不同应用场景的技术要点和优化方向,为智能交通管理提供了实用解决方案。

u010117029的博客 299

C语言学习笔记(十一):指针和动态内存管理

函数功能是否初始化失败返回申请size字节❌ 不初始化NULL申请num*size字节✅ 全初始化为 0NULL调整内存大小不初始化(新空间不保证)NULLfree(ptr)释放动态内存最佳实践malloccallocrealloc后必须检查返回值是否为NULL。free后必须将指针置为NULL,避免悬空指针。用realloc时先用临时变量接收返回值,避免失败时丢失原指针。谁申请,谁释放,避免内存泄漏。柔性数组是 C99 的特性,可以一次分配、一次释放,提高代码可维护性。

2501_91314704的博客 207

【docker】第10篇 Failed with result ‘exit-code和Start request repeated too quickly和Failed to start Docker

【docker】解决 Failed with result 'exit-code'和Start request repeated too quickly和Failed to start Docker。

Fanjufei的博客 6819

C语言-指针、数组、结构体、动态内存分配复习记录

int age;// 直接访问成员// 箭头运算符访问成员概念说明int *p[5]指针数组:包含 5 个int*元素的数组数组指针:指向含有 5 个 int 的一维数组的指针数组名在sizeof中不退化,返回整个数组字节数sizeof(p)指针变量的大小(通常 8 字节在 64 位系统)结构体赋值结构体可以直接赋值(逐成员拷贝),但含有指针时需深拷贝动态数组扩容使用realloc,但注意原指针可能被移动。

W2834_的博客 215

stm32项目实战(1)光敏电阻的介绍和使用

文章目录前言一、光敏电阻介绍二、cubeMX配置三、代码说明总结 前言 本篇文章我们讲带大家了解光敏电阻的使用 一、光敏电阻介绍 下面是4线光敏电阻实物图 产品接线说明: 1、VCC 接电源正极 3.3-5V 2、GND 接电源负极 3、DO TTL 开关信号输出 (配置为输出模式) 4、AO 模拟信号输出(ADC采样通道) 光敏电阻主要用于检测周围的光照强度,光照强度发生变化那么ADC采样的值也会发送变化。 二、cubeMX配置 简单的配置我们就不多说了主要是ADC采样的配置 这里我采用的是ADC1通道

m0_49476241的博客 1万+

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

C语言指针从入门到精通:内存地址、数组、函数与动态内存管理

指针是计算机编程中实现内存间接访问的核心机制,它本质上是一个存储内存地址的变量。其工作原理是通过地址值来定位和操作内存中的数据,这种间接性为程序带来了极高的灵活性和效率,是系统级编程、嵌入式开发等底层软件开发的技术基石。在工程实践中,指针广泛应用于数组遍历、函数参数传递、动态内存分配以及数据结构实现等场景。理解指针与数组的等价关系、掌握函数指针的回调机制、以及规避野指针和内存泄漏等常见错误,是编写健壮高效C/C++代码的关键。本文通过酒店房间的生动比喻,深入解析了指针声明、解引用、多级指针等核心概念,并涵盖

weixin_34220179的博客 410

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【2个月 C 语言从入门到精通:零基础系统教程】第十五讲:动态内存管理

本文系统讲解了C语言动态内存管理的核心概念和关键技术。首先分析了传统静态内存分配的局限性,引出了动态内存分配的必要性。重点介绍了四个关键函数:malloc用于申请内存、free用于释放内存、calloc能初始化分配的内存、realloc可调整已分配内存大小。文章详细剖析了常见的内存管理错误,如空指针解引用、越界访问、错误释放等,并通过经典笔试题加深理解。最后介绍了柔性数组特性和程序内存区域划分。全文强调了动态内存管理的重要性,并提供了使用这些函数时的最佳实践和注意事项,帮助开发者避免内存泄漏和访问错误。

专注计算机考研408,C语言入门,C++学习,分享学习笔记,代码实践,和大家一起进步 486

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

C语言内容知识总结(6.指针与内存管理)

内存与地址:内存被划分为一个个单元,每个单元1字节,每个单元有一个编号,这个编号就是地址。地址 = 指针。取地址(&)和解引用(*)::取变量 a 的地址:通过地址 p 访问该地址上存储的数据指针变量的定义: 指针大小:32位系统4字节,64位系统8字节。所有指针类型大小相同。空指针 NULL: 指针类型的意义:决定解引用权限: 访问4字节, 访问1字节决定步长: 跳4字节, 跳1字节 1.3踩坑记录 1 写顺序表时, 里 忘记加括号,写成 ,编译报错才知道 优先级高于 2 后经常忘记

2502_94558334的博客 919

C语言指针深度解析:从内存模型到动态分配与安全实践

指针是C语言中实现内存直接寻址的核心机制,它本质上是一个存储内存地址的变量。从原理上看,指针通过地址运算符(&)和解引用运算符(*)实现了对内存数据的间接访问,这种能力使得程序能够高效地操作内存,是理解计算机底层运行逻辑的关键。在技术价值层面,指针不仅实现了函数参数的高效传递,避免了大型数据结构的复制开销,更是动态内存管理的基础,通过malloc、free等函数实现了运行时内存的灵活分配与释放。在应用场景上,指针广泛应用于数组遍历、字符串处理、构建链表与树等复杂数据结构,以及实现函数间的数据共享与修改。本文

weixin_30550081的博客 401

c问题总结(2)

glibc 会返回合法可 free 的地址,但禁止解引用,部分库返回 NULL,不能仅靠判空判断申请成功。内存碎片分外部碎片、内部碎片,前者由频繁堆申请释放产生大量离散空闲小块;后者分配内存大于实际需求,。区分常量指针、指针常量,把握 const 修饰对象。宏为预处理文本替换,无类型检查,存在参数副作用等坑;inline 是编译期代码展开,具备类型检查。结构体内存对齐提升访问效率,ARM 平台非对齐访问会直接崩溃。。static可修饰局部变量、全局变量、函数,分别改变生命周期或作用域;extern声明外部符

2401_88413914的博客 214

动态内存管理

NULL) 判断为真,执行 strcpy(str, "world") 是对已释放内存的非法访问,触发未定义行为。有时候我们所需要的空间大小在程序运行时才能知道,那数组的编译时开辟空间的方式就无法满足。(1)free(str) 执行后,str 指向的堆内存已经被释放,这块内存不再属于程序。(2)但 str 本身的值没有被修改,仍然指向原来的地址,此时 str 成为野指针。(类比:微信好友给你发了一张图片,你改了图片内容,但好友的图片内容没有被修改)参数ptr:指向要释放的内存块的指针。

2601_95808032的博客 222
上一篇: C和C++关于const关键字的不同
下一篇: C语言传回多个参数
金达南
博客等级 码龄3年 137粉丝 12原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值