【问题标题】:Dereferencing an uninitialized pointer to pass into sizeof()取消引用未初始化的指针以传递给 sizeof()
【发布时间】:2020-11-10 20:42:34
【问题描述】:

在最近的一篇文章中,我意识到在分配结构变量时,与将结构类型传递给 sizeof() 相比,传递取消引用的指针被认为是一种更好的做法。这基本上是因为前者比后者更能适应代码更改。

这表明,在以下代码中,方法 1 被认为比 方法 2 更好。

typedef struct X_ {
    int x;
    int y;
    int z;
} X;

int main() {
    X* obj1 = malloc(sizeof(*obj1)); // ----> method 1
    X* obj2 = malloc(sizeof(X));     // ----> method 2
    return 0;
}

问题是,在 方法 1 中取消引用 obj1 的有效性如何?在malloc 内部,obj1 仍然是未构造/未初始化的内存,这表明在sizeof() 内部发生的obj1 的取消引用不应该是有效的。

让我猜一下是什么让 方法 1 有效。这是因为 sizeof() 是一个编译时操作,取消引用 obj1 被编译器翻译成方法 2

有人可以通过参考相关的C标准来详细说明它的技术有效性吗?

【问题讨论】:

  • 你没有取消引用任何东西...sizeof 不会评估其操作数(VLA 除外),即sizeof (42/0) 不会尝试除以零 :)
  • @pmg 我对“你没有取消引用任何东西”有点困惑......我知道sizeof 不会评估它的操作数,除非是 VLA......但是,如果sizeof(*obj1) 不是取消引用obj1(这是指向X 的指针)来获取X,那么我们怎么称呼它呢?是不是因为这是一个编译时操作,所以不被视为“解除引用”?
  • @dwillis77: sizeof 只关心类型*obj类型obj 的值相同(即使 obj 具有无效/不确定/陷阱值)。 int/int42/0)的类型是int,即使值没有意义:-)

标签: c pointers malloc language-lawyer sizeof


【解决方案1】:

操作数不是可变长度数组的 sizeof 表达式是非求值表达式。所以这个表达式

sizeof(*obj1)

格式正确。

来自 C 标准(6.5.3.4 sizeof 和 alignof 运算符)

2 sizeof 运算符产生其操作数的大小(以字节为单位),即 可以是表达式或类型的括号名称。尺寸是 由操作数的类型决定。结果是一个整数。 如果操作数的类型是变长数组类型,则对操作数求值;否则,不计算操作数,并且 结果是一个整数常量

至于您关于指定 malloc 参数的最佳方法的问题

X* obj1 = malloc(sizeof(*obj1)); // ----> method 1
X* obj2 = malloc(sizeof(X));     // ----> method 2

那么如果X类型在这种情况下使用malloc时可见

X* obj1 = malloc(sizeof(*obj1)); // ----> method 1

那么这种方法更可取。

但是,如果类型不可见,例如

obj1 = malloc(sizeof(*obj1)); // ----> method 1

那么我更喜欢明确指定类型

obj1 = malloc(sizeof( X ));

否则例如这个代码sn-p

p = malloc( *p );
q = malloc( *q );

没有为代码的读者提供足够的信息。并且读者需要前后滚动源代码才能找到pq的声明来确定它们的类型。

【讨论】:

  • 有争议的是,obj1 = malloc(sizeof(*obj1)) 获得的好处与obj1 = (X*)malloc(sizeof( X )); 相同
  • @chqrlie 只有第一个版本不会改变 obj1 的类型。如果您更改类型,则 (type)malloc(sizeof type) 需要在代码中编辑 2 个额外的位置,而 malloc(sizeof *object) 不需要(并且总是正确的)。通过强制转换 malloc 的结果,您将一无所获。
  • @FelixG:如果您启用了体面的警告 (-Wall -Werror),则会出现类型不匹配。演员表可能来自宏:#define new_object(type) ((type *)malloc(sizeof(type)))。本地编码约定可能包括这样的内容,如果您将对象直接作为函数参数传递,则替代 obj1 = malloc(sizeof(*obj1)) 不可用:append_node(new_object(Node))
【解决方案2】:

问题是,在方法 1 中取消引用 obj1 的有效性如何?

它是 100% 有效的。不过,您可以不带括号使用它,sizeof *obj1

来自N1570 ISO/IEC 9899:201x §6.5.3.4 The sizeof and _Alignof operators

2 - sizeof 运算符产生其操作数的大小(以字节为单位),它可以是表达式或带括号的类型名称。 大小由操作数的类型决定。结果是一个整数。 如果操作数的类型是变长数组类型,则计算操作数; 否则,不计算操作数,结果为整数常量。

实际上可以说这是一种更可取的方法,原因是如果您出于某种原因更改对象的类型很容易忘记更改sizeof 参数,使用取消引用的指针将避免这种情况潜在的静默错误。

【讨论】:

    猜你喜欢
    • 2016-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-28
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 2021-01-13
    相关资源
    最近更新 更多