引言

目前国内大量高校和教材的C语言以及数据结构等代码中,还存在在各种不珂学的写法,准备做一系列文章来对国内教材等内容进行一些 指指点点

首先批判一下大量教材中 C 语言中的 malloc用法。

例子

假设我们有一个结构体:

1
2
3
4
typedef struct {
char name[100];
int a;
} Node;

然后我们需要动态分配长度为 n 的结构体数组:

1
2
3
4
int main() {
int n = 10;
Node* arr = (Node*)malloc(sizeof(Node) * n);
}

问题分析1

留意一下这个代码,有什么问题吗?

第一个问题是,在调用malloc的时候,我们并没有做原型声明,即使用#include <stdlib.h> 或者#include <malloc.h>,在旧式的编译器中,如果没有原型声明的函数,默认返回值就是 int类型。

所以,在 64 位操作系统上,int一般是 4 字节的,而指针类型是 8 字节的,这样做强制类型转换,实际上是将int类型转换成void*类型,这就出现了问题:int 类型和void*类型无法进行无损的转换

并且,在你忘记使用头文件后,又做了强制类型转换,编译器反而无法进行警告,可能会带来一些问题。

问题分析2

其次,在 ANSI C 中,malloc()的返回值类型已经从老旧版本的char*变成了void*,因为char*无法直接复制给其他类型的指针变量,所以必须进行强制类型转换,而void*指针是可以不经转换就直接赋值给(除函数指针外)的所有类型的指针变量,所以强制类型转换已经没有必要。

所以我们没有必要再画蛇添足,进行多余的类型转换处理,使得代码更加清晰易读。

异议

有的人说使用强制类型转换是用来兼容C++,如果程序考虑兼容的话,那确实应该使用强制类型转换,不过为什么不使用 C++ 自带的 new运算符代替malloc呢?

总结

在 C 语言中,我们使用malloc(realloc和calloc也一样)就没必要再强制类型转换,所以上面的代码应该改成如下内容:

1
2
3
4
5
#include <stdlib.h>
int main() {
int n = 10;
Node* arr = malloc(sizeof(Node) * n);
}

在 C++ 中,我们应该使用 new运算符来替代 malloc

另外:

在C++中,处理内存重新分配的更好的方法是,使用标准库中的容器,例如vector,并让它自我增长。

C++ 抛弃的 malloc、realloc、calloc,在 C 语言中,malloc、realloc、calloc一般是重新分配一块空间,然后把旧空间的内容 copy 到新空间去,但在 C++ 中,对象空间怎么 copy 呢? 无论是拷贝字节,还是用拷贝构造函数,都不是很合适。

其次,对于对象内存分配,我们使用使用 new调用构造函数,而用上述三个则不会,所以会可能出现不可预知的问题,尤其是派生类对象,如果有虚函数表,则会出现更严重的问题。