目录
一、字符指针变量
二、数组指针变量
a.数组指针变量是什么
b.数组指针变量的书写格式
c.数组指针变量如何初始化
d.二维数组传参的本质
一、字符指针变量
在指针的类型中我们知道有一种指针类型为字符指针 char* 。
其一般使用:
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
但是,我们在实践中,会看到这样的使用方法:
int main()
{
const char* pstr = "hello boy.";
printf("%s\n", pstr);
return 0;
}
这里是把一个字符串放到pstr指针变量里了吗?
事实上,其本质是把字符串 hello boy. 首字符的地址放到了pstr中。因为 hello boy. 这一字符串的每个个字符在内存中连续存放,所以有了首字符地址就可以找到所有字符。
我们来观察如下代码(出自《剑指Offer》):
#include <stdio.h>
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;
}
这里str3和str4指向的是一个同一个常量字符串。
这是因为C/C++会把常量字符串存储到单独的⼀个内存区域,当几个指针指向同⼀个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
二、数组指针变量
a.数组指针变量是什么
数组指针变量,顾名思义,就是用来存放数组地址的指针变量,数组的地址是我们所认为的数组首元素的地址吗?那一个元素为 int 类型的数组,其指针变量不就为 int* 了吗?是这样的么,很显然,不是!首先我们要明白数组名和&数组名的区别,一个表示数组首元素地址,一个表示整个数组的地址,一个类型为 int* 一个类型为 int * [ n ] ,每 +1 一个跳过4个字节,一个跳过4 * n个字节,很明显的不同。(以元素为int类型数组为例)
b.数组指针变量的书写格式
下面代码哪个是数组指针变量?
int *p1[10];
int (*p2)[10];
第一条代码描述的是一个p1数组,其有10个元素,其每个元素为 int* 类型,这是一个指针数组。
第二天代码描述的是一个p2指针,其指向了一个,有10个元素,每个元素为 int 类型的数组。
指针变量:
int (*p)[10];
解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。这里要注意:[ ] 的优先级要高于 * 号的,所以必须加上()来保证p先和*结合。
c.数组指针变量如何初始化
数组指针变量是用来存放数组地址的,那怎么获得数组的地址呢?&数组名。
int arr[10] = {0};
&arr;//得到的就是数组的地址
如果要存放个数组的地址,就得存放在数组指针变量中:
&arr 和 p 的值与类型是完全一致的。
d.二维数组传参的本质
有了数组指针的理解,我们就能够讲一下二维数组传参的本质了。
过去我们有一个二维数组的需要传参给⼀个函数的时候,我们是这样写的:
#include <stdio.h>
void test(int a[3][5], int r, int c)
{
int i = 0;
int j = 0;
for(i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
这里实参是二维数组,形参也写成二维数组的形式,那还有什么其他的写法吗?
我们知道,数组名表示数组首元素的地址,一维数组我们知道数组首元素就是数组内存的个个变量,那么二维数组呢?二维数组的数组名表示的就是第一行的地址,是一维数组的地址。
arr + 1 相较于 arr 跳过了5 * sizof(int)个字节,这恰好为第一行的大小。
第一行的一维数组的类型就是 int [5] ,所以第一行的地址的类型就是数组指针类型 int(*)[5] 。那就意味着二维数组传参本质上也是传递了地址,传递的是第一行这个⼀维数组的地址,那么形参也是可以写成指针形式的。
#include <stdio.h>
void test(int(*p)[5], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
代码中的*(*(p + i) + j)很有说法,当p + i时,为 int *[5] 类型,当*(p + i) + j时,为 int* 类型,*(*(p + i) + j)时就为 int 类型了,逐层转变。