Hi~你好呀,这里是刹那芳间,浅算一下,距离上次更博客已经过去两个月啦~确实,我也没有想到还会有这么多小伙伴关注俺,soooooo俺又回来写博客啦!!!🙌😆💕(谢谢大家的关注~)最近正在学习指针,所以会整理出一篇指针的合集,希望对你有帮助~一起进步~~
前言
之前有在学校学习过指针,但内容是比较浅显的啊,相信很多学校都是这样子的,所以现在正学的是指针进阶,较之前会有一些些拔高,不管之前学得如何,请具备空杯心态,继续学习,打起精神,认认真真,不要急~慢慢消化~
📚关于指针的简单介绍:
1.指针是一个变量,用来存放地址,地址唯一标识一块内存空间;
2.指针的大小是固定的4/8个字节(32位平台/64位平台);
这里是个很重要容易误以为的点,曾经的我也以为char型的指针大小是一个字节,但不是,实际情况是,char型的指针大小和int型的指针大小都是4个字节,至于是4还是8,看运行平台,在vs2019的x86环境下是4个字节,x64是8个字节。
3.指针是有类型,指针的类型决定了指针+-整数的步长,指针解引用操作时的权限;
一个char型的指针 +1向后跳一个字节 -1向前走一个字节
一个int型的指针 +1向后跳四个字节 -1向前走四个字节
4.指针的运算
具体接触了三种:
1.指针 +- 整数
2.指针 - 指针
3.指针的关系运算
1.字符指针
一般场景:改变字符
Besides, 也可以这样使用:
#include <stdio.h>
int main()
{
char* p = "abcdef";//p是指针变量,存储的是字符串首元素a的地址
printf("%c\n", *p);//打印第一个字符,即a
printf("%s\n", p);//打印整个字符串,即abcdef
return 0;
}
在我所用的编译器环境下,指针的大小是4个字节,字符串abcdef再加上个'\0',是7个字节,指针变量p是存不下的,所以规定无论哪个编译器,将一个常量字符串放到一个指针变量里,指针变量存储的是该字符串的第一个字符的地址。
这里的" "双引号内的字符串是常量字符串,里面的内容是不能被改变的,
比如:
想要将这个字符串的第一个元素变成字符w,于是你写了这么一个代码:
好家伙,喵喵喵,报错 error 运行了 异常
所以有些时候在定义一个常变量时,前面加一个const , 对于在写一个很长的代码的时候,出现错误时可以减轻一些调试量,加const,欲改变这个常变量,编译器是不让通过的,无法编译
一道面试题:(思考一下)
#include <stdio.h>
int main()
{
const char* p1 = "abcdef";
const char* p2 = "abcdef";
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (p1 == p2)
{
printf("p1==p2\n");
}
else
{
printf("p1!=p2\n");
}
if (arr1 == arr2)
{
printf("arr1==arr2\n");
}
else
{
printf("arr1!=arr2\n");
}
return 0;
}
这里的运行结果最终是什么样子呢?
恭喜你~说对了~
详解:
变量p1所指向的字符串是abcdef,变量p2所指向的字符串也叫做abcdef,这两个字符串都是常量字符串并且相同,都不能被修改,试问,在电脑中有没有必要保留两份吗?
答案很简单,电脑中保存一份,共用 ,这样子,内存空间会得到一定的节省。
所以p1和p2指向的是同一个字符串abcdef,它们所存储的地址一模一样,所以p1==p2
char arr1[] = "abcdef";
char arr2[] = "abcdef";
这两句代码的意思是用字符串abcdef去初始化arr1和arr2这两个数组,这两个数组是不一样的,它们在内存中有着独立的空间,数组名表示首元素的地址,所以arr1!=arr2
2.指针数组
存放指针的数组。
📚类比理解:
int arr[10]; //整型数组,存放整型的数组
char ch[2]; //字符数组,存放字符的数组
使用场景(一): (特意将地址直接初始化数组中的这种情况是比较少见的)
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
int* arr[3] = { &a,&b,&c };//arr就是一个指针数组
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%d ", *(arr[i]));//对这个地址(指针)解引用
}
return 0;
}
程序运行结果:
使用场景(二):
#include <stdio.h>
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* parr[3] = { arr1,arr2,arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
//printf("%d ", parr[i][j]);
printf("%d ", *(parr[i] + j));
}
printf("\n");
}
return 0;
}
这有点像一个二维数组,不是吗?🤸♀️
3.1 数组指针
数组指针是能够指向数组的指针,不是数组
int* p1[10]; 指针数组
int (*p2)[10]; 数组指针
巧记:
[ ]的优先级高于 *
p1先与[ ]结合,说明p1是一个数组,再看int* ,说明p1数组里存的是整型指针,即指向整型的指针;
( ) 优先,所以p2先与 * 结合,说明p2是一个指针变量,然后指针指向的是一个大小为10个整型的数组
arr 和 &arr 在数值上相同,但类型不同。
arr 是数组名,也是首元素的地址,类型是 int*
&arr 是整个数组的地址,类型是 int (*)[10] ,[ ]内的数字不能省
3.2 数组名
通常情况下,人们所说的数组名指的首元素的地址,
但是有两个例外:
1. sizeof(数组名),这里的数组名表示的是整个数组,sizeof(数组名) 计算的是整个数组的大小;
2. &(数组名) ,这里的数组名表示的也是整个数组, &(数组名) ,取得是整个数组的地址。
3.3 数组指针有什么好处
一般会用在二维数组
函数传参时,二维数组传过去,用数组指针指向一维数组,进行降维,会更加方便。
任务列表:
写一个函数打印一个二维数组。
法一:
#include <stdio.h>
void printf1(int arr[3][5], int c, int r)
{
int i = 0;
for (i = 0; i < c; i++)
{
int j = 0;
for (j = 0; j < r; j++)
{
printf("%d ", arr[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} };
printf1(arr, 3, 5);
return 0;
}
法二:
#include <stdio.h>
void printf2(int (*p)[5], int c, int r)
{
int i = 0;
for (i = 0; i < c; i++)
{
int j = 0;
for (j = 0; j < r; j++)
{
//p指向第0行,p+i指向第i行
//*(p+i)相当于拿到了第i行,也相当于第i行的数组名
//数组名表示首元素的地址,*(p+i)表示第i行第一个元素的地址
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} };
//二维数组名相当于首元素的地址,相当于第一行的地址,二维数组的首元素是第一行
printf2(arr, 3, 5);
return 0;
}
📚一些数组的等价写法:
arr[i]
*(arr+i)arr[i][j]
*(arr+i)[j]
*(*(arr+i)+j)
今天的内容就先到这里啦~
谢谢你的耐心阅读,
愿:
Keep up working hard !