首先,让我们澄清一些术语:
所以我们将有一个指针指向另一个指针
这是不正确的。当您将一个指针赋值给另一个指针时,第二个指针并不指向第一个指针,它指向与第一个指针相同的东西。指向指针意味着需要两个级别的解引用 - 在这种情况下 - 才能到达 char
,即 char a = **pr;
因此,让我们看看代码。
pr = (char*)malloc( 50 * sizeof(char) );
malloc的原型是
void *malloc(size_t size);
malloc
函数分配 size
字节的内存,并返回指向该内存块的指针(如果无法分配内存,则返回NULL
)。malloc
函数不知道您想指向什么类型的东西,因此它选择返回一个指向未知数据类型的void *
指针。您的对malloc
的调用前面的(char*)
是一种类型转换操作。它将其右侧表达式的类型更改为括号中所包含的类型,在这种情况下为char *
(可以在char
和*
之间放入或留出空格而不会有任何影响)。
现在,指针的类型变为char *
,随后被赋值给也是char *
类型的pr
。
唯一需要注意的是,只要从void *
到另一个指针类型,C会自动完成强制类型转换。因此,您所写的代码实际上与示例中的代码完全等价。
pr = malloc( 50 * sizeof(char) );
通常认为这种写法比直接进行强制类型转换更好。这样更易于阅读,也更加简洁。
过去还存在这样的问题:如果你忘记了包含#include <stdlib.h>
,编译器会假定malloc
返回一个int
。如果你省略了强制类型转换,编译器会将从int
到char *
的尝试转换标记为错误,但是如果你加入了它,错误会被抑制。由于C11禁止使用未声明的函数,因此这个问题已经不存在了。
还有另一个问题,与上述问题相反。如果你有
char* pr;
// Lots of code
pr = malloc( 50 * sizeof(char) );
pr[49] = 0;
如果你决定让pr
指向int
,你最终可能会得到以下结果:
int* pr;
// Lots of code
pr = malloc( 50 * sizeof(char) );
for (int i = 0 ; i < 50 ; ++i)
{
pr[i] = 0; // buffer overflow!
}
这段代码可以编译,但是由于分配的内存块不足以容纳50个整数,因此是未定义行为。你可以通过回归显式类型转换(int* pr = (char*)malloc(...)
是错误的)来解决这个问题,但更好的方法是对解引用指针进行sizeof操作。
int* pr;
pr = malloc( 50 * sizeof *pr );
for (int i = 0 ; i < 50 ; ++i)
{
pr[i] = 0;
}
calloc
与malloc
类似,不同之处在于你需要分别提供指向的对象数量和指向的对象大小,而不是绝对的字节数。 calloc
还会将分配块中的字节清零。
int *pr = calloc(50, sizeof *pr);
这段代码与前面的代码基本相同,唯一不同的是如果无法分配内存,它不会产生未定义行为。
NB: 很多人说你绝对不应该放置显式转换,但实际上有双方都有 争议。此外还有这个。
sizeof(char)
的大小始终为1。该段落指出:“当sizeof
应用于具有类型char
、unsigned char
或signed char
(或其限定版本)的操作数时,结果为1。” - Andrew Henle