深入了解指针
前面已了解到
1)指针就是个变量,用来存放地址,地址是唯一的,可以标识一块内存空间。
2)指针的大小是固定的4/8个字节(32位平台/64位平台)。
3)指针是有类型的,指针的类型决定了指针的 +/- 整数的步长,指针解引用操作时候的权限。
4)指针的运算。
以此为基础,深入了解指针。
1、字符指针
在指针的类型中有一种指针类型为字符指针 char* 。
示例如下:
//字符数组
int main()
{char* pc = "hello"; //字符指针printf("%c\n", *pc); //打印结果 h 。对指针解引用得到指针指向的字符。printf("%s\n", pc); //打印结果 hello 。 字符串打印,提供第一个字符串的地址即可。char arr[] = "hello"; //字符数组arr[0] = 'w'; //将字符数组第一个元素改为 w 。return 0;
}
一般使用演示:
//字符指针用法一
int main()
{char a[] = "hello";char* pa = &a; //定义字符指针,指向数组地址,数组地址与数组首元素地址一致//即此处pa指向的是数组a[]中首元素。printf("%s\n", pa); //打印结果 hello 。*pa = 'w'; //对pa解引用,得到的是a[0],将a[0]改成w。printf("%s\n", pa); //打印结果 wello 。return 0;
}
另一种使用演示:.
//字符指针用法二
int main()
{char* pstr = "hello";printf("%s\n", pstr); //打印结果 hello 。 *pstr = 'w'; //报错,因为pstr此时指向的是常量字符串。//实际代码一般写成 const char* pstr = "hello" 。return 0;
}
代码 const char* pstr = "hello." 本质上是把字符串 “hello.” 首字符的地址放到了pstr中。即把一个常量字符串的首字符 h 的地址存放到指针变量 pstr 中。
C/C++ 会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。
2、指针数组
指针数组是一个存放指针的数组。
演示如下:
//指针数组用法一
int main()
{int a = 0;int b = 2;int c = 3;int* arr[3] = { &a,&b,&c }; //定义数组,数组含3个指针变量int i = 0;for (i = 0; i < 3; i++){printf("%d ", *(arr[i])); //打印结果0 2 3 。//即对数组某个元素解引用,该元素为地址,解引用得到地址中的数据。}return 0;
}
还可模拟实现二维数组,演示如下:
//指针数组用法二
int main()
{int a[] = { 1,2,3,4,5 };int b[] = { 2,3,4,5,6 };int c[] = { 3,4,5,6,7 };int* arr[3] = { a,b,c }; //将三个数组的首元素地址放进指针数组中。int i = 0;for (i = 0; i < 3; i++) //循环三次,每次进入一个数组{int j = 0;for (j = 0; j < 5; j++) //循环五次,每次打印数组中的一个元素。{printf("%d ", *(arr[i]+j)); //打印结果//1 2 3 4 5 //2 3 4 5 6//3 4 5 6 7}printf("\n");}return 0;
}
3、数组指针
3.1、数组指针的定义
整型指针:int * pint 是能够指向整型数据的指针。
浮点型指针:float * pf 是能够指向浮点型数据的指针。
数组指针:int (*p) [10] 是能够指向数组的指针。
int (*p) [10] 中,p 先和 * 结合,说明p是一个指针变量,然后指针指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
需要注意,[ ] 的优先级要高于 * ,所以必须加上()来保证 p 先和 * 结合。
演示如下:
//数组名和数组指针
int main()
{int a = 0; //定义整型变量 aint* pa = &a; //定义整型指针 pachar b = '1'; //定义字符变量 bchar* pb = &b; //定义字符指针 pbfloat c = 1.2; //定义浮点型变量 cfloat* pc = &c; //定义浮点型指针 pcint arr[5] = { 1,2,3,4,5 }; //定义数组 arr[5]int(*parr)[5] = &arr; //定义数组指针 parrprintf("%d\n", *(*parr+1)); //打印结果 2 。//*parr 得到的是数组名 arr 。//数组名 arr 代表数组首元素地址,对其 +1,得到数组第二个元素地址。//对 *parr+1 解引用,得到该地址上的元素 2 。return 0;
}
3.2、&数组名 VS 数组名
对于数组 int arr[10] ,arr 是数组首元素的地址,&arr 是数组的地址。arr +1 得到的是数组第二个元素的地址,&arr +1得到的是下一个数组(或数据)的地址。
&arr 的类型是 int (*) [10] ,是一种数组指针类型,&arr+1会跳过整个数组的大小,即 &arr+1 与 &arr 的差值是40(数组是10个整型,占40个字节)。
注意:
1)sizeof (数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2)&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3)除此之外,所有的数组名都表示首元素的地址。
演示如下:
//数组指针使用
int main()
{int arr[5] = { 1,2,3,4,5 };int* parr1= arr; //定义整型指针 parr1,值为数组首元素地址。int(*parr2)[5] = &arr; //定义数组指针 parr2,值为数组地址。printf("%p\n", parr1); //打印结果 00D6F838printf("%p\n", parr1+1); //打印结果 00D6F83C 差值为 4 。printf("%p\n", parr2); //打印结果 00D6F838printf("%p\n", parr2+1); //打印结果 00D6F84C 差值为 14(十六进制,转换为十进制是20)。//可以看到parr1代表的容量是1个整型,parr2代表的容量是整个数组(5个整型长度是20)。return 0;
}
3.3、数组指针的使用
数组指针中存放的是数组的地址。
数组指针在一维数组中的使用(感觉没优点),演示如下:
//数组指针在一维数组中的使用
int main()
{int arr[5] = { 1,2,3,4,5 };int(*parr)[5] = &arr; //定义数组指针 parr,值为数组地址。int i = 0;for (i = 0; i < 5; i++){printf("%d ", *(*parr + i)); //打印结果 1 2 3 4 5 。//*parr 得到的是数组名 arr 。//数组名 arr 代表数组首元素地址,对其 +i,得到数组第i+1个元素地址。//对 *parr+i 解引用,得到该地址上的元素 arr[i+1] 。}return 0;
}
数组指针在二维数组中的使用,演示如下:
//数组指针在二维数组中的使用
int main()
{int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} }; //定义二维数组,三行,每行四列。int(*parr)[4] = arr; //定义数组指针,指向二维数组的首元素。//二维数组的首元素是第一行数组,所以要用数组指针。int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 4; j++){printf("%d ", *(*(parr + i) + j)); //数组指针+i(parr+i),得到第 i+1 行数组的地址,对其解引用 *(parr + i) 得到这一行数组,即数组名。//数组名就是数组首元素地址,对这一行数组名+j (*(parr + i) + j),得到该行数组第 j+1 个元素的地址。//对第 j+1 个元素的地址解引用 *(*(parr + i) + j) 得到该地址中的数据 。//打印结果//1 2 3 4 //2 3 4 5 //3 4 5 6 }printf("\n");}return 0;
}
4、数组参数、指针参数
4.1、一维数组传参
演示如下:
//一维数组传参
//void test(int arr) //以整型类型传参,无法得到数组
//{
// printf("%d\n",arr); //打印结果 16709412 ,为数组地址。
//}//void test(int arr[]) //以数组类型传参,可以得到数组
//{
// printf("%d\n",arr[1]); //打印结果 0 。
//}//void test(int arr[5]) //以数组类型传参,可以得到数组
//{
// printf("%d\n", arr[0]); //打印结果 0。
//}void test(int* arr) //以指针类型传参,可以得到数组
{printf("%d\n", *arr); //打印结果 0 。
}//void test2(int* arr) //以指针类型传参,可以得到数组
//{
// printf("%d\n", *arr); //打印结果 0 。
//}void test2(int** arr) //以二级指针类型传参,可以得到数组
{printf("%d\n", *arr); //打印结果 0 。
}int main()
{int arr[5] = { 0 };int* arr2[5] = { 0 };test(arr);test2(arr2);return 0;
}
4.2、二维数组传参
演示如下:
//二维数组传参
//void test(int arr[][4]) //以二维数组类型传参,写明列数,可以得到数组元素
//{
// printf("%d\n", arr[2][1]); //打印结果 4 。
//}//void test(int arr[3][4]) //以二维数组类型传参,写明行数和列数,可以得到数组元素
//{
// printf("%d\n", arr[2][1]); //打印结果 4 。
//}//void test(int arr[3][]) //以二维数组类型传参,不写明列数,报错
//{
// printf("%d\n", arr[2][1]); //打印结果 4 。
//}//void test(int* arr) //以整型指针类型传参,无法得到数组
//{
// ;
//}//void test(int* arr[4]) //以整型指针数组类型传参,无法得到数组
//{
// ;
//}//void test(int (*arr)[4]) //以数组指针类型传参,可以得到数组
//{
// printf("%d\n", (*arr+1)[2]); //打印结果 4 。这里是第二行的第三个元素。
//}void test(int** arr) //以二级指针类型传参,无法得到数组
{;
}int main()
{int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };test(arr);return 0;
}
4.3、一级指针传参
演示如下:
级指针传参void test(int* x)
{(*x) = (*x) + 1;
}int main()
{int a = 0;int* pa = &a;test(pa);printf("%d\n", a); //打印结果 1 。//说明一级指针传参,可以通过指针变量传参。test(&a);printf("%d\n", a); //打印结果 2 。//说明一级指针传参,可以通过取变量地址形式传参。return 0;
}
4.4、二级指针传参
演示如下:
//二级指针传参
void test(int** x)
{(**x) = (**x) + 1;
}int main()
{int a = 0;int* pa = &a;int** ppa = &pa;int* arr[10] = { &a };int* (*parr)[10] = &arr;test(ppa);printf("%d\n", a); //打印结果 1 。//说明二级指针传参,可以通过二级指针变量传参。test(&pa);printf("%d\n", a); //打印结果 2 。//说明二级指针传参,可以通过取一级指针变量地址形式传参。test(parr);printf("%d\n", a); //打印结果 3 。//说明二级指针传参,可以通过一级指针数组的指针传参。test(&arr);printf("%d\n", a); //打印结果 4 。//说明二级指针传参,可以通过取一级指针数组地址的形式传参。//注:这里其实传的是数组地址,严格来说格式不规范。test(arr);printf("%d\n", a); //打印结果 4 。//说明二级指针传参,可以通过一级指针数组名的形式传参。//这是因为数组名就是数组首元素地址。return 0;
}