C语言结构体指针成员内存分配与三种malloc写法差异咨询
测试代码合法性说明
你提供的这段测试代码不存在未定义行为,是完全符合C标准的合法代码。
结构体char*成员赋值问题说明
结构体的char*类型成员本质就是普通的指针变量,没有必须手动分配内存才能使用的强制要求。直接为其赋值字符串常量的写法完全合法:C语言中字符串常量存储在程序的静态只读存储区,赋值操作只是把字符串常量的首地址写入指针变量s,这个过程没有任何非法操作。
唯一需要注意的限制:后续不能通过s指针修改指向的字符串常量内容,比如执行x->s[0] = 'a'这类写操作才会触发未定义行为,因为字符串常量的内存是只读的,单纯的指针赋值操作没有问题。
三种malloc写法差异说明
第一种写法:struct X* x = (struct X*)malloc(sizeof(struct X));
这个写法功能正确,会分配和struct X大小匹配的内存块。缺点是如果后续修改了x的类型(比如把指针类型改成struct Y*),需要同步修改sizeof里的结构体类型名,维护成本稍高。另外C语言标准允许malloc返回的void*指针自动转换为任意类型的指针,这里的强制类型转换是冗余的,不过写了也不会影响程序运行,只有C++场景下才必须加这个强制转换。
第二种写法:struct X* x = (struct X*)malloc(sizeof(x));
这个是完全错误的写法。x本身是struct X*类型的指针变量,sizeof(x)取到的是指针本身的内存大小,32位系统下是4字节,64位系统下是8字节,远小于struct X结构体的实际占用大小。分配的内存不足,后续对x指向的结构体成员赋值时会发生堆内存越界,属于典型的未定义行为,大概率会导致程序崩溃或者数据污染。
第三种写法:struct X* x = (struct X*)malloc(sizeof *x);
这是工业界最推荐的写法,功能完全正确,可维护性最高。*x的类型就是struct X,所以sizeof *x会自动计算得到结构体的正确大小,就算后续修改了x的指针类型,这部分代码也不需要同步修改,不会出现第一种写法的维护问题,也完全规避了第二种写法的大小错误问题。同样这里的强制类型转换在C语言场景下是冗余可省略的。
内容的提问来源于stack exchange,提问作者user16464042

