当前位置:  开发笔记 > 人工智能 > 正文

如果没有指针被解除引用,C是否检查指针是否超出范围?

如何解决《如果没有指针被解除引用,C是否检查指针是否超出范围?》经验,为你挑选了4个好方法。

我和一些人说过这个论点,即C出界指针会导致未定义的行为,即使它们没有被解除引用.例:

int a;
int *p = &a;
p = p - 1;

这里的第三行将导致未定义的行为,即使p永远不会被解除引用(*p从未使用过).

在我看来,如果没有指针被使用,C会检查指针是否超出界限听起来有点不合逻辑(就像有人会检查街上的人,看看他们是否携带枪支以防他们进入他的房子.理想的做法是在人们即将进入房屋时对他们进行检查.我认为如果C检查,那么会发生很多运行时开销.

另外,如果C真的检查OOB指针那么为什么这不会导致UB:

int *p; // uninitialized thus pointing to a random adress

在这种情况下,即使p指向OOB地址的机会很高,为什么也不会发生任何事情.

加:

int a;
int *p = &a;
p = p - 1;

&a是1000. p评估第三行后的价值是:

996但仍未定义的行为,因为p可能在其他地方取消引用并导致真正的问题.

未定义的值,这是未定义的行为.

因为我认为"第三行被称为未定义的行为"首先是因为未来可能会使用该OOB指针(解除引用),随着时间的推移,人们将其视为一种未定义的行为.现在,值p是100%996还是未定义的行为或其值将是未定义的?



1> chqrlie..:

C不检查指针是否超出范围.但是当计算出落在对象边界之外的地址时,底层硬件可能会以奇怪的方式运行,指向对象结束后的异常.C标准明确地将此描述为导致未定义的行为.

对于大多数当前环境,上述代码不会造成问题,但类似的情况可能导致x86 16位保护模式中的分段错误,大约25年前.

在标准的语言中,这样的值可以是陷阱值,在不调用未定义的行为的情况下无法操纵.

C11标准的相关部分是:

6.5.6加法运算符

    当一个具有整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型.如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向偏离原始元素的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式.[...]如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义.如果结果指向数组对象的最后一个元素之后,则不应将其用作*已计算的一元运算符的操作数.

类似的未定义行为的例子是:

char *p;
char *q = p;

仅仅加载未初始化指针的值会p调用未定义的行为,即使它永远不会被解除引用.

编辑:试图争论这个问题是一个没有实际意义的问题.标准说计算这样一个地址会调用未定义的行为,所以它会这样做.某些实现可能只是计算某个值并存储它的事实是无关紧要的.不要依赖任何有关未定义行为的假设:编译器可能利用其固有的不可预测性来执行您无法想象的优化.

例如这个循环:

for (int i = 1; i != 0; i++) {
    ...
}

可能会在没有任何测试的情况下编译成无限循环:i++如果iINT_MAX,则调用未定义的行为,因此编译器的分析如下:

初始值i> 0.

对于任何正值i < INT_MAX,i++仍然是> 0

因为i = INT_MAX,i++调用未定义的行为,所以我们可以假设,i > 0因为我们可以假设任何我们喜欢的东西.

因此i总是> 0可以删除测试代码.


不只是"可能以奇怪的方式表现"的"底层硬件".无论硬件如何,编译器都可以自由地生成在调用UB时可能表现不一致的代码.例如,在OP的例子中,`p> =&a`可能在某些地方评估为true而在其他地方为false,或者`p ==&b`(对于某些其他变量`b`)可能在某些地方(编译器)评估为true在其他地方(编译器看到`p`基于'&a`,因此永远不能等于另一个对象`b`的地址),看不到'p`的来源和false.
好吧,因为行为是未定义的,所以允许一致的实现来检查指针是否超出范围并且因运行时错误而崩溃.它也允许在星期一,星期三和星期五这样做; 将代码视为正确,并让它在星期二和星期四运行; 在周末,去'exec``nethack`.一个编译器,只是为了表明它永远不会支持非标准扩展,每当它遇到以##pragma`开头的行时,确实会运行roguelike游戏.它可以做*任何事情*.
@CoolGuy是的,它适用于任何对象.标准中的前一段说明:*"出于这些运算符的目的,指向非阵列对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型."*因此,出于此目的,非数组对象是长度为1的数组对象.
"_...指向对象结束后的异常._" - 对于任何对象或仅对于数组对象,这是真的吗?那么,`int a = 3,*p =&a + 1;`有效吗?

2> Kerrek SB..:

实际上,如果C程序试图通过指针算法计算一个值而不是一个指向一个元素的指针,或一个超过同一个数组元素结尾的指针,那么它的行为就是未定义的.从C11 6.5.6/8:

如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义.

(出于本说明的目的,可以将类型对象T的地址视为数组的第一个元素的地址T[1].)


@DavidBowling:谨慎对待"工作"意味着什么的自由解释!

3> harmic..:

澄清一下,"未定义的行为"意味着有关代码的结果未在管理该语言的标准中定义.实际结果取决于编译器的实现方式,并且可以从完全崩溃到完全崩溃以及介于两者之间的所有内容.

标准没有规定应该对指针进行任何范围检查.但就你的具体例子而言,这就是他们所说的:

当一个具有整数类型的表达式被添加到指针或从指针中减去时......如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不得产生溢出; 否则,行为未定义.如果结果指向数组对象的最后一个元素之后,则不应将其用作已计算的一元*运算符的操作数.

以上引用来自C99§6.5.6第8段(我手头的最新版本).

注意,上面也适用于非数组指针,因为在前面的子句中它说:

出于这些运算符的目的,指向不是数组元素的对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型.

因此,如果您执行指针运算,并且结果在边界内或指向对象末尾之后的结果,那么您将获得有效结果,否则您将获得未定义的行为.这种行为可能是你最终得到一个迷路指针,但它可能是另一回事.



4> Kornel..:

是的,即使指针未被解除引用,它也是未定义的行为.

C只允许指针只指向一个元素超过数组边界.

推荐阅读
Gbom2402851125
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有