4 min to read
椋鸟C语言笔记#20
指针数组、指针模拟二维数组、数组指针、二维数组传参的本质、字符指针与字符串
![Featured image](https://res.cloudinary.com/dm7h7e8xj/image/upload/v1559820489/js-code_n83m7a.jpg)
萌新的学习笔记,写错了恳请斧正。
指针数组
整型数组存放整型,那么指针数组自然是存放指针的数组。
要定义一个指针数组,我们应该像这样写:
int* parr[3] = { pa, pb, pc };
因为方括号的优先级要高于解引用操作符,所以 parr 先与方括号结合,规定其为数组。其中存储的数据类型为 int*(整型指针)。
指针模拟二维数组
#include <stdio.h>
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {2,3,4,5,6};
int arr3[] = {3,4,5,6,7};
int* parr[3] = {arr1, arr2, arr3};
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for(j=0; j<5; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
上述代码就是用指针数组来模拟实现一个三行五列的二维数组
parr[i] 是访问 parr 数组的元素,找到的数组元素指向了整型一维数组,parr[i][j] 就是整型一维数
组中的元素。
上述的代码模拟出二维数组的效果,但实际上并非不是二维数组,每一行不是连续的。
数组指针
相反的,那数组指针就是指向数组的指针
但是如果我们写成下面这样,定义的就是指针数组而不是数组指针
int* parr[3] = { pa, pb, pc };
所以我们就要避免 parr 与方括号先结合,导致被认为是数组
那么就只需加一个括号就行:
int (*parr)[3] = &arr;
这样 parr 先与解引用操作符结合,被认为是指针,指向 int [3] 类型的数据(数组)
数组指针的初始化
可以看到,上面我是通过取地址数组名来初始化数组指针的。
因为数组指针是指向整个数组的,一般只能用这种方式来初始化与赋值(数组名的特例,上一篇笔记讲了)(类型应当是这种格式:int[3] *)
二维数组传参的本质
我们之前把二维数组作为参数传递给函数是这么写的:
#include <stdio.h>
void func(int arr[8][9], int m, int n)
{
//...
}
int main()
{
int arr[8][9] = { 0 };
func(arr, 8, 9);
return 0;
}
实参是二维数组名,形参直接写成二维数组
我们知道,二维数组其实是元素为一维数组的数组
根据数组名是数组首元素的地址,二维数组的数组名表示的就是第一个一维数组的地址
上述代码中,第一行的一维数组的类型就是 int [9],所以第一行的地址的类型就是数组指针类型 int(*)[9]。所以说二维数组传参本质上也是传递了地址,传递的是第一个一维数组的地址,那么形参也是可以写成指针形式的。如下:
#include <stdio.h>
void func(int (*arr)[9], int m, int n)
{
//...
}
int main()
{
int arr[8][9] = { 0 };
func(arr, 8, 9);
return 0;
}
字符指针与字符串
字符指针,就是指向字符的指针
一般我们这么用:
#include <stdio.h>
int main()
{
char ch = 'a';
char* pch = &ch;
printf("%c", *pch);
return 0;
}
但是,我们 看看下面这一段代码:
#include <stdio.h>
int main()
{
const char* pch = "abc"; //这里const可以省略
printf("%s", pch);
return 0;
}
这一段代码其实也是对的,能正常打印字符串 abc,那我们如何理解呢?
首先我们要知道一点,%s 作为格式控制字符,接受的其实是字符串第一个字符的地址。随后从这个地址开始,一个一个打印字符,直到遇到 \ 0 为止
那第一句怎么理解呢?难道是把字符串存储在字符指针 pch 中吗?
不是的,这里就涉及到字符串双引号的作用了。
字符串的双引号有 3 个作用:
- 在常量区申请一片空间用于存放字符串
- 在字符串结尾加上 \ 0
- 返回字符串第一个字符的地址
所以第一句其实是创建了字符串常量并将首元素地址传递给 pch
那么,我们其实也就可以这么写:
#include <stdio.h>
int main()
{
printf("%s", "abc");
return 0;
}
这样,同样可以将字符串 abc 打印出来。
!!!注意!!!
用等号对字符数组整体赋值只能与初始化同时进行,不能分开!
初始化的时候使用等号只是规定了其一开始指向的位置。在初始化完成后数组名就是一个指向首元素地址的指针常量了,不能进行等号赋值操作(不能改变指向)。
这时如果想整体赋值可以使用 strcpy 函数(后面会讲)
为了更好的理解字符串双引号的作用,我们来看看这一段代码:
#include <stdio.h>
int main()
{
char str1[] = "abcdef";
char str2[] = "abcdef";
const char *str3 = "abcdef"; //const可省略
const char *str4 = "abcdef"; //const可省略
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 and str2 are not same.
而 str3 和 str4 是创建的字符指针,其指向的是由双引号在常量区创建的字符串常量 abcdef。常量不像变量一样会重复创建,所以两个指针最终指向的其实就是同一个地址,输出 str3 and str4 are same.
Comments