使用未定义指针分配内存

6

当我使用gcc -Wall编译时,没有任何警告,这让我感到惊讶。这是真正合法的C代码吗?编写这种代码的风险是什么?

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  int a;
  int b;
} MyStruct;

int main(void) {
  MyStruct *s = malloc(sizeof(*s)); // as opposed to malloc(sizeof(MyStruct))
  s->a = 5;
  printf("%d\n", s->a);
}

建议显式转换以避免错误。https://dev59.com/dHRB5IYBdhLWcg3wgHWr - AntonH
是的,它是有效的并且值得推荐。 - Gábor Buella
@AntonH 这不是关于转换结果的问题。 - SomeWittyUsername
4
不,这不是。 - SomeWittyUsername
@icepack:好的,它并没有解释为什么这是更可取的,但它解释了其余部分。 - Deduplicator
显示剩余2条评论
3个回答

5

这不仅是合法的,甚至比另一种方式更可取。这样做可以让编译器推断实际类型,而无需手动指定。


2

sizeof 在编译时计算。在这个上下文中,*s 解析为 *s 的类型,它不会解引用指针。

这是使用 sizeof 的规范方式。如果您使用 sizeof(int),则可能会留下错误的机会,如果类型更改(在这种情况下,可能不太可能,但仍然存在)。


请注意,给定以下代码:void function(const char *n) { short a[atoi(n)]; size_t s = sizeof(a)/sizeof(a[0]); …code using a and s… },因为a是可变长度数组(VLA),所以sizeof(a)的值是运行时计算的。 (尽管sizeof(a[0])仍然是编译时计算的。)除此之外,我对代码的质量不做任何声明,只是为了狭义的教学目的而使用它(它很糟糕)。 - Jonathan Leffler
@JonathanLeffler:没错,VLA是个例外。 - Ed S.

1
写作
MyStruct *s = malloc(sizeof(*s));

具有完全相同的效果,如下所示:

MyStruct *s = malloc(sizeof(MyStruct));

除了现在你只需要写一次MyStruct之外,其他都没变。也就是说,你正在分配的对象其源类型会自动确定,这样可以减少错误的发生机率。
例如 - 我曾经遇到过这种情况 - 你开始使用一个MyStruct。然后你决定需要另一个不同的MyStruct来完成不同的任务。因此,你最终得到了两个不同的结构体,MyStruct和AnotherStruct。
然后你重构你的代码,并将一些变量从MyStruct更改为AnotherStruct,最终得到
AnotherStruct *s = malloc(sizeof(MyStruct));

在某些情况下,这可能实际上会起作用很长一段时间,直到您对结构进行另一个小更改,而此时该更改与之前完全无关。在那个时候,您的代码就会崩溃。

例如:

typedef struct {
        int a;
        int b;
} StructA;

typedef struct {
        int a;
        int b;
        int c;
} StructB;

int main() {
        // No problem here except wasted space
        StructA *s = malloc(sizeof(StructB));

        // t->c dwells in undefined country
        StructB *t = malloc(sizeof(StructA));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接