*vp = 0;
由标准可以,第二个for语句是有问题的.但实际上,在绝大多数C编译器中,这个循环将顺利完成任务.然而,你还是应该避免使用它,因为标准并不保语它可行.你迟早可能遇到一台这个循环失败的机器.对于负责可移植代码的程序员而言,这类问题简直就是个恶梦.
数组:
int a;
int b[10];
b[4]的类型是整型,但b的类型又是什么呢 它所表示的又是什么呢 一个合乎逻辑的答案是它表示整个数组,但事实并非如此.在C中,在几乎所有使用数组名的表达式中,数组名的值是一个指针常量,也就是数组第1个元素的地址.它的类型取决于数组元素的类型:如果它们是int类型,那么数组名的类型就是”指向int的常量指针”;如果它们是其它类型,那么数组名就是”指向其它类型的常量指针”.
只有在两种场合下,数组名并不用指针常量来表示------就是当数组名作为sizeof操作符或单目操作符的操作数时.sizeof返回整个数组的长度,而不是指向数组的指针的长度,取一个数组名的地址所产生的是一个指向数组的指针!
数组和指针
指针和数组并不是相等的,为了说明这个概念,请考虑下面这两个声明:
int a[5];
int b;
a和b能够互换使用吗 它们都具有指针值,它们都可以进行间接访问和下标引用操作.但是它们还是存在相当大的区别.
声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值是一个常量,指向这段空间的起始位置.声明一个指针变量时,编译器只为指针本身保留内存空间,它并不为任何整型值分配内存空间.而且,指针变量并未被初始化为指向任何现有的内存空间,如果它是一个自动变量,它甚至根本不会被初始化.因此,上述声明之后,表达式*a是完全合法的,但表达式*b却是非法的.*b将访问内存中某个不确定的位置,或者导致程序终止.另一方面,表达式b++可以通过编译,但a++却不行,因为a的值是一个常量.
字符数组的初始化
char message[] = { ‘H’, ‘e’, ‘l’, ‘l’, ‘o’, 0};
这个方法当然可行.但除了非常短的字符串,这种方法确实很笨拙.因此,语言标准提供了一种快递方法用于初始化字符数组:
char message[] = “Hello”;
尽管它看上去像是一个字符串常量,实际上并不是.它只是前例的初始化例表的另一种写法.
如果它们看上去完全相同,你如何分辨字符串常量和这种初始化例快递记法呢 它们是根据它们所处的上下文环境进行区分的.当用于初始化一个字符数组时,它已经一个初始化例表.在其他任何地方,它都表示一个字符串常量.
这里有一个例子:
char message[] = “Hello”;
char *message = “Hello”;
这两个初始化看上去很像,但它们具有不同的含义.前者初始化一个字符数组的元素,而后者则是一个真正的字符串常量.这个指针变量被初始化为指向这个字符串常量的存储位置.
多维数组:
考虑下列这些维数不断增加的声明:
int a;
int b[10];
int c[6][10];
int d[3][6][10];
a是个简单的整数.接下来的那个声明增加了一个维数,所以b就是一个向量,它包含10个整型元素.
c只是在b的基础上再增加了一维,所以我们可以把c看作是一个包含6个元素的向量,只不过它的每个元素本身是一个包含10个整型元素的向量.换句话说,c是一个一维数组的一维数组.d也是一如此:它是包含3个元素的数组.简洁的说,d是一个3排6行10列的整型三维数组.
数组名
一维数组名的值是一个指针常量,它的类型是”指向元素类型的指针”,它指向数组的第1个元素.多维数组也差不多简单.唯一的区别是多维数组第1维元素实际上是另一个数组.例如,下面这个声明:
int matrix[3][10];
matrix这个名字的值是一个指向它第1个元素的指针,所以matrix是一个指向一个包含10个整型元素的数组的指针.
指向数组的指针:
下面这些声明合法吗
int vector[10], *vp = vector;
int matrix[3][10], *mp = matrix;
第一个声明是合法的.它为一个整型数组分配内存,并把vp声明为一个指向整型的指针,并把它初始化为指向vector数组的第1个元素.vector和vp具有相同的类型:指向整型的指针.但是第二个声明是非法的.它正确地创建了matrix数组,并把mp声明为一个指向整型的指针.但是mp的初始化是不正确的,因为matrix并不是一个指向整型的指针,而是一个指向整型数组的指针.我们应该怎样声明一个指向整型数组的指针的呢
int (*p)[10];
这个声明比我们以前见过的所有声明更为复杂,但它事实上并不是很难.你只要假定它是一个表达式并对它进行求值.下标引用的优先级高于间接访问,但由于括号的存在,首先执行的还是间接访问,所以,p是一个指针,但它指向什么呢 接下来扫行的是下标的引用,所以p指向某种类型的数组.这个声明表达式中并没有更多的操作符,所以数组的每个元素都是整数.
声明并没有直接告诉你p是什么,但推断它的类型并不困难-----当我们对它执行间接访问操作时,我们得到的是个数组,对该数组进行下标引用操作得到的是一个整型值.所以p是一个指向整型数组的指针.
在声明中加上初始化后是下面这个样子:
int (*p)[10] = matrix;
它使p指向matrix的和1行.
p是一个指向拥有10个整型元素的数组的指针.当你把p与一个整数相加时,该整数值首先根据10个整型值的长度进行调整,然后再执行加法.所以我们可以使用这个指针一行一行地在matrix中移动.
如果你需要一个指针逐个访问整型元素而不是逐行在数组中移动,你应该怎么办呢 下面两个声明都创建了一个简单的整型指针,并以两种不同的方式进行初始化,指向matrix的第1个整型元素.
int *pi = &matrix[0][0];
int *pi = matrix[0];
增加这个指针的值使它指向下一个整型元素.
指针数组
int *api[10];
为了弄清楚这个复杂的声明,我们假定它是一个表达式,并对它进行求值.
下标引用的优先级高于间接访问,所以在这个表达式中,首先执行下标引用.因此,api是某种类型的数组(噢! 顺便说一下,它包含的元素个数为10).在取得一个数组元素之后,随即执行的是间接访问操作.这个表达式不再有其它操作符,所以它的结果是一个整型值.对数组的某个元素执行间接访问操作后,我们得到一个整型值,所以api肯定是个数组,它的元素类型是指向整型的指针.
高级指针话题
int *f();
要想推断出它的含义,你必须确定表达式*f()是如何进行求值的.首先执行的是函数调用操作符(),因为它的优先级高于间接访问操作符.因此,f是一个函数,它的返