C语言总结10-数组和指针
参考书《C Primer Plus》第六版
10.数组和指针
10.1.1 数组的初始化
数组完全不初始化,其元素的值是随机数。
数组只初始化部分元素,未初始化的元素会被初始化为0。
数组初始化列表元素个数超出数组类型元素个数,有的编译会报错,有的编译器只会警告,忽略初始化列表中多出的元素。
int test1[3];
printf(“test1[0] = %d, test1[1] = %d, test1[2] = %d\n”, test1[0], test1[1], test1[2]); //未初始化就使用,编译器会警告
int a[3] = {0}; //这行代码的意思是把数组的第一个元素a[0]初始化为0,其它未初始化的元素会被编译器自动初始化为0。
int test2[3][2] = {(1,2), (3,4), (5,6)}; //test2[0] = {2, 4}, test2[1] = {6, 0}, test2[2] = {0, 0},编译器会警告
int *pData = test2[0];
printf(“test2[0][0] = %d, test2[0][1] = %d\n”, pData[0], pData[1]); //输出为test2[0][0] = 2, test2[0][1] = 4
pData = test2[1];
printf(“test2[1][0] = %d, test2[1][1] = %d\n”, pData[0], pData[1]); //输出为test2[1][0] = 6, test2[1][1] = 0
pData = test2[2];
printf(“test2[2][0] = %d, test2[2][1] = %d\n”, pData[0], pData[1]); //输出为test2[2][0] = 0, test2[2][1] = 0
int test3[3] = {1, 2, 3, 4, 5};
printf(“test3[0] = %d, test3[1] = %d, test3[2] = %d\n”, test3[0], test3[1], test3[2]);
10.1.2 数组索引
数组索引必须是整数,可以为负数。数组索引为正数表示从前往后数,索引为负数表示从后往前数。
array[-1] = (char *)array + sizeof(array) - 1;
char name[4] = {‘K’, ‘A’, ‘N’, ‘G’};
char *pByte = name + 1;
printf(“BYTE = %c\n”, pByte[-1]); //输出为BYTE = K
pByte = (char *)&name + 1;
printf(“BYTE = %c\n”, pByte[-1]); //输出为BYTE = K
pByte = &name + 1; //编译可通过,但有警告,指针类型不匹配,&name的类型为char (*)[4]
printf(“BYTE = %c\n”, pByte[-1]); //输出为BYTE = G
pByte = (char *)(&name + 1);
printf(“BYTE = %c\n”, pByte[-1]); //输出为BYTE = G
10.6 数组和指针的异同
数组和指针只是访问数据的方式相同,本质上是不同的东西,不能当做同一个东西。
*是数组和指针的解引用操作,[]是数组和指针的索引操作。
(1)数组和指针的共同点:访问数据的方式相同
数组和指针都可以通过解引用形式*(x+i)和索引形式x[i]访问数据。
数组可以通过类似指针的解引用形式*(a+i)访问。
指针可以通过类似数组的索引形式p[i]访问。
(2)数组和指针的差异:本质上是不同的东西
数组首地址所在的存储单元中存储的是数据内容。
指针所在的存储单元中存储的是数据的地址。
数组名不能出现在=左边,即不能向数组名赋值,因为数组首地址是固定的,不能改变。
10.7 指针和多维数组
10.7.2 指针的兼容性
嵌套指针赋值时,把非const指针赋给const指针是不安全的,会导致const失效
int *p;
const int **pp; //const修饰的是紧随其后的int,即(**pp)
pp = &p; //不会报错,但是不安全,会导致const失效
const int n = 8;
*pp = &n; //*pp的值是p,即p = &n,间接把p指向了n
*p = 6; //通过p改变了只读变量n的值,导致pp的const失效了
10.7.3 函数和多维数组
多维数组形参的函数,多维数组形参必须指定最高阶之外的尾数。
当一维数组作为函数参数时,编译器会把数组解析为指向其首元素地址的指针,维数信息会被忽略,但是这条规则并不是递归的。
当多维数组作为函数参数时,编译器只会把数组的最高维解析为指向其首元素地址的指针,低维保持不变。
只有最高维的维数信息会被忽略,低维的维数信息不会被忽略。
只忽略最高维的维数,不影响计算指针的步长,若低维的维数也忽略,指针的步长就无法计算了。
void Func(int p[5])会被解析为 void Func(int *p);
void Func(int *p[5])会被解析为 void Func(int **p);
void Func(int p[4][5])会被解析为void Func(int p[][5])或void Func(int (*p)[5]);
void Func(int p[4][5][6])会被解析为void Func(int p[][5][6])或void Func(int (*p)[5][6])
10.8 函数指针
10.8.1 函数指针的声明和定义
typedef void (*fn_t)(void); 只定义函数指针的类型fn_t,并不定义函数指针变量 fn_t func; 定义函数指针变量func
void (*func)(void); 声明函数指针类型的同时定义函数指针变量func
10.8.2 函数指针的赋值和调用
赋值:func = &test; 或 func = test; 函数名就是函数地址,所以取地址符&可以不加
调用:func(); 或(*func)();
10.8.3 函数指针数组
fn_t func[3]; 或 void (*func[3])(void);
调用方式func[0]();
func[3]是数组,void (*)(void)是函数指针,[]的优先级高于*,所以func优先跟[]结合构成数组
10.8.4 函数指针数组的指针fn_t (*func)[3];或void (*(*func)[3])(void);
调用方式(*func)[2](); 或func[1][2]();
(*func)[3]是数组,func是指向数组的指针。指针func的步长是3个函数指针,func++的结果是func加12个字节。
10.8.5 函数指针作为函数形参void test(fn_t func);或void test(void (*func)(void));
10.8.6 指针和数组运算符的优先级
[]优先级高于*,char*p[5],[]优先与p结合构成数组,数组的元素是char *
.优先级高于*,*p.data,.优先与data结合构成结构成员取值,结果是*(p.data),
()优先级高于*,char *fn(),fn优先与()构成函数声明,声明的是函数,不是函数指针,所以fn是函数,不是函数指针。
