目录
通过指针详解(一)我们已经了解到:
- 指针变量存储的是地址,每个地址唯一标识一个内存空间
- 指针的大小为4/8个字节
- 指针是有不同类型的,类型决定了指针+-整数的步长以及指针解引用所访问的字节大小
- 指针的运算
- 二级指针的指向是一级指针的地址
现在开始新的知识归纳
字符指针
指针类型有char*类型,这种类型就叫做字符指针
字符指针的一般使用方法:
#include<stdio.h>
int main()
{
char m="c";
char *p=&m;
return 0;
}
这里就是将字符变量m的地址赋予指针变量p;
字符指针的另一种使用方法:
#include<stdio.h>
int main()
{
char m="csdn";
char *p=&m;
printf("%s",m);
printf("%c",*p);//这里打印的是“c”
printf("%s",p);//这里打印的才是“csdn”
return 0;
}
这里字符串的地址就是字符串首元素的地址,即c的地址,所以对指针p解引用并以字符的形式打印时,这里只能打印出首元素c;而指针变量p的值其实就是字符串首元素的地址,我们知道要打印一串字符串,只要给出字符串的首地址即可,所以我们只要提供指针变量p然后通过字符串的形式打印即可打印出csdn;
注意:这里的csdn其实就是一个常量字符串
给出一道题目以便我们分清楚字符串、字符串数组和字符指针
#include<stdio.h>
int main()
{
char str1="best csdn";
char str2="best csdn";
char str3[] = "hello csdn.";
char str4[] = "hello csdn.";
char*p1="hello csdn";
char*p2="hello csdn";
if(str1==str2)
printf("&str1=&str2\n");
else
printf("&str1!=&str2\n");//结果是&str1=&str2
if (str3 == str4)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");//结果是str1 and str2 are not same
if(p1==p2)
printf("p1=p2");
else
printf("p1!=p2");//结果是p1=p2
return 0;
}
可以看到用一个字符串去赋给两个不同的变量,两个变量的地址是相同的,因为两个变量的地址都是首元素b的地址;
而给两个字符串数组赋予同一个字符串时,两个数组的首地址并不相同,因为两个数组分别开辟了两个内存空间,里面的字符串虽然相同,但是所处的内存空间是不同的;
而两个指针变量都是指向同一个字符串的首元素地址,所以它们的值是相同的。
指针数组
指针数组,本质上还是数组,只不过数组里面存放的都是指针
int* arr[5];
arr里面5个元素类型都是int*
以此类推
int**arr1[10];
arr1里面10个元素都是二级指针
指针数组如何分辨数组中元素类型:
- 我们要知道[]的优先级比*高
- 除去数组的变量名,剩下的就是变量类型
比如:
int arr[10] = { 0 };//除去变量名arr,变量类型为int [10]
int arr[3][4] = { 0 };//除去变量名arr,变量类型为int [3][4]
int* arr[10] = { 0 };//除去变量名arr,变量类型为int* [10]
数组的变量类型决定数组的元素个数以及每个元素的类型
数组指针
我们已经了解了有指向整型的指针和有指向字符的指针,同时指针也可以指向数组;
数组指针,主语是指针,是指向数组的指针。
#include<stdio.h>
int main()
{
int a=1;
char b=0;
char arr[10]={0};
int *p1=&a;
char *p2=&b;
char *p3=&arr;
return 0;
}
和基础的指针定义一样,我们只需要将数组的地址赋给指针变量即可。
如何正确写出能指向数组的指针
我们要知道一个指针变量除去变量名和一个符号*,剩下的就是该指针所指向的类型
比如:
int a=10;
int *p1=&a;//指针p1去掉变量名p1和一个*,剩下的int就是p1指向的类型
char b=0;
char*p2=&b;//指针p2去掉变量名p1和一个*,剩下的int就是p1指向的类型
数组指针也是如此:
#include<stdio.h>
int main()
{
int *arr[10]={0};
int (*p)[10]=&arr;
return 0;
}
首先p是一个指针,因为用()将*和p结合,防止[]的优先级比*高而导致p与[]先结合
指针p指向的内容,即数组类型是int [10],所以数组指针p就变成了int(*p)[10]。
去掉变量名p和*后,便是该数组指针所指向的数组的变量类型,即为int [10]。
注意如何分辨是指针数组还是数组指针:
因为[]的优先级比*高,而()的优先级又比[]高,所以如果变量名与*没有用()括起来,并且变量名与[]相接触的就是指针数组。
而如果加上()后*与p结合则为数组指针
数组指针的使用
此处用一个打印二维数组的案例来分析数组指针应该如何使用
#include<stdio.h>
void print(int(*p)[5],int x,int y)
{
int i=0;
for(i=0;i<x;i++)
{
int j=0;
for(j=0;j<y;j++)
{
printf("%d",*(*(p+i)+j));
}
printf("\n"};
}
}
int main()
{
int arr[4][5]={{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15},{16,17,18,19,20}};
print(arr,4,5);
return 0;
}
这里打印一个4行5列的二维数组,主函数中arr传参传的是数组的首元素地址,也是一维数组的首元素地址,要接收一个数组的地址,这里就要是要数组指针*p来接收,用x,y明确打印的范围
解析*(*(p+1)+1)
数组名和&数组名
数组名代表整个数组的地址的情况其实只有两种:
- &数组名。
- 数组名单独放在sizeof内部,即sizeof(数组名)。
除此之外,所有的数组名都是数组首元素地址。
比如
int arr[5]={1,2,3,4,5};
只有以下两种情况才代表整个地址:
&arr;
sizeof(arr);
除此之外都是首元素的地址,即1的地址。
打印出arr即首元素的地址与&arr虽然值是一样的,但是两者的类型不同;
&arr的类型是数组指针类型
而我们在指针详解(一)中讲过指针类型决定了它+-整数所走的步长
当arr+1后地址增加了四个字节,因为arr的类型是int,int类型所占的字节便为4
&arr[0]的情况和arr是一样的,&arr[0]就是arr中的第一个元素,是int类型,+1也是加4个字节;
而&arr就不同了,地址14到3C差了0x28总共40个字节(这里是按十六进制计算的),这里+1跳过了整个数组,而不是跳过一个元素