你真的了解指针吗,在C语言中指针具有非常大的作用,这篇文章会先带你了解指针数组和数组指针的区别以及数组指针的使用。
目录
一、字符指针
在指针类型中,有一种指向字符型的字符指针:char*
int main ()
{
const char* p="hello,world";
printf ("%s",p);
return 0;
}
这段代码中,p指针指向了hello,world这串字符串。接下来我们来分析下这段代码。
首先,编译器分配了一片内存,放置了hello,world这串字符,然后我们用p指针指向了这串字符串的第一个字符,然后我们将p以%s的形式打印,这样就打印了这串字符串。
特别注意:p只是存放了h字符的地址,并没有存放整个字符串的地址,更没有存放整个字符串。
(这里的const修饰的是char*p ,表示p的指向不能被修改)
接下来我们来进一步熟悉字符指针的指向
int main()
{
char str1[] = "hello";
char str2[] = "hello";
const char *str3 = "hello";
const char *str4 = "hello";
if(str1 ==str2)
printf("str1 == str2\n");
else
printf("str1 != str2\n");
if(str3 ==str4)
printf("str3 == str4\n");
else
printf("str3 !=str4\n");
return 0;
}
答案输出:
str1 != str2
str3 == str4
这里我们来分析一下,str1和str2都是数组,我们在创建数组的时候内存会分别开辟两块空间给str1和str2,而数组名代表首元素的地址,所以两个数组的首元素不相同。
而str3和str4是指针,指向常量字符串hello,因为他们指向的是同一个字符h,他们存储的地址相同(都是字符h的地址),所以他们相等。常量字符串是在内存分区中的常量区,两个指针指向的区中的同一块地址,不会因为str3和str4的指向他们,内存就会创建两串hello字符串。
二、指针数组
我们知道
char arr[10] //存放char型数据的数组
int arr[10] //存放int型数据的数组
那我们可以想,有没有存放指针类型的数组呢?
有的,而且存放的指针还可以是char*型、int*型、float*型
char * arr1[10] //存放10个char*型指针数据的数组
int * arr2[10] //存放10个int*指针数据的数组
int ** arr3[10] //存放10个二级int型指针的数组
这里我们可以发现,arr会与[]结合,就表示是一个数组,然后arr前面就是数组中数据的类型。
我们还可以知道,arr会先与[]结合,[]的优先级比*的优先级高,这便在我们后面创建数组指针埋下伏笔。
三、数组指针
看到这可能有些人已经懵了,我们可以观察这些名词的最后两个字,如果是指针数组,就表明是数组,是数组指针,就表明这是一个指针.
我们来分别写一个指针数组和一个数组指针
int * p [10] //指针数组——存放指针的数组
int (*p)[10] //数组指针——指向数组的指针
这里我们发现,这两条代码只有一个括号之差,但是表达的意义却是天差地别,因为优先级的问题,导致了他们表达的意义十分不同,接下来我画一个图来剖析他们之间的不同.
这里很关键的一个点就是p会先和[]结合。所以我们要用括号来将p括起来,让p是一个指针。
第二个点是数组指针中的【10】,这个10不能省略,因为表示这个指针指向的数组中有几个元素,可以理解为,这个指针+1,跳过多少个数组元素,不能省略。
数组指针的使用
我们先来看一下arr和&arr的区别
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
打印结果:
我们可以得出一个结论,数组名表示首元素的地址,而&arr表示整个数组的地址,arr+1跳过一个元素,而&arr+1则跳过一个数组,跳过一个有10个int型的数组。(以上打印地址是以16进制表示的。
数组指针的使用:
我们可以使用数组指针指向一个数组,然后使用数组指针进行打印。
我们来展示一下使用数组指针打印一个一维数组。
#include <stdio.h>
void print (int (*p)[3],int x)
{
int i=0;
for (i=0;i<x;i++)
printf ("%d",*(*p+i));
}
int main ()
{
int arr[8]={1,2,3,4,5,6,7,8};
int sz=sizeof(arr)/sizeof(arr[0]);
print(&arr,sz);
return 0;
}
我们可以分析一下这段代码,①我们将整个数组的地址传入到print函数中,然后用一个指向数组的指针——指针数组来接收。②打印的目标是*(*p+i),首先,int (*p)[8]=&arr,所以p中存放的是arr整个数组的地址,因为p=&arr,所以(*p) == arr,而数组名等于首元素的地址,我们再对p进行+i,等于arr首元素向后跳过i个元素,再解引用(*),就拿出了arr+i处的元素数据,即数组第i个存的数据,这两个星号是必不可少的。
当然可能会感觉很别扭,打印一维数组可以直接用arr【i】的方式来打印,为什么要用数组指针呢,因为数组指针多用于二维数组中,使用数组指针则会方便很多,接下来我们来看看使用如何数组指针来打印二维数组。
void print(int(*p)[5], int x,int y)
{
int i = 0;
int j = 0;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
printf("%2d ", *(*(p + i) + j));
if (j == 4)printf("\n");
}
for (j = 0; j < y; j++)
{
printf("%2d ", p[i][j]);
if (j == 4)printf("\n");
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
print(arr, 3, 5);
return 0;
}
这段代码需要注意的点:①我们是将arr传入函数,因为二维数组的数组名表示二维数组第一行的地址。这点需要特别注意,传过来的是一行有5个整形元素的一个数组的地址,我们可以在看一维数组的首元素地址时arr=&arr[0],则二维数组则是arr=&arr[0],而这时二维数组的arr[0]表示第一行,arr[0][0]才是第一个元素。②p+i,表示跳过一行,*(p+i)表示找到了第i行首元素的地址,再对行解引用则找到了第j列的元素,arr[i][j]的元素内容就这样被找了出来。
这里我用了两种方式打印,*(*(p + i) + j)和p[i][j]的打印方式相同,实质上以p[i][j]的形式打印最终还是会转化为*(*(p + i) + j)的形式打印的。
好了,我们在了解了数组指针和指针数组后我们来分析几条代码
int arr[5]
很简单,是一个存放整形的一维数组
int *parr1[10]
这里parr1先与[10]结合,是一个一维数组,然后存放的是int*型的数据。所以这是一个存放整形指针的一维数组。
int (*parr2)[10]
这里parr2与*号结合,是一个指针,然后指针指向的是int [10],所以parr2指向一个数组,这个数组的元素是int型,一行数组中有10个元素,所以这是一个指向有10个int型数据的数组的指针。
int (*parr3[10])[5]
首先,parr3与[10]结合,表示parr3数组中有10个数组。然后数组中的元素类型是int (*)[5],而int (*)[5]是数组指针类型,所以这是一个存放数组指针的数组,其中数组指针可以指向一行有5个int型数据的指针。
这篇文章的分享就到这了,希望能加深大家对指针的了解,还有更多关于指针的知识我会在以后分享出来,如果感觉还不错的hxd可以给点个赞呗~