c和数组边界中的指针算法

我正在浏览一个有一些常见问题解答的网页 ,我发现了这个陈述。

类似地,如果a有10个元素且ip指向a [3], 则无法计算或访问ip + 10或ip – 5. (有一种特殊情况:在这种情况下,您可以计算,但不能访问,一个指向不在数组末尾的不存在元素的指针,在本例中是&a [10]。

我对这句话感到困惑

你无法计算ip + 10

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

我写了下面的片段,它计算 (让我知道这是网站对计算的意义 )指针越界。

#include  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是指向数组中下一个对象的指针。 如果sum指针指向数组范围之外,除了高端之外的第一个位置,结果是未定义的。

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

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

在这种特殊情况下,是的,C标准说甚至在有效数组边界之外计算指针的地址而不解除引用它是未定义的行为。 它之所以说这是因为存在一些神秘的系统,进行这样的计算可能会导致某种类型的错误。 例如,您可能在可寻址存储器的最末端有一个数组,并且构造超出该指针的指针会导致特殊地址寄存器溢出,从而产生陷阱或故障。 C标准希望允许这种行为以便尽可能便携。

但实际上,您会发现构建这样一个无效地址而不解除引用它在绝大多数系统中具有明确定义的行为,这些行为在常见用法中会遇到。 除非您尝试取消引用,否则创建无效的内存地址不会产生任何不良影响。 但是,当然,最好避免创建这些无效地址,这样即使在那些神秘的系统上,您的代码也能完美运行。

网页的措辞令人困惑,但在技术上是正确的。 C99语言规范(第6.5.6节)讨论了加法表达式,包括指针算法。 子项目8明确指出,计算一个超过数组末尾的指针不应导致溢出,但除此之外,行为是未定义的。

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

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

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

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