指针的这些知识你知道吗?C语言超硬核指针进阶版3w+字详解+指针笔试题画图+文字详细讲解
Posted 小赵小赵福星高照~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了指针的这些知识你知道吗?C语言超硬核指针进阶版3w+字详解+指针笔试题画图+文字详细讲解相关的知识,希望对你有一定的参考价值。
指针的进阶
指针初阶的基础知识:
1、指针就是地址,地址就是指针,指针存在变量里叫指针变
2、指针的大小是4(32位平台)/8个字节(64位平台)
3、指针类型的意义:决定指针±整数的步长,指针解引用访问的字节
4、指针的运算
本人初阶指针的总结:C语言指针初阶
字符指针
int main()
{
// char ch='q';
// char *pc=&ch;
//一个指针是可以指向一个字符串的,这个字符串是常量字符串
char *ps="hello world";
//ps本质上把字符串首字符'h'的地址存进去了
char arr[]="hello world";
//和ps不相同,arr是数组,ps是变量,arr是吧hello world都存进数组里,而ps只存了首字符的地址
printf("%c\\n",*ps);//h
//两种定义的名字都能打印字符串,都是首字符地址
printf("%s\\n",ps);
printf("%s\\n",arr);
return 0;
}
注意:
ps本质上把字符串首字符’h’的地址存进去了
arr和ps不相同,arr是数组,ps是变量,arr是把hello world所有字符都存进数组里,而ps只存了首字符的地址
我们来看一道笔试题
#include <stdio.h>
int main()
{
char str1[] = "hello worid.";
char str2[] = "hello world.";
char *str3 = "hello world.";
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;
}
这个代码结果会打印什么呢?
解读:
数组名是首元素的地址,数组str1和str2创建的空间不同,首元素地址就不相同,所以str1和str2肯定不相等
str3和str4是指针变量,str3和str4存放的是hello world字符串的首字符的地址,hello world这个字符串叫做常量字符串 ,是不能修改的,我们看一下我们如果要改的话的测试结果
这里代码直接挂掉了,我们进行调试后发现会进行报错:
所以常量字符串是不能修改的
str3和str4中的常量字符串是不能被修改的,两份相同的内容,而且不能被修改,是没有必要存两份的,所以这样的str3和str4在内存中只会存一份,所以常量字符串hello world只有一份,把h的地址存在str3中,把h的地址也存在str4中,str3中存的地址和str4中存的地址一样,所以str3等于str4。
下面我们来看另外一个知识点:
指针数组
存放指针的数组,它本质上是个数组,数组中存放的是地址(指针)。
int main()
{
//int *arr[3];//arr是存放3个整形指针的数组
int a=10;
int b=20;
int c=30;
int *arr[3]={&a,&b,&c};
int i=0;
for(i=0;i<3;i++)
{
printf("%d ",*(arr[i]);
}
return 0;
}
这样的写法可行,是没有什么错误的,但是不太好,没什么应用场景,我们也不经常这样使用指针数组,我们来看下面这种写法:
int main()
{
int a[5]={1,2,3,4,5};
int b[5]={2,3,4,5,6};
int c[5]={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));
printf("%d ", arr[i][j]);
//arr[i]==*(arr+i)
//arr[i][j]==*(arr[i]+j)
//[j]==*(+j)
}
printf("\\n");
}
return 0;
}
这个写法将a,b,c三个数组名放在了指针数组中,而数组名是首元素地址,拿到了首元素地址,我们就可以访问数组当中的元素,而这种写法是我们经常使用的。
下面我们试着来辨别以下声明是什么:
int *arr1[10];
arr先和[]结合,说明它是个数组,然后去掉arr1[10]发现数组的每个元素为int*类型。所以它是存放整形指针的数组
char *arr2[4];
存放一级字符指针的数组,arr先和[]结合,说明它是个数组,然后去掉arr2[4]发现数组的每个元素为char*类型。
char **arr3[10];
存放二级字符指针的数组,arr先和[]结合,说明它是个数组,然后去掉arr3[10]发现数组的每个元素为char**类型。
指针数组就讲到这里,下面我们来看数组指针。
数组指针
数组指针是数组还是指针呢?答案是指针。
整形指针是指向整形的指针
int a = 10;
int* pa = &a;
字符指针是指向字符的指针
char ch = 'w';
char* pc = &ch;
顾名思义,数组指针就是指向数组的指针
接下来我们看看是如何定义数组指针的呢?我们来看下面的代码:
int main()
{
double* d[5];//指针数组
double* (*pd)[5]= &d;//pd就是一个数组指针
int arr[10] = { 1,2,3,4,5 };
int (*parr)[10] = &arr;//取出的是数组的地址
//parr 就是一个数组指针 - 其实存放的是数组的地址
//arr;//arr-数组名是首元素的地址- arr[0]的地址
return 0;
}
我们在定义数组指针时,需要注意[]的优先级比*高,所以我们需要使用()强制先于*结合。
&数组名与数组名
我们首先看以下代码:
int main()
{
int arr[10]={0};
printf("%p\\n",arr);
printf("%p\\n",&arr);
return 0;
}
打印上面代码后发现他们两个值一样,但是这两个的意义是不一样的,&arr是整个数组的地址,arr是数组首元素地址
那么&arr和arr区别怎么体现呢?以下代码就体现了他们的区别:
int main()
{
int arr[10]={0};
int *p1=arr;
int (*p2)[10] = &arr;
printf("%p\\n",arr);
printf("%p\\n",&arr);
return 0;
}
我们用指针p1,p2存放这两个地址时,int *p1=arr;int (*p2)[10];
p1是个整形指针就可以了,而p2要是个数组指针,因为他存放的是&arr,是整个数组的地址。
既然他们的类型不一样,加减整数的步幅就不一样了
int main()
{
int arr[10]={0};
int *p1=arr;
int (*p2)[10] = &arr;
printf("%p\\n",p1);
printf("%p\\n",p1+1);
printf("%p\\n",p2);
printf("%p\\n",p2+1);
return 0;
}
我们能够发现p1加1的步幅是4个字节,p2加1的步幅却是40个字节
说明数组名和&数组名还是有差别的。
我们经常说数组名是数组首元素的地址
但是有两个例外:
1.sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小
2.&数组名 - 数组名表示整个数组,取出的是整个数组的地址
数组指针的使用
利用数组指针访问一维数组
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int *p=arr;//这个方便
int (*pa)[10]=&arr;//不方便
int i=0;
for(i=0;i<10;i++)
{
*((*pa)+i);//*pa拿到arr
}
return 0;
}
我们也可以通过数组指针来访问一维数组的指针,但是不方便,将问题复杂化了。我们完全可以利用整形指针和[]下标引用操作符来访问,所以我们一般一维数组不用数组指针,数组指针的使用一般用于二维指针,接下来我们看一下二维数组是如何访问的。
传统的方式访问二维数组
void print1(int arr[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 ",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}};
print1(arr,3,5);
print2(arr,3,5);//arr数组名,表示数组首元素的地址
return 0;
}
利用数组指针来访问二维数组
//p是一个数组指针---指向一维数组的指针
void print2(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}};
print2(arr,3,5);//arr数组名,表示数组首元素的地址
return 0;
}
我们需要注意:
二维数组的数组名表示首元素的地址
二维数组的首元素是:第一行!
这里我们将第一行的地址传过去了,需要用一个指向一维数组的指针来接收
printf("%d ",*(*(p+i)+j));
这句代码的解读:
p是一个指向一维数组的指针,他存的是整个数组的地址,p+i跳过的是整个数组,p+0指向二维数组第一行arr[0]的整个一维数组的地址&arr[0],p+1指向二维数组第二行arr[1]的整个一维数组的地址,p+2指向二维数组第三行arr[2]的整个一维数组的地址,对p+i解引用找到的是第i行的数组名,数组名就是首元素地址,*(p+i)+j拿到的是第i行第j个元素的地址,在对它解引用就拿到了元素。
下面我们来辨别几个声明:
int arr[5];//整形数组
int *parr1[10];//指针数组
int (*parr2)[10];//数组指针,数组的元素为int类型,10个元素
int (*parr3[10])[5];//指针数组,每个元素为数组指针
注释是答案,我们重点来看一下最后一个怎么辨别:
parr3首先和[]结合,说明它是个数组,将圈起来红色部分去掉,剩下的就是数组元素的类型,例如int arr[10];去掉arr[10],剩下的int就是数组元素类型
所以首先parr3是有10个元素的数组,数组的每一个元素又是数组指针。每个数组指针能够指向一个数组,数组5个元素,元素类型为int
数组参数、指针参数
一维数组传参
我们一维数组传参,函数参数类型可以写成什么呢?
int main()
{
int arr[10]={0};
int *arr2[20]={0};//arr2是存放整形指针的数组
test(arr);
}
首先我们知道一维数组的数组名是首元素的地址,我们传过去的是首元素的地址,是arr[0]的地址,我们可以用哪些类型的参数来接收呢?
void test(int arr[10])
{}
传过来的是数组,当然可以用数组接收
void test(int arr[])//参数部分写成数组,数组元素可省略
{}
传过来的是数组,当然可以用数组接收,参数部分写成数组,数组元素可省略,操作系统会自行计算
void test(int *arr)//参数部分写成指针
{}
参数部分写成指针,数组名是首元素的地址,当然可以用一个指针来接收
我们再来看看指针数组传参,函数参数类型该如何写?
int main()
{
int *arr2[20]={0};//arr2是存放整形指针的数组
test2(arr2);//数组名表示首元素地址,而首元素又是int*,所以用int**接收
}
我们依次来看以下的传参方式可行不可行:
void test2(int *arr[20])//指针数组
{}
可以,我们传过来的是指针数组,所以可以直接用指针数组来接收
void test2(int **arr)//数组名表示首元素地址,而首元素又是指针,所以要用一个二维指针来接收
{}
可以,我们知道数组名表示首元素地址,而数组里面存放的是指针,首元素是一个指针,所以我们可以用一个二维指针来接收
二维数组传参
我们二维数组传参,函数参数类型可以写成什么呢?
int main()
{
int arr[3][5]={0};
test(arr);//传过去的是首元素的地址,首元素是arr[0],是第一行一维数组的地址&arr[0]
return 0;
}
首先我们知道二维数组的数组名是首元素的地址,我们传过去的是首元素的地址,是第一行一维数组的地址&arr[0],我们可以用哪些类型的参数来接收呢?
我们来看以下的函数形参的类型是否可行:
void test(int arr[3][5])//可以,传过来的是二维数组,当然可以用二维数组接收
{}
可以,传过来的是二维数组,当然可以用二维数组接收
void test(int arr[][])//不行,不能省略列
{}
不行,二维数组不能省略列
void test(int arr[][5])//可以,可以省略行
{}
可以,二维数组可以省略行
void test(int *arr)//不可以,传过来的是一个一维数组的地址,要用数组指针来接收
{}
不可以,传过来的是一个一维数组的地址,要用数组指针来接收
void test(int* arr[5])//更不行,这是一个指针数组
{}
更不行,这是一个指针数组
void test(int (*arr)[5])//可以,指向一个5个元素的数组的指针
{}
可以,指向一个5个元素的数组的指针
一级指针传参
void print(int *ptr,int sz)//用一级指针接收
{
int i=0;
for(i=0;i<sz;i++)
{
printf("%d ",*(ptr+i));
}
}
int main()
{
int arr[10]={1,2,3,4,5,6,7,以上是关于指针的这些知识你知道吗?C语言超硬核指针进阶版3w+字详解+指针笔试题画图+文字详细讲解的主要内容,如果未能解决你的问题,请参考以下文章
自定义类型的这些知识你知道吗?C语言超硬核结构体枚举联合体画图+文字详细讲解