- 1️⃣指针概念
- 1.字符指针
- 2.️指针数组
- 3.️数组指针
- 3.1数组指针的定义
- 3.2 &数组名 数组名
- 3.3数组指针的使用
- 4.数组参数、指针参数
- 4.1一堆数组传参
- 4.2二维数组传参
- 4.3一级指针传参
- 4.4二级指针传参
1.字符指针1.指针就是个变量,用来存放地址,地址唯一标识的一块内存空间。
2.指针的大小是固定的4/8个字节(32位平台/64平台)。
3.指针是有类型的,指针的类型决定了指针的+ - 整数的范围和指针解引用操作的时候的权限。
4.指针的运算。
在指针的类型中有一种指针类型为字符指针 char* ;
一般使用:
#includeint main() { char ch = 'w'; char* pc = &ch;//取ch地址放入pc中 *pc = 'c'; //通过pc解引用把ch存入的数据改成'c' return 0; }
还有一种使用方法:
int main()
{
const char* pstr = "hello bit.";
//这里是把一个字符串放到pstr指针变量里了吗?
printf("%sn", pstr);//显然不是!输出 h
return 0;
}
代码:const char* pstr = "hello bit."特别容易认为是把字符串hello bit放到字符指针pstr里了,但本质上是把字符串hello bit.首字符的地址放到了pstr中。
上面代码的意思是把一个常量字符串的首字符h的地址存放到指针变量pstr中。
于是有了这样的面试题:
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
if (str1 == str2)
printf("str1 and str2 are samen");
else
printf("str1 and str2 are not samen");
if (str3 == str4)
printf("str3 and str4 are samen");
else
printf("str3 and str4 are not samen");
return 0;
}
这里最终输出的结果是:
这里的str3和str4指向的是同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针,去指向同一个字符串的时候,它们实际会指向同一块内存,但是用相同的常量字符串去初始化不同的数组的时候,不同数组会开辟不同的内存块,所以str1和str2不同,str3和str3相同。
指针数组是一个存放指针的数组
int* arr1[10];//整形指针的数组
char* arr2[4];//一级字符指针数组
char** arr3[4];//二级字符指针数组
举例:
int a = 1;
int* pa = &a;
int* arr1[3] = {pa};
//arr1[0]访问pa的地址
-* ——————————————————*
3.️数组指针 3.1数组指针的定义数组指针是指针
整形指针 int* pa能够指向整形数据的指针,那数组指针应该是:能够指向数组的指针。
int (*p)[10];
3.2 &数组名 数组名解释:p先和*结合,说明p是一个指针变量,然后指针指向的是一个大小为10个整形的数组。所以p是一个指针,指向一个数组,叫数组指针。
这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和 *结合。
对于下面的数组:
int arr[10];
arr是数组名,数组名表示数组首元素的地址。
那 &arr是什么呢?我们看一段代码:
#includeint main() { int arr[10] = {0}; printf("%pn", arr); printf("%pn", &arr); return 0; }
由此可见数组名 和 &数组名打印的地址是一样的。
难道两个真的一样吗?
再来看一段代码:
#includeint main() { int arr[10] = { 0 }; printf("arr = %pn", arr); printf("&arr= %pn", &arr); printf("arr+1 = %pn", arr+1); printf("&arr+1= %pn", &arr+1); return 0; }
根据上面的运行结果我们发现:其实数组名 和 &数组名,虽然值是一样的,但是意义应该不一样。
实际上:&arr表示的是整个数组的地址,而不是数组首元素的地址。(细品一下)
本例中&arr的类型是:int(*)[10],是一种数组指针类型。
数组的地址+1,跳过整个数组大小,所以&arr+1相当于&arr的差值是40。
3.3数组指针的使用为什么&arr+1跳过40个字节?
因为:arr的类型是int [10]10个int相当于40个字节。
数组的类型决定了+1/ -1 跳过几个字节。
#includeint main() { int arr[10] = {1,2,3,4,5,6,7,8,9,0}; int (*p)[10] = &arr; //把数组arr的地址赋值给数组指针变量p //但是我们一般很少这样写代码 return 0; }
一个数组指针的使用:一般使用在二维数组上
void print1(int (*pa)[5], int r, int c)
//int (*pa)[5]一维数组指针接收
{
int i = 0;
for (i = 0; i < r; i++)
{//pa+0指向第1行, + 1指向第2行...
//+0找到第一个1维数组,+1找到第2个一维数组
int j = 0;
for (j = 0; j < c; j++)
{
//printf("%d ", pa[i][j]);
//等同于下列
printf("%d ", *(*(pa+i)+j));
// (pa+i)——>&arr+i找到第i行地址
//*(pa+i)——>arr[i]
//解引用找第i行数组名,相当于首元素地址
//*(*(pa+i)+j)——>arr[i][j]
//再解引用找到第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 };
print1(arr, 3, 5);
//二维数组的首元素地址是第一行
//所以这里传参的是
//相当于传了一个一维数组的地址
return 0;
}
你要知道:int (*p) [5]
p的类型是:int(*)[5] ,指向一个整形数组,数组有5个元素
p+1 跳过5 个int元素的数组
学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
int arr[5];//arr是一个数组,数组有5个元素,每个元素是int型 int *parr1[10];//parr是数组,数组有10个元素,每个元素是int*型 int (*parr2)[10];//parr2是一个指针,指向一个有10个元素的数组,每个元素是int型,所以parr2是数组指针 int (*parr3[10])[5];//parr3是一个数组,数组有10个元素,每个元素是int(*)[5], //该指针指向一个含有10个元素的数组,所以parr3是一个数组指针数组
4.数组参数、指针参数对int (*parr3[10])[5];不理解,可以看下列补充:
4.1一堆数组传参在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
#include4.2二维数组传参void test(int arr[])//ok,数组大小可以不写 {} void test(int arr[10])//ok {} void test(int *arr)//ok,整形地址放入整形指针 {} void test2(int *arr[20])//ok,数组传参,形参可以数组也可以指针接收,20也可以省略 {} void test2(int **arr)//ok,二级指针接收一级指针地址 //数组名相当于首元素地址,arr2是int*类型的地址 {} int main() { int arr[10] = {0}; int *arr2[20] = {0}; //20个元素,每个元素int*.. //数组名相当于首元素地址,arr2是int*类型的地址 test(arr);//数组名相当于首元素地址 test2(arr2); }
void test(int arr[3][5])//ok,二维数组接收
{}
void test(int arr[][])//NO,形参的二维数组,行可省,列不行
{}
void test(int arr[][5])//ok,行省了,列没省
{}
void test(int *arr)//NO,arr是二维数组,
//首元素地址此时代表的是第一行的地址,相当于是一维数组
//一维数组不能放入一级指针
{}
void test(int* arr[5])//NO
{}
void test(int (*arr)[5])//ok,指针指向5个元素,每个元素是int
{}
void test(int **arr)//NO,二级指针只能接收一级指针的变量的地址
{}
int main()
{
int arr[3][5] = {0};
test(arr);
//数组传参一般写成--数组名
//除非传某个元素,——>&arr[1],arr[1]
}
4.3一级指针传参总结:
1 . 二维数组传参,函数形参的设计只能省略第一个 [ ] 的数字。
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
这样才方便运算。
2 . 二维数组的数组名,表示首元素地址,其实是第一行的地址,第一行是一个一维数组
3.二级指针只能接收一级指针变量的地址
#includevoid print(int *p, int sz) { int i = 0; for(i=0; i printf("%dn", *(p+i));//*(p+i)==p[i] //(p+i),如果i=1, //+i跳过一个整形地址,再解引用, //就找到了相对应的元素 } } int main() { int arr[10] = {1,2,3,4,5,6,7,8,9}; int *p = arr;//1的地址赋给p int sz = sizeof(arr)/sizeof(arr[0]); //一级指针p,传给函数 print(p, sz); return 0; }
思考:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
void test1(int *p)
{}
//test1函数能接收什么参数?
int main()
{
int a =10;
test1(&a);//ok
int* pa = &a;
test1(pa);//ok
int arr[5];
test1(arr);//ok
}
4.4二级指针传参只要传给函数本质是一个指针,实参和形参类型能匹配上,就没问题。
#includevoid test(int** ptr) { printf("num = %dn", **ptr); } int main() { int n = 10; int*p = &n; int **pp = &p; test(pp);//ok,本身是二级指针,传参二级指针接收 test(&p);//ok,二级指针接收一级指针变量地址 return 0; }
思考:当函数的参数为二级指针的时候,可以接收什么参数?
void test(char **p)
{
}
int main()
{
char c = 'b';
char*pc = &c;
char**ppc = &pc;
char* arr[10];
test(&pc);//ok,二级指针接收一级指针变量地址
test(ppc);//ok,本身是二级指针,传参二级指针接收
test(arr);//Ok?
//ok,每个元素是int*,首元素地址就是int*
return 0;
}
//三级指针解引用也可以....四级....五级....
如果你觉得文章不错,记得点赞+分享喔~如果有地方不对,欢迎随时评论指出 ~


![【C语言进阶】——指针进阶[Ⅰ] 【C语言进阶】——指针进阶[Ⅰ]](http://www.mshxw.com/aiimages/31/960964.png)
