Nek*_*eko 0 c arrays null pointers dereference
这种行为是否已定义?
volatile long (*volatile ptr)[1] = (void*)NULL;
volatile long v = (long) *ptr;
printf("%ld\n", v);
Run Code Online (Sandbox Code Playgroud)
它起作用是因为通过取消对数组的指针的引用,我们正在接收一个数组本身,然后该数组衰减为指向它的第一个元素的指针。
更新演示:https : //ideone.com/DqFF6T
此外,GCC 甚至将下一个代码视为常量表达式:
volatile long (*ptr2)[1] = (void*)NULL;
enum { this_is_constant_in_gcc = ((void*)ptr2 == (void*)*ptr2) };
printf("%d\n", this_is_constant_in_gcc);
Run Code Online (Sandbox Code Playgroud)
基本上,在编译时取消引用 ptr2;
这个:
long (*ptr)[1] = NULL;
Run Code Online (Sandbox Code Playgroud)
正在声明一个指向“1 的数组long”(更准确地说,类型为long int (*)[1])的指针,初始值为NULL。一切正常,任何指针都可以NULL。
然后,这个:
long v = (long) *ptr;
Run Code Online (Sandbox Code Playgroud)
正在取消引用NULL指针,这是未定义的行为。所有的赌注都关了,如果你的程序没有崩溃,下面的语句可以打印任何值或真正做任何其他事情。
让我再澄清一次:未定义的行为意味着任何事情都可能发生。没有解释为什么在调用未定义行为后会发生任何奇怪的事情,也不需要解释。编译器可以很好地发出 16 位实模式 x86 程序集,生成一个删除整个主文件夹的二进制文件,发出 Apollo 11 指导计算机程序集代码,或其他任何东西。这不是一个错误。它完全符合标准。
您的代码似乎可以工作的唯一原因是 GCC纯粹出于巧合决定执行以下操作(Godbolt 链接):
long (*ptr)[1] = NULL;
Run Code Online (Sandbox Code Playgroud)
导致NULL-dereference 永远不会真正发生。这很可能是解引用ptr¯\_(?)_/¯ 中未定义行为的结果
有趣的是,我之前在评论中说:
取消引用
NULL是无效的,并且基本上总是会导致分段错误。
但是当然,因为“基本上总是”是错误的未定义行为。我认为这是我第一次看到空指针取消引用不会导致 SIGSEGV。
这种行为是否已定义?
不是。
Run Code Online (Sandbox Code Playgroud)long (*ptr)[1] = NULL; long v = (long) *ptr; printf("%ld\n", v);它起作用是因为通过取消对数组的指针的引用,我们正在接收一个数组本身,然后该数组衰减为指向它的第一个元素的指针。
不,您将类型与价值混淆了。*ptr第二行中的表达式确实具有type long[1],但是无论数据类型如何,并且无论将应用于结果的自动转换如何,计算该表达式都会产生未定义的行为。
规范的相关部分是第 6.5.2.3/4 段:
一元 * 运算符表示间接。如果操作数指向一个函数,则结果是一个函数指示符;如果它指向一个对象,则结果是一个指定该对象的左值。如果操作数的类型为“指向类型的指针”,则结果的类型为“类型”。如果为指针分配了无效值,则一元 * 运算符的行为未定义。
脚注继续澄清
[...] 一元 * 运算符取消引用指针的无效值包括空指针 [...]
从经验意义上讲,它可能对您“有效”,但从语言的角度来看,任何输出或根本没有输出都是一致的结果。
更新:
有趣的是,明确取地址的答案*ptr与假设数组衰减将克服取消引用的不确定性的答案不同。该标准规定,作为一种特殊情况,当一元运算&符的操作数是一元运算符的结果时,这两个运算*符都不求值。如果满足所有相关约束,结果就好像它们都被完全省略了,只是它永远不是左值。
因此,这是可以的:
long (*ptr)[1] = NULL;
long v = (long) &*ptr;
printf("%ld\n", v);
Run Code Online (Sandbox Code Playgroud)
在许多实现中,它会可靠地打印 0,但请注意,C 没有指定它必须为 0。
这里的关键区别在于,在这种情况下,*不评估操作(根据规范)。在*原始代码中的操作被评价,尽管事实上,如果该指针值是有效的,所得到的阵列将被转换右回指针(不同类型的,但相同的位置)。这确实显示了明显的捷径,实现可采取与原代码,他们可能会采取它,如果他们愿意的话,不考虑是否ptr的值是有效的,因为如果是在有效的,那么他们可以为所欲为。
| 归档时间: |
|
| 查看次数: |
418 次 |
| 最近记录: |