我之前在Stack Overflow上提出了一个使用malloc定义结构体的问题。
大多数人给我的答案是:
struct retValue* st = malloc(sizeof(*st));
我正在向朋友展示我的代码,但我们遇到了一个难题。请问为什么这段代码能够运行?从我的角度来看,在你分配内存时,*st并没有被定义,所以里面可能会有任何一种垃圾数据。正确的应该是
malloc(sizeof(struct retValue))
感谢任何帮助。我之前在Stack Overflow上提出了一个使用malloc定义结构体的问题。
大多数人给我的答案是:
struct retValue* st = malloc(sizeof(*st));
malloc(sizeof(struct retValue))
感谢任何帮助。sizeof操作符只关注所给表达式的类型,不会对表达式进行求值。因此,您只需要确保在表达式中使用的变量已经声明,这样编译器就能推断出它们的类型。
在您的示例中,st已经声明为指向结构体retValue的指针。因此,编译器可以推断出表达式“*st”的类型。
尽管在您的代码中看起来似乎还没有声明,但编译器已经替您处理了这个问题。您的代码块中的所有声明都将被编译器移到其所在块的开头。假设您写了如下代码
展示编译器可用的知识的一种方法是查看它生成的中间输出。考虑以下示例代码...
struct retValue {long int a, long int b};
...
printf("Hello World!\n");
struct retValue* st = malloc(sizeof(*st));
以gcc为例,使用在test.c文件的main()函数中的上述代码,让我们来查看通过运行...得到的中间输出。
gcc -fdump-tree-cfg test.c
编译器会生成文件test.c.022t.cfg - 看一下,你就会看到。[ ... removed internal stuff ...]
;; Function main (main)
Merging blocks 2 and 3
main (argc, argv)
{
struct retValue * st;
int D.3097;
void * D.3096;
# BLOCK 2
# PRED: ENTRY (fallthru)
__builtin_puts (&"Hello World!"[0]);
D.3096 = malloc (16);
st = (struct retValue *) D.3096;
D.3097 = 0;
return D.3097;
# SUCC: EXIT
}
注意声明已经移动到块的开头,并且malloc的参数已经被替换为表示表达式求值类型大小的实际值。正如评论中指出的那样,声明被移动到块的顶部是编译器的实现细节。然而,编译器能够做到这一点,并且还能将正确的大小插入到malloc中,这表明编译器能够从输入中推断出必要的信息。*st
做了“坏事”。如果 st
没有指向有效数据,那么 *st
是无效的。但由于 sizeof
不评估其参数,因此即使 st
为 NULL
,将 *st
用作 sizeof
的操作数也是可以的。 - Alok Singhalsizeof(*st); char *st = 0;
。 - Steve Jessopsizeof
是否有效的不是代码重排,而是在范围内的声明/定义。你的答案错误地将标准行为归因于实现细节。 - dirkgentlysizeof
运算符不会实际评估其操作数 - 它只是查看其类型。这是在编译时而不是运行时完成的。因此,它可以在变量被赋值之前安全地执行。
sizeof
看着 *st 说:“哦,那是一个指针!”然后为指针分配足够的内存。它不在乎 *st 实际上持有什么。对吗? - Blackbinary*st
的意思是“st指向的那个东西”,所以编译器返回的是结构体的大小,而不是指针的大小。 - Graeme Perrowst
是一个指针,但 *st
是一个结构体。所以它查看 *st
并说“哦,那是一个 struct retValue
!”然后分配足够的内存来容纳一个 retValue 结构体。*st
的实际内容并不重要。 - interjay重要的是结构类型的声明/定义,而不是这个类的对象的定义。当你到达malloc
时,编译器已经遇到了一个声明/定义,否则你会遇到编译器错误。
sizeof
不评估其操作数是一个次要问题。
一个小细节:记住当我们向sizeof
提供类型名称时需要使用括号,例如:
sizeof(struct retValue);
而对于对象,我们只需执行:
sizeof *st;
请参阅标准:
6.5.3 一元运算符语法
unary-expression:
[...]
sizeof unary-expression
sizeof ( type-name )
sizeof
是一个运算符,并且不会评估其参数。这可能会导致一些“有趣”的影响,对于新手来说可能无法预料。我在我的回答中详细提到了这一点,回答了“最奇怪的语言特性”问题。
st
(而不是结构)。该指针已经定义并可在初始化表达式中使用(在等号右侧),只是它没有值,因此大多数用途都无效。这个是可以的,因为sizeof不使用该值。 - Steve Jessop