【问题标题】:Pointer arithmetic in c and array boundsc和数组边界中的指针算术
【发布时间】:2011-03-17 15:57:13
【问题描述】:

我正在浏览一个webpage,其中有一些常见问题解答,我发现了这个声明。

同样,如果 a 有 10 个元素并且 ip 指向 a[3],你无法计算或 访问 ip + 10 或 ip - 5。(有 一种特殊情况:你可以,在这个 案例,计算,但不访问,a 指向不存在元素的指针 刚刚超出数组的末尾, 在这种情况下是 &a[10]。

我被这句话弄糊涂了

你无法计算 ip + 10

我可以理解越界访问元素是未定义的,但是计算!!!。

我写了以下sn-p,其中计算(让我知道这是否是网站计算的意思)指针越界。

#include <stdio.h>                                                                                                                                                                  

int main()                                                                                                                                                                          
{                                                                                                                                                                                   
        int a[10], i;                                                                                                                                                               
        int *p;                                                                                                                                                                     

        for (i = 0; i<10; i++)                                                                                                                                                      
                a[i] = i;                                                                                                                                                           

        p = &a[3];                                                                                                                                                                  

        printf("p = %p and p+10 = %p\n", p, p+10);                                                                                                                                  
        return 0;                                                                                                                                                                   
}                     

$ ./a.out                                                                                                                                     
p = 0xbfa53bbc and p+10 = 0xbfa53be4     

我们可以看到 p + 10 指向 p 之后的 10 个元素(40 字节)。那么网页上的声明究竟是什么意思。我是不是误解了什么。

即使在 K&R (A.7.7) 中也有此声明:

+ 运算符的结果是 操作数之和。一个指针 数组中的对象和任何值 可以添加整型。 ... 这 sum 是与 原始指针,并指向 同一数组中的另一个对象, 与原作适当偏移 目的。因此,如果 P 是指向 数组中的对象,表达式 P+1 是指向下一个对象的指针 大批。 如果总和指针指向 在数组边界之外, 除了在第一个位置以外 高端,结果是 未定义。

“未定义”是什么意思。这是否意味着总和将是未定义的,还是仅意味着当我们取消引用它时,行为是未定义的。即使我们不取消引用它并且只计算指向元素的指针越界,操作是否未定义。

【问题讨论】:

  • 表示此特定位置的内容未定义(随机)。
  • undefined 意味着得到你所期望的就像把你银行账户里的钱转到我的银行账户一样糟糕。继续编写未定义的结构......总有一天会发生意想不到的......我将度过我梦想中的假期:)

标签: c pointers


【解决方案1】:

未定义的行为意味着:任何事情都可能发生。它可能会默默地成功,也可能会默默地失败,它可能会使您的程序崩溃,它可能会蓝屏您的操作系统,或者它可能会擦除您的硬盘驱动器。其中一些不太可能,但所有这些都是允许的行为就 C 语言标准而言

在这种特殊情况下,是的,C 标准说即使 计算有效数组边界之外的指针的地址,而不取消引用它,也是未定义的行为。之所以这么说,是因为在一些神秘的系统中,进行这样的计算可能会导致某种故障。例如,您可能在可寻址内存的最末端有一个数组,并且在此之外构造一个指针会导致特殊地址寄存器溢出,从而产生陷阱或故障。 C 标准希望允许这种行为,以便尽可能具有可移植性。

但实际上,您会发现在不取消引用的情况下构造这样一个无效地址在您经常遇到的绝大多数系统上具有明确定义的行为。除非您尝试取消引用,否则创建无效的内存地址不会产生不良影响。但当然,最好避免创建那些无效地址,这样您的代码即使在那些晦涩难懂的系统上也能完美运行。

【讨论】:

  • 谢谢亚当。所以这意味着在我的系统上这是一个“定义”的行为。但是如果在同一系统上,阵列位于可寻址内存的末尾,这可能会导致麻烦。在其他系统上,可能有某种“指针验证硬件”根本不允许这种操作,即,甚至算术都不允许,更不用说取消引用了。我理解正确吗?再次感谢您。
  • @jailed 它甚至可能不是您系统上定义的行为;您需要检查您正在使用的特定 C 编译器的文档。
  • "创建无效的内存地址不会有任何不良影响",除非您使用无效的段标识符
【解决方案2】:

网页措辞令人困惑,但在技术上是正确的。 C99 language specification (section 6.5.6) 讨论了加法表达式,包括指针算法。 Subitem 8 明确指出,计算一个超过数组末尾的指针不会导致溢出,但超出此行为是未定义的。

从更实际的意义上说,C 编译器通常会让您侥幸逃脱,但如何处理结果值取决于您自己。如果您尝试取消引用指向某个值的结果指针,如 K&R 所述,则行为未定义。

在编程术语中,未定义的意思是“不要那样做”。基本上,这意味着定义语言如何工作的规范没有定义在那种情况下的适当行为。因此,理论上任何事情都可能发生。通常情况下,您的程序中存在静默或嘈杂(段错误)错误,但许多程序员喜欢开玩笑说导致未定义行为的其他可能结果,例如删除所有文件。

【讨论】:

  • 不,您无法计算该值。正如 C 标准所说,即使只是计算越界指针也是未定义的行为。
  • @Adam 我想我将 undefined 与通常实现的混淆了。几乎任何 C 编译器都会为您计算数学并给您一个值,然后将该值的取消引用保留为未定义。
  • @Adam,你是对的。 C99 规范 (open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf) 第 6.5.6 节第 8 项明确指出,使用指针算法计算数组末尾之后的值不会导致溢出,但进一步指出未定义。我会相应地更新我的答案。
【解决方案3】:

在以下情况下行为将是未定义的

int a[3];
(a + 10) ; // this is UB too as you are computing &a[10]
*(a+10) = 10; // Ewwww!!!!

【讨论】:

    猜你喜欢
    • 2023-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-20
    相关资源
    最近更新 更多