也就是数组第一个元素的地址。如果要修改这个指针常量,唯一可行的操作就是把整个数组移动到内存的其他位置。但是,当程序链接后,内存中数组的位置便固定了,当运行时就不能移动数组了。
1)sizeof(数组名)——返回整个数组的长度。而不是指向数组的指针的长度。
2)&数组名——代表一个指向整个数组的指针,而非指向一个数组头元素的指针
举个栗子:
int a[10];
int b[10];
int* c;
c = &a[0];
&a[0]是指向数组第一个元素的指针,同时也是数组名本身的值,所以下面的这条赋值语句也是一样的
c=a;
这里可以正确认识到数组名,是首元素的指针,c所指向的元素是a数组的第一个元素。但是下面的赋值语句是非法的:
b=a;
不能用赋值符把整个数组复制到另一个数组中去,必须使用循环。
考虑下面语句
a=c;
看上去像是指针赋值,把c赋值给a,但是是非法的,因为a的值是常量不能被修改。
a[x]=*(a+x)
举个栗子
int a[10];
int *ap=a+2;
以下ap的表达式能否改写为关于a的表达式
ap——a+2 ,&a[2]
*ap—— *(a+2),a[2]
ap[0]——*(ap+0)= *(a+2)=a[2] 。C语言的下标引用和间接访问表达式是一样的,此处并不在意ap是否为数组。
ap+6——(a+8),&a[8]
*ap+6—— *(a+2)+6,a[2]+6
*(ap+6)—— *(a+8),a[8]
ap[6]——*(ap+6)=a[8]
2[a];
等于 *(2+a),但是不要这么写,影响可读性。
int a[5];
int *b;
a和b都是指针,都可以进行间接访问和下标引用操作,但仍存在区别。
声明一个数组时,编译器会根据数组元素数量和元素类型为其分配内存空间,然后将数组名指向这段空间的起始位置。声明指针变量时,编译器只为指针本身保留内存空间,并不为任何整型值分配内存空间,且b不会初始化。
所以表达式*a是合法的,但 *b是非法的,因为 *b将访问内存中一个不确定的位置。
另一方面,b++可以通过编译,但是a++不行,因为a是常量。
将数组名作为一个参数传递给函数时,函数如果执行了下标引用,实际上是对这个指针进行间接访问操作,通过这种间接访问函数可以访问和修改调用程序的数组元素,就是传址调用。
那么数组的传值调用体现在什么地方呢?那就是数组名指针本身,函数可以自由操作指针形参,因为形参作为一份实参拷贝不会影响到实参的指针!
函数的所有参数本质上是通过传值传递的,当然如果传递的是指针,且函数又对其执行了间接访问,那么函数就可以修改指针指向的那个变量了。数组名作为参数时,函数得到的是该指针的一份拷贝,它可以被修改,但实参不受影响。
函数的形参实际上是个指针,编译器也接受数组形式的函数形参:
下面的函数原型是相等的:
int func(int* a);
int func(int a[]);
但是要注意,两者的函数声明虽然相等,但数组名与指针还是存在区别(sizeof,&),如果在函数中出现表达式 sizeof (a),此时的长度则为指针的长度而非整个数组的长度了,所以在数组传入函数时还是以指针形式更为准确!
这样你就清楚为什么一维数组形参无需写明元素数目了,因为函数不为数组参数分配内存空间。形参只是个指针,它指向的是在其他地方分配好的内存空间。另一方面,函数无法知道数组的长度,如果需要则必须作为一个显式的参数传递给函数。
int a[]={1,2,3,4,5};
如果声明中没有给出数组的长度,编译器就把数组的长度设置为刚好容纳所有初始值的长度。
int main()
{
int a[5];
static int b[5];
int c[3]={1,2};
printf("a[1]=%d\n", a[1]);
printf("b[1]=%d\n", b[1]);
printf("c[2]=%d\n", b[1]);
return 0;
}
static变量未初始化时,数组元素的初始值将设为0,而局部数组变量,存放在栈中,每次运行时给其分配的内存空间随机,编译器没有办法对这些位置进行初始化。这些缺省static的变量如果未赋值初始化,则其中存放为随机值。
如果局部数组初始化一部分,未初始化的值默认为0。
int a[]={0,1,2,3,4};
如果声明中没有给出数组长度,编译器就把数组长度设置为刚好能够容纳所有初始值的长度。
观察下列三行代码:
char message[]={'H','e','l','l','o',0};
char message1[]="Hello";
char* message2="Hello";
第一行和第二行完全一致,第二行尽管看上去像字符常量,但它并不是。它只是第一行初始化的另一种快速写法。但第三行则是不同意义,他是一个真正的字符串常量,这个指针变量被初始化为指向这个字符串常量的地址。
int a;
int b[10];
int c[6][10];
a是一个整型,b是一维数组其中包含10个整型元素,c是一个包含6个元素的数组,而每个元素又是包含10个整型的数组。
int a[3];
包含3个元素,
如果每个元素又是包含六个元素的数组,则有如下声明
int a[3][6];
它在内存中的存储形式:
实线分割的是第一维的三个元素,虚线又将一维的三个元素各划分为6个元素。从左到右的元素标为
a[0] [0],a[0] [1],a[0] [2],a[0] [3],a[0] [4],a[0] [5],a[1] [0],a[1] [1],a[1] [2],a[1] [3],a[1] [4],a[1] [5],
a[2] [0],a[2] [1],a[2] [2],a[2] [3],a[2] [4],a[2] [5]
可见多维数组的存储顺序按照右边下标率先变化的原则。
一维数组名的值是指针常量,类型是”指向元素类型的指针“,它指向数组的第一个元素。那二维数组自然也是如此,但是二维数组的第一个元素是个一维数组。例如以下声明:
int a[3][10];
a是该二维数组的数组名,a是一个指向第一个元素指针,第一个元素是个包含10个整数的数组,所以a是一个指向10个元素的数组指针。它的值如下
对于表达式a+1,也是一个”指向包含10个元素的数组的指针“,它指向a之后的一行
为什么?因为1这个值根据包含10个int元素的数组长度进行调整。
如果对其进行间接访问
*(a+1)
他是一个包含10个int元素的一维数组的数组名,类型是“指向整型的指针:
*(a+1)虽然指向第二行的数组的第一个元素,但是在sizeof和&场合下,其表示整个第二行的数组,
int main()
{
int a[3][6];
printf("%d\n", sizeof(*(a + 1)));
return 0;
}
再看一行表达式:
*(a+1)+5
根据以上我们知道这是一个指针(指向的是一个整型),所以5这个值应该根据整型的长度进行调整,他所指向的位置如图所示:
对其进行间接访问操作:
*(*(a+1)+5)
访问的就是图中所指的那个元素,如果换成下标访问则为
*(a[1]+5)
a[1]选定了一个子数组,所以它的类型是指向整型的指针。再次下标访问
a[1][5]
先看两行声明
int vector[10],*vp=vector;
int matrix[3][10],*mp=matrix;
第一行是合法的,vector和vp都是指向整型的指针,vp初始化为指向vector数组第一个元素的指针。
第二行的声明则是非法的,mp是指向整型的指针,但是mp的初始化不正确,因为matrix不是一个指向整型的指针,而是一个指向拥有10个整型元素的数组的指针。
那么如何声明一个指向数组的指针呢?
int (*p)[10];
p为指向整型数组的指针,下标访问优先级比间接访问高需加上圆括号,在声明中加上初始化:
int (*p)[10]=matrix;
它使p指向matrix的第一行。
p是一个指向拥有10个整型元素的数组的指针,当把p与一个正数相加时,该整数值首先根据10个整数长度进行调整,然后执行加法
。所以我们可以使用这个指针一行一行的在matrix中移动。
如果你需要一个指针逐个访问整型元素而不是逐行在数组中移动,该怎么声明这个指针呢
?
下面的三个声明都创建了简单的整型指针,并以不同方式进行初始化,指向了a这个二维数组的第一个整型元素
int *pi=a[0];
int *pi=*a;
int *pi=&a[0][0];
增加这个指针将使它指向下一个整形元素
。
将二维数组名作为函数参数的传递方式和一维数组名相同——实际上传递的是指向数组的第一个元素的指针。
但是
,两者的区别在于二维数组的首元素是另一个数组,编译器需要他的维数,举个例子:
首先来看下一维数组的函数传参
:
int vector[10];
...
func1(vector);
参数vector为数组名,类型是指向整形的指针,所以func1的原型可以声明为下面两种的任何一种:
void func1(int vec[]);
void func1(int* vec);
作用于vec上面的指针运算把整型的长度作为调整因子
。
现在观察二维数组的函数传参
:
int matrix[3][10];
...
func2(matrix);
参数matrix的类型是指向包含10个整型元素的数组的指针,func2的声明为下面两种的任何一种:
void func2(int mat[][10]);
void func(int (*mat)[10]);
在这个函数中,mat的第1个下标根据包含10个元素的整型数组的长度进行调整,接着第二个下标根据整型的长度进行调整
,
记住func2这样的声明是错误的
:
void func2(int **mat);
这个声明把mat声明为一个指向整型指针的指针,他和指向整型数组的指针并不是一回事。
int (* parr[10])[5];
parr是一个数组,拥有10个元素
每个元素的类型是 int (*)[5],
即parr的每个元素是一个指针,指向拥有5个整型的数组。
数组序列 | 类型 |
---|---|
parr[i] (0<=i<10) | int (*)[5] |