一维数组
数组名
// 如下声明
int a;
int b[10];
a称之为标量,即单一的一个值
b称之为数组,因为他是一些值的集合
数组名和下标一起使用,用于标识集合中某个特定的值,例如b[0]就是b集合中的第一个值,b[4]就是第五个值,每个特定值都是一个标量
b[4]的类型是整型,在c语言中,数组名的值是一个指针常量,也就是数组第一个元素的内存地址.它的类型取决于数组元素的类型.
-
数组名具有一些和指针完全不同的特征
数组具有确定数量的元素,而指针只是一个标量值.编译器用数组名来记住这些元素,只有当数组名在表达式中使用时,编译器才会为它产生一个指针常亮
注意数组的名称是一个指针 常量,而不是指针 变量,意味着不能修改常量的值
int a[10];
int b[10];
int *c;
c = &a[0];
表达式&a[0]是指向数组的第一个元素的指针,但那正是数组名的本身,所以
c=&a[0] 与 c=a是一致的
a = c ,c被声明为一个指针常量,这条语句看上去是把c值赋值给a,但是这个赋值是非法的,a的值是个常量,不能被修改
下标引用
*(b+3)
b的值是一个指向整型类型的指针,所以3这个值根据整型值长度进行调整.加法运算的结果是另一个指向整型的指针(数组第一个元素向后移3个整数长度的位置),*间接访问操作符访问这个新位置,或者取得那里的值(右值),或者把一个新值存储于该处(左值)
除了优先级不同,它和使用下标访问完全相同,
int array[10];
int *ap = array + 2;
// 指针加法运算时会对2进行调整,运算结果所产生的指针ap指向array[2]
ap array+2 与&a[2]对等
*ap array[2] 与 *(array+2) 对等,对数组第三个元素的内存地址执行间接访问
ap[0] c语言下标引用与间接访问表达式是一样的,这样,对等的表达式是*(ap+(0)) 除去0和括号,其结果与前一个表达式相等.因此,和array[2]相同
ap+6 ap指向array[2],加法运算产生的指针所指向的元素是array[2]向后移动6个整数位置的元素,与之相同的为array[2]+6
*(ap+6) 括号迫使加法运算先执行,所以得到的值是array[8]
ap[6] 相当于*(ap+(6)) 去除括号为array[8]
&ap ap的指针,是合法的
ap[-1] 使用偏移量-1可以得到前一个元素,相当于(ap+(-1)),也就是array[1]
ap[9] 看上去是正常的,但是array的长度为10,这个表达式是越界访问的,是非法的
指针与下标
对于指针和下标,两种形式都可以使用,小标容易理解,可读性强,尤其在多维数组中,但指针的效率更高
// 使用下标循环
int array[10],a;
for (a=0;a<10;a++)
array[a] = 0;
// 使用指针循环
int array[10], *ap;
for (ap=array;ap<array+10;ap++)
*ap = 0;
数组和指针的关系
int a[5];
int *b;
在声明一个数组时,编译器将根据所指定的元素数量分配内存空间,然后在创建数组名,它的值是一个常量,指向这段空间的起始位置。
在声明一个指针时,编译器只为指针本身保留内存空间,不为其他值分配空间,而且指针变量并不指向任何现有的内存空间。如果指针时是一个自动变量,它甚至根本不会被初始化。
作为函数参数的数组
当一个数组传递给函数时,数组名是指向该数组的常量,因此传递给数组的是一份指针的拷贝。函数如果执行了下标引用,实际上就是对这个指针执行间接访问操作,并且通过间接访问,函数可以修改和调用数组元素
由于传递给函数的是一份指针的拷贝,因此函数可以自由操作它的指针形参,而不必担心会修改对应实参的指针
当然,如果传递了一个指向数组的指针,则会修改对应的实参
声明数组参数
int strlen(char *string);
int strlen(char string[]);
首先,这两个声明是相等的,调用函数时实际传递的是一个指针.
数组初始化
int vector[5] = {1,2,3,4}
如果初始化值的数目与元素数目不匹配,编译器会自动补全
自动计算长度
int vector[] = {1,2,3,4,5,6}
可以不必指定元素数组,数组会自动计算长度
计算元素数目
int vector[] = {1,2,3,4,5,6};
void sum(int vector[]){
int len = sizeof(vector) / sizeof(vector[0]);
//....
}
多维数组
数组名
一维数组的数组名是指向这个数组首元素的指针常量,二维数组也差不多,唯一区别是多维数组的第一维的元素又是一个数组
int matrix[3][10]
matrix这个名字实际上指向的是二维数组的第一个元素,也就是指向一个包含10个整型元素的指针
matrix + 1
也是指向包含10个整型元素的指针
*(*(matrix + 1) + 5)
*(matrix + 1) 实际上等价于 matrix[1]的,
而后这个包含10整型的数组又移动了5个元素 也就是, *(matrix+1)+5
最后对这个表达式求值 *(*(matrix+1)+5)
等价于 martix[1][5]
指向数组的指针
int vector[10], *vp = vector;
int matrix[3][10],*mp = matrix;
第一个声明是合法的,把vp声明为一个指向整型的指针,vector和vp是相同的类型:指向整形的指针,*vp是访问这个指针保存的值,和vector[0]一个意思
第二个声明是非法的,matrix是一个多维数组,matrix并不是一个指向整型的指针,而是指向整型数组的指针,所以应该这样声明:
int (*mp)[10]
下标引用的优先级高于间接访问,由于括号的存在,首先执行的还是间接访问,所以mp是个指针,接下来执行下标引用,所以mp是指向某种类型的数组的指针。
因此 int (*mp)[10]
mp指向matrix的第一行,也就是matrix[0]
如果访问数组元素而不是在数组中移动,则需要
int *p = &matrix[0][0];
或者 int *p = matrix[0];
作为函数参数传递
int martix[3][10]
void func(int (*mp)[10])
void func(int mp[][10])
指针数组
#include <stdio.h>
void traversal(char **,int);
int main(void) {
char *keyword[] = {
"do","while","for","return","const","register"
};
int len = sizeof(keyword) / sizeof(keyword[0]);
traversal(keyword,len);
return 0;
}
void traversal(char **keyword,int size) {
char **i;
char *j;
for (i = keyword; i<keyword+size;i++){
printf("[");
for (j=*i;*j!='\0';j++){
printf ("%c,",*j);
}
printf("]->%s\n",*i);
}
}
上面的代码中,keyword是一个字符串指针数组,通过长度来进行遍历 keyword是指向保存字符数组的字符型指针,keyword是访问字符数组,(*keyword) 是访问字符数组的第一个元素