我一直在阅读K&R的C语言书籍,发现C语言中的指针算术运算可以访问数组末尾的下一个元素。我知道C语言几乎可以对内存做任何事情,但我不明白这种特性的目的是什么?
我一直在阅读K&R的C语言书籍,发现C语言中的指针算术运算可以访问数组末尾的下一个元素。我知道C语言几乎可以对内存做任何事情,但我不明白这种特性的目的是什么?
C语言不允许访问数组末尾之外的内存,但是它允许指针指向数组末尾之外的一个元素。这种区别非常重要。
因此,下面的代码是可以的:
char array[N];
char *p;
char *end;
for (p = array, end = array + N; p < end; ++p)
do_something(p);
(执行*end
会导致错误。)
这正显示了为什么这个特性是有用的:指向数组结束后(不存在的)元素的指针可用于比较,例如在循环中。
从技术上讲,这是C标准允许的全部内容。然而,在实践中,C实现(编译器和运行时)并不检查您是否访问了超出数组末尾的内存,无论它是一个元素还是多个元素。必须进行边界检查,这会减慢程序的执行速度。C最适合的程序类型(系统编程,通用库)往往更受益于速度,而不是安全边界检查所能提供的安全性。
这意味着C也许不是通用应用程序编程的好工具。
通常,标记“结束”位置很有用,它比实际分配多一个位置,这样您可以编写以下代码:
char * end = begin + size;
for (char * curr = begin; curr < /* or != */ end ; ++curr) {
/* do something in the loop */
}
C标准明确表示该元素是一个有效的内存地址,但解引用它仍不是一个好主意。
为什么它有这个保证?假设你有一台拥有2^16个字节(0000-FFFF,16位指针)的内存的机器。假设你创建了一个16字节的数组。那么内存可以在FFF0处分配吗?
有16个连续的空闲字节,但问题在于:
begin + size == FFF0 + 10 (16 in hex) == 10000
由于指针大小的限制,在循环中被包装为0000。现在考虑循环条件:
curr < end == FFF0 < 0000 == false
循环不执行任何操作而是遍历数组,这将破坏大量代码,因此C标准规定分配不允许。
你可以超出数组的范围,例如:
int main()
{
char *string = "string";
int i = 0;
for(i=0; i< 10;i++)
{
printf("%c\n", string[i]);
}
return 0;
}
在单词字符串结束后,将打印垃圾内容,无论之前存储在内存中的是什么。