说起指针,初学C语言的时候,总是让人感觉似懂非懂。
后来,学习到了很多操作系统、计算机组成原理、体系结构的知识,逐渐深切地认识到:指针其实就是一块一个字长大小的内存单元,它上面保存着 被其指向的 另一块内存的地址。
理解了指针的实现,由于指针所引发的诸多疑难杂症也很容易就一一治愈。于是在某简历中自诩“精通C语言”……
直到有一天,在论坛到看到一些关于C标准的讨论,才发觉C语言比我所理解的要宽泛得多……
有这么个例子:
int a[10], *p;
for (p=a+9; p>=a; p--) {
printf("%d\n", *p);
}
这段代码有问题吗?
看到这个问题的时候,我的第一感觉是:这帮人太无聊了,这段代码怎么会有问题?
但是正确答案却是:有问题!
为什么呢?假设a==0会怎样?p--会溢出,于是p永远比a大,造成死循环……(或者在循环过程中程序就崩溃了。)
那么,改成这样写呢?
int a[10], *p;
for (p=a; p<=a+9; p++) {
printf("%d\n", *p);
}
这样就没问题。
为什么呢?如果p++溢出,p永远都会小于a+9,同样是死循环。那么,既然p--会溢出,p++为什么就不会?
这就要看C标准了:
c99, 6.5.6 Additive operators
When an expression that has integer type is added to or subtracted from a pointer, the
result has the type of the pointer operand. If the pointer operand points to an element of
an array object, and the array is large enough, the result points to an element offset from
the original element such that the difference of the subscripts of the resulting and original
array elements equals the integer expression. In other words, if the expression P points to
the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and
(P)-N (where N has the value n) point to, respectively, the i+n-th and i?n-th elements of
the array object, provided they exist. Moreover, if the expression P points to the last
element of an array object, the expression (P)+1 points one past the last element of the
array object, and if the expression Q points one past the last element of an array object,
the expression (Q)-1 points to the last element of the array object. If both the pointer
operand and the result point to elements of the same array object, or one past the last
element of the array object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined. If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is evaluated.
这条标准提到,参加加减运算的指针及其结果,只有都指向同一数组内(包括数组元素和数组之后的第一个“元素”)时,才是合法的,否则行为是未定义。
也就是说,标准规则了上面的p++是不溢出的,而p--则未定义。
尽管我到现在还觉得这条标准有些蛮横(凭什么p++不溢出,p--就是未定义呢?要么都不溢出、要么都未定义,对称一点多好!),但是我还是很认同标准中诸如此类的许多概念与表述。
在此之前,我总是对一些网友“啃标准”的行为不以为然,何苦让那些概念与规则把自己绕晕,看看实现,一切都一目了解。但是现在我觉得研究标准也还是很有意义的。
C标准描述的是一个抽象的语言逻辑,而现在我们使用的C语言则是在特定计算机条件下的C语言实现。
如果将来计算机环境发生重大变革,C语言还是C语言,但是C语言的实现则可能大相径庭了。比如(假想一下)内存可能不再是一维的了,于是指向内存的指针也需要保存x、y、z这样的多维信息。等等。
理论上说,不管计算机怎么变革,标准的C语言程序都能无缝的移植。当然,现在不利用到操作系统特性(比如多进程、进程间通信、等等等等)并且又很有价值的程序实在太少了,无缝移植我觉得只是个传说。
但是研究研究标准,对深刻理解语言本身还是很有益处的。