指针的回顾
在C语言中,指针是一个变量,与其他数据不同的是,它的作用是用来存储其它变量的内存地址。指针可以指向不同类型的数据,包括整数、浮点数
、字符、数组等。通过使用指针,我们可以直接访问和修改存储在内存中的数据,而不需要进行复制或者传递大量的数据。
这是指针的大概情况,下面就要对指针进一步研究。
字符指针
对于字符来说,我们可以用char类型来创建变量,那么就有char*类型的指针,通常我们称之为字符指针。
先看以下代码:
int main() { char ch = 'w'; char *pc = &ch; *pc = 'w'; return 0; }
创建一个字符变量ch,字符指针pc指向ch的地址,对pc解引用就是'w'。
这是常规的用法,但在大多数情况下,以下情况更加普遍使用:
int main() { const char* pstr = "hello cc."; printf("%s\n", pstr); return 0; }
在这里,打印出来的就是“hello cc",但pstr存的是字符串的首元素地址 ,也就是说,对ptsr解引用,得到的是'h'。
在这里,会发现我在指针pstr加入了const,这是因为字符串的地址本来就不可改变,加上const,增加程序的可读性和维护性。看下面例子:
#include <stdio.h> int main() { char str1[] = "hello cc."; char str2[] = "hello cc."; const char *str3 = "hello cc."; const char *str4 = "hello cc."; 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和str2,它们是字符数组,对于数组来说,会在内存中开辟空间,而str1和str2是两个不同的字符数组,虽然它们的内容相同,但开辟的空间肯定是不一样的,而数组名一般又是指首元素的地址,所以各不相同;对于str3和str4,它们是字符指针,相当与常量字符串自己开辟了空间,然后两个指针都会指向内容相同的字符串首元素。
指针数组
顾名思义,它是一个数组,只是每个数组里面存放的是指针;
我们可以用指针数组实现二维数组
int main() { int arr1[] = { 1,2,3,4,5 };//arr1 - int* int arr2[] = { 2,3,4,5,6 }; int arr3[] = { 3,4,5,6,7 }; //指针数组 int* arr[3] = { arr1, arr2, arr3 }; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 5; j++) { printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
对于一维数组来说,本质上就是一维指针,也就是arr1 类型为int*.
数组指针
指针的定义
数组指针,是一个指针,指向数组的指针。
int *p1[10];//指针数组
int (*p2)[10];//数组指针
这里要注意:[]的优先级要高于号的,所以必须加上()来保证p先和结合。
&数组名VS数组名
int arr[10];
我们知道,arr是指首元素地址,&arr指的是整个数组的地址;
int main() { int arr[10] = { 0 }; printf("%p\n", arr); printf("%p\n", &arr[0]); printf("%p\n",&arr); return 0; }
上面虽然的地址都是一样,这是因为刚好都指向了首元素地址
int main() { int arr[10] = { 0 }; printf("%p\n", arr);// printf("%p\n", arr+1);// printf("%p\n", &arr[0]);// printf("%p\n", &arr[0]+1);// printf("%p\n", &arr);// printf("%p\n", &arr+1);// return 0; }
但我们把16进制转换为10进制后&arr+1就增加了40个bit,但arr+1相对于arr只增加了4bit;
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址.
数组指针的使用
直接看以下代码:
#include <stdio.h> 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); print_arr2(arr, 3, 5); return 0; }
对于一维数组来说,数组名表示首元素地址,而二维数组中,首元素表示的是第一行的地址,所以把数组指针看作是二维数组中,指向一维数组的指针。也就是所,二维数组可以由数组指针来表示。
数组参数、指针参数
一维数组传参
#include <stdio.h> void test(int arr[]) {} void test(int arr[10]) {} void test(int *arr) {} void test2(int *arr[20]) {} void test2(int **arr) {} int main() { int arr[10] = {0}; int *arr2[20] = {0}; test(arr); test2(arr2); }
对于以上所有test函数来说,所有形式参数都是可以接收到实参的;数组本质来说就是指针,所以可由指针来接收;而指针数组来说,就要用二维指针来接收。
二维数组传参
void test(int arr[3][5]) {} void test(int arr[][5]) {} //对于二维数组来说,行的数字可以省略,列的数字不能省略 void test(int *arr) {} void test(int (*arr)[5]) {} //数组指针是可以的,但对于指针数组来说,数组中包含的是指针,虽然说 //数组本质就是指针,但是由于数组的限制,指针的地址可能会出现连续的 //状态,无法确定一行中有多少列,故不可使用。
一级指针传参
#include <stdio.h> 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; }
对于数组来说,元素的地址是连续存在的,所以可以用指针叠加的方式进行循环遍历。
二级指针传参
#include <stdio.h> void test(int** ptr) { printf("num = %d\n", **ptr); } int main() { int n = 10; int*p = &n; int **pp = &p; test(pp); test(&p); return 0; }
num=10
num=10
对于实参来说,只要是一级指针的地址,就可以传参,而函数中的形参一般只用二级指针接收即可。
总的来说,对于数组传参,形参部分可以是指针,也可以是数组;
而对于指针传参,实参部分只要是地址,都可以当作实参,形参只用指针来接收。