博客主页:https://blog.csdn.net/wkd_007
博客内容:嵌入式开发、Linux、C语言、C++、数据结构、音视频
本文内容:介绍C语言指针和数组的关系
金句分享:你不能选择最好的,但最好的会来选择你——泰戈尔
本文未经允许,不得转发!!!
指针和数组有什么关系呢?
C语言中,并没有明确规定指针和数组的关系,也就是说,指针和数组实际上并没有什么关系,只是它们的很多用法很相似,才令很多初学者感到困惑。
定义一个指针变量后,系统会分配一块内存(32位系统是4个字节,64位系统是8个字节)。然后不管这块内存在之后放了什么内容,编译器都会把他当成一个内存地址来处理。指针可以指向任何地方,但是不是任何地方你都能通过这个指针变量访问到。
数组的本质是一块连续内存区域,里面分成若干个相同大小的内存区域,这些小的内存块就是数组元素。数组的大小与元素的类型和个数有关。定义数组时必须指定其元素的类型和个数。数组可以存任何类型的数据,但不能存函数(但可以存函数指针)。
数组名就是让初学者混淆指针和数组的一个重要原因。
上篇文章 提到,数组的数组名其实可以看作一个指向数组首个元素的指针。既然是指针,我们就清楚它关于指针的4个方面内容:指针所在的地址、指针的值、指针的类型、指针所指向的类型。
printf
打印出来,会等于&a[0]
;数组元素类型*
;下面看例子2.1,看看是怎样混淆的:
// array_name.c
#include
int main()
{
int arr[5] = {1,2,3,4,5};
int *pa = arr; // pa指针指向 arr数组首个元素
printf("num1=%d num2=%d unm3=%d\n", *arr, *(arr+1), arr[2]);
printf("num1=%d num2=%d unm3=%d\n", *pa , *(pa+1) , pa[2]);
pa++;
//arr++; // 保存,数组名的值不能修改
}
pa指针指向 arr数组首个元素之后,下面使用了完全相同的用法,不去看定义的话,都分不清谁是指针,谁是数组。
那有哪些区别吗?数组名是常量指针,它不能作为左值使用,它的值无法被修改,所以不能使用arr++。
指针的形式访问内存,就是指利用指针加减运算来访问内存空间。
指针和数组都可以用指针的形式来访问内存空间,但不同的是,指针访问的是指针值所指向的内存空间,而数组访问的是自己的内存空间。
看例子3.1.1:
// array_access.c
#include
#include
int main()
{
int arr[5]; // 定义一个数组
int *pa = (int*)malloc(5*sizeof(int));// 定义一个指针指向malloc分配的内存
// 数组的指针形式访问:
int i=0;
for(i=0; i<sizeof(arr)/sizeof(*arr); i++)
{
*(arr+i) = i;
printf("*(arr+%d)=%d, ",i,*(arr+i));
}
printf("\n\n");
// 指针以指针形式访问:
for(i=0; i<5; i++)
{
*(pa+i) = i;
printf("*(pa+%d)=%d, ",i,*(pa+i));
}
printf("\n");
free(pa);
return 0;
}
例子中,数组arr
利用数组名是数组首元素地址的特性,使用指针运算完成了对数组内容的修改和读取。
指针pa
定义时指向了malloc函数分配的内存,内存大小5*sizeof(int)
,然后使用使用指针运算完成了对该段内存空间的读写。
我们初学C语言时,就知道数组允许以下标的形式来表示数组元素,arr[0]
表示首个数组元素,arr[0]
表示第二个数组元素,其他依此类推。但有点惊讶的是,指针也可以使用下标的形式访问内存空间。
看例子3.2.1:
// array_access2.c
#include
#include
int main()
{
int arr[5]; // 定义一个数组
int *pa = (int*)malloc(5*sizeof(int));// 定义一个指针指向malloc分配的内存
// 数组以下标形式访问:
int i=0;
for(i=0; i<sizeof(arr)/sizeof(*arr); i++)
{
arr[i] = i;
printf("arr[%d]=%d, ",i,arr[i]);
}
printf("\n\n");
// 指针以下标形式访问:
for(i=0; i<5; i++)
{
pa[i] = i;
printf("pa[%d]=%d, ",i,pa[i]);
}
printf("\n");
free(pa);
return 0;
}
实际上,编译器总是把以下标的形式的操作解析为以指针的形式的操作。
pa[i]
这个操作会被解析成:先取出 pa 里存储的地址值,然后加上中括号中 i
个元素的偏移量,计算出新的地址,然后从新的地址中取出值。也就是说以下标的形式访问在本质上与以指针的形式访问没有区别,只是写法上不同罢了。
首先,复习一下,定义会分配内存的,而声明没有。定义只能出现一次,而声明可以出现多次。
前面说过,数组名可以看出数组首元素的地址,那么,在文件1定义数组
char a[100];
,在文件2声明为指针extern char *a;
,这样的做法可以吗?
答案是不行的,因为数组和指针有着本质的区别,这里定义数组时分配了一块100个字节内存,并命名为a;而声明为extern char *a
时,编译器理所当然的认为 a 是一个指针变量,在 32 位系统下,占 4 个 byte。这 4 个 byte 里保存了一个地址,这个地址上存的是字符类型数据。
总结:定义为数组,不能将该数组名声明为指针,但可以在声明数组时,去掉第一维的数组长度,因为声明不分配内存,只要求类型匹配。
定义为指针,声明为数组也是错误的。因为类型不匹配。
看例子4.1:
// array_extern1.c
char a[100];
char *p = "abcdefg";
int ai[10][100];
// array_extern2.c
#include
//extern char *a; // 错误
extern char a[];
// extern char p[]; // 错误
extern char *p;
// extern int ai[][]; //error: array type has incomplete element type(数组类型具有不完整的元素类型)
extern int ai[][100];
int main()
{
printf("%p %p %p\n",a, p, ai);
return 0;
}
指针数组:首先它是一个数组
,数组的元素都是指针,数组占多少个字节由数组本身决定。它是储存指针的数组
的简称。
数组指针:首先它是一个指针
,它指向一个数组。在 32 位系统下永远是占 4 个字节,至于它指向的数组占多少字节,不知道。它是指向数组的指针
的简称。
定义一个指针数组p1,根据右左法则,p1先跟
[]
结合,是个数组,然后再跟*
结合,说明数组每个元素都是指针:int *p1[10];
定义一个数组指针p2,*p2 被括号括起来,改变优先级,p2先与
*
结合,说明p2是个指针,然后再和[]
结合,说明p2指向一个数组,数组元素是int类型的,有10个数组元素。int (*p2)[10];
p2的内存布局如下:
本文介绍指针和数组的联系和区别:指针和数组名、指针和数组访问方式、指针和数组的定义和声明、指针数组和数组指针。
如果文章有帮助的话,点赞、收藏⭐,支持一波,谢谢