前言:
进阶指针我打算分三篇文章进行讲解,第一篇主要围绕数组与指针进行,第二篇主要围绕函数指针进行,最后一篇会引入面试题讲解。
指针的基本概念:
1、指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2、指针的大小是固定的4/8个字节(32位/64位)
3、指针是有类型,指针的类型决定的指针+-整数的步长,指针解引用操作的权限。
一、字符指针
字符指针就是指针类型为字符的指针:char*。
可以这样定义:
int main() { char ch = 'w'; char* pc = &ch; *pc = 'w'; return 0; }
如果是字符串呢?
int main() { char* pstr = "hello world."; printf("%s\n", pstr); return 0; }
注意这里指针变量pstr中存放的是字符串中首字符的地址。
一道面试题:
int main() { char str1[] = "hello world."; char str2[] = "hello world."; const char* str3 = "hello world."; const char* str4 = "hello world."; if (str1 == str2) printf("str1 and str2 are same\n"); else printf("str1 and str2 are not same\n"); if (str3 == str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0; }
输出结果:
str1 and str2 are not same
str3 and str4 are same
分析:str1与str2为两个数组,而数组名代表首元素地址,所以str1与str2不相同。而str3与str4指向的是同一个字符串常量,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。
二、指针数组与数组指针
(一)指针数组
指针数组是数组,数组存放的内容是指针,那么很简单定义数组的时候,数组类型定义为XXX*即可,如:
int* arr1[10]; //整型指针的数组 char* arr2[4]; //一级字符指针的数组 char** arr3[5];//二级字符指针的数组
(二)数组指针
数组指针是指针,指针指向的内容为数组。
观察下面的代码,哪个是数组指针?
int* p1[10]; int (*p2)[10];
注:[]操作符的优先级高于*。
分析:p1为指针数组。p2先于*结合,即p2为指针变量,指针变量的类型为int [10]整型数组,所以p2为数组指针。
我们知道数组名arr表示首元素的地址,那么&arr 取出的是不是首元素的地址呢?
其实数组名表示数组首元素的地址有两个例外:
1、sizeof(arr),sizeof计算的是整个数组大小,单位为字节。
2、&arr,此时取出的是整个数组的地址。
我们利用下面的代码进行验证:
int main() { int arr[10] = { 0 }; printf("arr = %p\n", arr); printf("&arr= %p\n", &arr); printf("arr+1 = %p\n", arr + 1); printf("&arr+1= %p\n", &arr + 1); return 0; }
结果为:
我们发现&arr+1跳过的是整个数组的大小。
数组指针一般用于二维数组中。如:
void print_arr1(int arr[3][5], int row, int col) { int i = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } void print_arr2(int(*arr)[5], int row, int col) { int i = 0; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { printf("%d ", arr[i][j]); } printf("\n"); } } int main() { int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 }; print_arr1(arr, 3, 5); //数组名arr,表示首元素的地址 //但是二维数组的首元素是二维数组的第一行 //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址 //可以数组指针来接收 print_arr2(arr, 3, 5); return 0; }
三、数组传参与指针传参
如何将【数组】或【指针】作为参数传给函数呢?
(一)数组传参
正确设计:
void test1(int arr[]) {} void test1(int arr[10]) {} void test1(int* arr) {} void test2(int* arr[20]) {} void test2(int** arr) {} void test3(int arr[3][5]) {} void test3(int arr[][5]) {} void test3(int (*arr)[5]) {} int main() { int arr1[10] = { 0 }; int* arr2[20] = { 0 }; int arr3[3][5] = { 0 }; test1(arr1); test2(arr2); test3(arr3); }
错误设计:
void test3(int arr[][])//err {} void test3(int* arr)//err {} void test3(int* arr[5])//err {} void test3(int** arr)//err {} int main() { int arr1[10] = { 0 }; int* arr2[20] = { 0 }; int arr3[3][5] = { 0 }; test1(arr1); test2(arr2); test3(arr3); }
错误分析:
首先要搞清楚函数参数的类型,test3(arr3)中明显传入的是arr3数组的首元素地址,又arr3为二维数组,所以该函数实际传入参数为arr3首行元素的地址,本质是指针。
然后我们看函数定义中设计的参数:
void test3(int arr[][])二维数组可以省略行,但是不能省略列,所以该函数设计错误。
void test3(int* arr)此函数设计参数类型为指针数组,本质是数组,与传入参数不符,所以该函数设计错误。
void test3(int* arr[5])同上。
void test3(int** arr)此函数设计参数类型为二级指针数组,本质是数组,与传入参数不符,所以该函数设计错误。
(二)指针传参
void print(int* p, int sz) { int i = 0; for (i = 0; i < sz; i++) { printf("%d\n", *(p + i)); } } int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9 }; int* p = arr; int sz = sizeof(arr) / sizeof(arr[0]); //一级指针p,传给函数 print(p, sz); return 0; }
当函数的参数部分为一级指针时,该函数能接收什么参数呢?
比如:void test(char* p)
以下供参考:
void test(char* p) {} int main() { char ch = 'w'; char ptr = &ch; char arr[] = "abcdef"; test(&ch); test(ptr); test(arr); }
当函数的参数部分为二级指针时,该函数又能接收什么参数呢?比如:
void test1(int** p1)
void test2(char** p2)
以下供参考:
void test1(int** p) {} void test2(char** p) {} int main() { int n = 10; int* p1 = &n; int** pp = &p1; test1(pp); test1(&p1); char c = 'b'; char* pc = &c; char** ppc = &pc; char* arr[10]; test2(&pc); test2(ppc); test2(arr); return 0; }
今天的内容就分享到这,接下来博主还会继续跟进进阶指针的更新,关注博主不迷路🔥🔥🔥