下面代码分别代表什么?
int *parr[4]; //1 int (*parr)[4]; //2
结果:1是指针数组,2是数组指针,初学者很容易把他俩搞混,或者不知道怎么用???
读完本篇文章你会有新的理解和认识>_<
🍌指针数组
1.定义
指针数组是存放指针的数组
指针数组是 指针?数组?
很明显是数组。
下面代码解释为
- 创建了一个指针数组类型的变量
- 数组中有三个元素,元素的类型为char*
char* ptr[3] = {"I", "LOVE", "YOU"};
🍌数组指针
1.定义
数组指针是一个指向数组的指针
int arr[5] = {0}; int (*parr)[5] = &arr;
2. &数组名 与 数组名
int arr[3] = {0}; //arr 与 &arr 相同吗???
在VS中输出看一下
打印出来的结果是一样,但是事实不是这样的。
当我们对上面两个地址同时加1时,观察结果他们相差了8
🍋先说结论,指针类型决定指针运算时跳过的字节数,数组名就是地址,地址就是指针嘛,所以遵循这个原则!!!
两个地址相差了8个字节,我们画下内存图,看看为什么是8
=========================================================================
补充:数组名在除以下两种情况外,其他都代表首元素地址
1.sizeof(数组名) -计算的是整个数组的大小
2.&数组名 -拿到的数组的地址
=========================================================================
3.数组指针的使用
那数组指针有什么用呢?
很明显数组指针存放的数组的地址,看代码
void print_arr1(int arr[3][2]) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { printf("%d ", arr[i][j]); } } } void print_arr2(int (*parr)[2]) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { printf("%d ", parr[i][j]); } } } int main() { int arr[3][2] = { {1 ,2}, {3 ,4},{5 ,6} }; print_arr1(arr); printf("\n"); print_arr2(arr); return 0; }
可以作为形参接受二维数组的首元素地址
🐒总结
学完上面指针数组与数组指针,总结和回顾下所学内容,解释代码:
int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];
// 1 数组 -> 声明一个整形数组,大小为5
// 2 数组指针 -> parr1先跟[]结合,说明是数组,去掉parr1[10],剩下的就是数组存放类型为int*
// 3 指针型数组 -> parr2先跟*结合,说明是指针,去掉指针部分*parr2,剩下的是指针的指向int()[10],指向的是一个数组
// 4 指针数组型的数组 -> parr3先与[]结合,是数组,去掉parr3[10],剩下的就是数组存放的类型int(*)[5]。可以对比int arr[5]去掉arr[5]就是数组存放的类型int。
🍋规律:先看是指针还是数组,是指针就看指针指向的是什么,是数组就看数组里面存放的是什么,复杂无非就是这部分复杂。
🍌指针数组与数组指针的传参
💧一位数组的传参:
当我们把一维数组作为参数是,函数的形参可以怎么设计呢?
// 一维数组的传参 void test1(int arr[]) { } void test2(int* arr) { } int main() { int arr[] = { 1,2,3 }; test1(arr); test2(arr); return 0; }
💧二维数组的传参:
二维数组写法就比较多了,不妨自己先试着写写看能写几个?
在这先说明二维数组可以看成n个一维数组,arr数组名代表首元素地址,也就是int [3]地址,总起来arr就是&int [3]。
int arr[2][3];
test(arr);
void test1(int arr[][3]) { } void test2(int arr[2][3]) { } void test3(int (*parr)[3]) { } int main() { int arr[2][3] = { 0 }; test1(arr); test2(arr); test3(arr); test4(arr); return 0; }
思考一下 可不可以用下面这样形式接受呢
void test(int *parr[3]) //这是数组,压根不是指针
void test(int** parr) //二级指针存放int*的地址
void test(int* parr) //存放int的地址
🍌🍌指针笔试题
秘诀:指针指向的类型决定运算时跳过的字节
🔪 -> offer ①
int main() { int a[5] = { 1, 2, 3, 4, 5 }; int *ptr = (int *)(&a + 1); printf( "%d,%d", *(a + 1), *(ptr - 1)); return 0; } //程序的结果是什么?
=========================================================================
运行结果:
🔪 -> offer ②
//结构体的大小是8个字节 struct Test { int val; char c; }*p; //假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是8个字节 int main() { printf("%p\n", p + 0x1); printf("%p\n", (unsigned long)p + 0x1); printf("%p\n", (unsigned int*)p + 0x1); return 0; }
=========================================================================运行结果:
分析:
p+0x1 等价于 p+1,p指向的结构体类型大小为8,所以跳过8个字节
p强制转为unsigned long,我们说地址也是个整数嘛,整数加1不就是答案嘛
同理1,强转后跳过4个字节
🔪 -> offer ③
int main() { int a[5][5]; int(*p)[4]; p = a; printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); return 0; }
=========================================================================运行结果:
指针相减结果为元素个数
%d格式化输出为-4
-4补码 // 11111111 11111111 11111111 11111100
%p输出为十六进制的unsigned int,所以把-4当作无符号整数输出了。
好啦,感谢观看!!!