C - 取消引用术语后的引用 [英] C - Reference after dereference terminology

查看:29
本文介绍了C - 取消引用术语后的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是关于术语的.

This question is about terminology.

int main()
{
    unsigned char array[10] = {0};

    void *ptr = array;

    void *middle = &ptr[5]; // <== dereferencing ‘void *’ pointer
}

Gcc 发出警告取消引用无效指针.

我理解警告是因为编译器需要计算实际偏移量,而它不能,因为 void 没有标准大小.

I understand the warning because the compiler needs to compute the actual offset, and it couldn't because void has no standard size.

但我不同意错误消息.这不是取消引用.我找不到取消引用的解释,它不是获取某物的价值.

But I disagree with the error message. This is not a dereference. I can't find a dereference explanation where it is something else than taking value of something.

offsetof 也一样:

Same thing for offsetof:

#define offsetof(a,b) ((int)(&(((a*)(0))->b)))

由于空指针取消引用,有很多关于这是否是 UB 的线程.但这不是空指针取消引用!是吗?

There are lot of threads about whether this is UB because of a null pointer dereference. But this is not a null pointer dereference! Is it?

汇编代码中没有存储访问

mov rax, QWORD PTR [rbp-48]
add rax, 5
mov QWORD PTR [rbp-40], rax

取消引用和存储访问有什么区别?

What is the difference between dereference and storage access?

推荐答案

但我不同意错误消息.这不是取消引用.我找不到取消引用的解释,它不是获取某物的价值.

But I disagree with the error message. This is not a dereference. I can't find a dereference explanation where it is something else than taking value of something.

该标准没有提供术语取消引用"的正式定义.它唯一使用它的地方是(非规范)footnote 102:

The standard does not provide a formal definition of the term "dereference". The only place it uses it at all is in (non-normative) footnote 102:

[...] 通过一元 * 运算符取消引用指针的无效值包括空指针、与指向的对象类型不正确对齐的地址以及对象生命周期结束后.

[...] Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.

但是请注意,该注释将取消引用描述为一元 * 运算符的行为,而不是对结果执行某些其他操作的影响.您可以将操作视为将指针转换为它指向的对象,如果指针实际上不指向指向类型的对象,或者如果指向的对象 -to type 是一个不完整的类型,例如 void.即使结果对象未被使用,这种问题也正式存在.

Note well, however, that this note characterizes dereferencing as the behavior of the unary * operator, not the effect of performing some other operation on the result. You can think of the operation as converting a pointer into the object to which it points, which you will recognize presents an issue if the pointer does not, in fact, point to an object of the pointed-to type, or if the pointed-to type is an incomplete one such as void. Such an issue exists formally even if the resulting object goes unused.

现在我承认这里存在混淆的空间,因为在不使用结果对象的情况下执行取消引用是没有用的,但这不是重点.考虑以下完整的 C 语句:

Now I acknowledge that there is room for confusion here on account of the fact that it is useless to perform a dereference without using the resulting object, but that's beside the point. Consider the following complete C statement:

1 + 2;

你是否会因为结果未被使用而否认它执行加法?

Would you deny that it performs an addition just because the result is unused?

现在,您的(子)表达式 ptr[5] 被定义为与 (*((ptr)+(5))) 具有相同的含义.指针加法表达式的类型与所涉及的指针的类型相同,因此在应用一元 * 的意义上,这确实涉及取消引用 void *> 运算符到该类型的表达式.

Now, your (sub-)expression ptr[5] is defined to have meaning identical to that of (*((ptr)+(5))). The type of a pointer addition expression is the same as the type of the pointer involved, so the that indeed does involved dereferencing a void *, in the sense of applying the unary * operator to an expression of that type.

尽管如此,虽然我认为错误消息是正确的,我确实同意这是一个糟糕的选择.这里的一个更基本的问题是违反语言约束的,即在指针加法中,指针必须指向 complete 类型,即 void 不是.实际上,很难将发出的消息解释为满足约束违规导致诊断的要求.这似乎是一个不同的问题——一个产生未定义行为但不涉及约束违规的问题.

Nevertheless, although I think the error message is correct, I do agree that it is a poor choice. A more fundamental problem here, and one that is reached first in evaluation order, is a violation of the language constraint that in pointer addition, the pointer must point to a complete type, which void is not. Indeed, it's hard to construe the message that is emitted as satisfying the requirement that constraint violations result in a diagnostic. It seems to be about a different problem -- one that produces undefined behavior, but does not involve a constraint violation.

您还说:

offsetof 也一样:

Same thing for offsetof:

#define offsetof(a,b) ((int)(&(((a*)(0))->b)))

[...] 但这不是空指针解引用!是吗?

[...] But this is not a null pointer dereference! Is it?

小心,那里.C语言没有定义offsetof()宏的替换文本的具体形式;您提供的是实现细节.

Be careful, there. The C language does not define the specific form of the replacement text of the offsetof() macro; what you've presented is an implementation detail.

在这里我们可以很容易地转向语义,因为取消引用"在标准中不是一个定义的术语,所以我将解决一个类似的问题:当宏参数满足 offsetof 的要求时() 宏,所呈现的定义是否扩展为具有明确定义行为的表达式?

We could easily divert into semantics here, since "dereference" is not a defined term in the standard, so I'll address instead a similar question: when the macro arguments meet the requirements of the offsetof() macro, does the definition presented expand to an expression with well-defined behavior?

当间接成员选择运算符 (->) 的左侧操作数具有可接受的类型但不指向任何对象时(例如当它是空值).因此行为是未定义的.或者,如果我们将 a->b 完全等同于 ((*a).b),那么当 a 时行为是明确未定义的code> 不指向任何对象.无论哪种方式,C 语言都没有为表达式定义行为.

The standard does not define behavior for the indirect member selection operator (->) when its left-hand operand has an acceptable type but does not point to any object (such as when it is null). The behavior is therefore undefined. Or if we take a->b to be wholly equivalent to ((*a).b), then the behavior is explicitly undefined when a does not point to any object. Either way, the C language does not define behavior for the expression.

但这就是您的特定宏定义是实现细节变得重要的地方.绘制它的实现可以自由地提供它希望的任何行为,特别是,它可以提供可靠地满足 C 对 offsetof() 宏的规范的行为.您不应该自己依赖此类代码.即使在提供该表单的 offsetof() 定义的实现上,您也不能确定它是否也使用了一些特殊的内部魔法——不能直接用于您自己的代码——来实现它工作.

But this is where it becomes important that your particular macro definition is an implementation detail. The implementation from which it is drawn is free to provide whatever behavior it wishes, and in particular, it can provide behavior that reliably satisfies C's specifications for the offsetof() macro. You should not rely on such code yourself. Even on an implementation that provides an offsetof() definition of that form, you cannot be certain that it does not also employ some special internal magic -- not available directly to your own code -- to make it work.

这篇关于C - 取消引用术语后的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆