数组与指针详解
来源:互联网 发布:宏观经济基础数据库 编辑:程序博客网 时间:2024/05/19 02:30
一、数组与指针的基本关系
C语言的初学者,对数组和指针的关系含混不清,其实一句话就可以道出两者的关系——“在C语言中, 只有指针算术和数组下标运算是等价的,指针和数组是不同的”。
很多人认为char a[]与char *a是等价的,也许他们看到的是在函数的形参中char a[]与char *a可以相互替换,而误以为两者等价,实则完全不然。
例如:若char a[]与char *a等价,下面的例子执行的时候就不会出错。
文件1:test_array_point.c
1 #include <stdio.h>
2 char a[30]="test array and point";
3 extern void test_func();
4 int main(int argc, char **argv){
5 test_func();
6 printf("test!\n");
7 return 0;
8 }
文件2:test_extern_array_by_point.c
1 #include <stdio.h>
2
3 extern char *a;
4
5 void test_func(){
6 printf("test_func a=%s\n",a);
7}
执行结果:Segmentationfault (core dumped)
出现了段错误,但是“extern char*a;”改为“extern char a[];”,程序就能正确执行,由此简单实例看来数组和指针不是等价的。
再看一下数组和指针在声明定义时的区别:
char a[6]=”hello”;和char *p=”world”;
数组定义char a[6]请求系统在内存中创建6个字节的空间,并用名字a表示。而指针声明char*p请求一个位置放置一个指针,并用名称p表示,这里的数组和指针初始化后的逻辑如下图:
接下来再看一下数组和指针在访问时的区别:
以第四个位置元素为例,也即a[3]和p[3],当编译器看到表达式a[3]的时候,它生成的代码从a的位置开始向后跳过3个,然后取出那个字符;若编译器看到p[3]时,它生成的代码找到p的位置,取出其中的指针值,在指针值上加3,然后取出指向的字符。数组和指针的本质区别在于类似a的数组和类似p的指针一旦在表达式中出现就会按照不同的方法计算,不论它们是否有下标。
还是回到一开始的那个问题:char a[]与char *a是等价的,为什么很多人都认为它们是等价的呢?一定有相似点的存在才会有这样的理解。
是的,在函数的形参中他们是相同的,例如:
void f(char a[]){
……
}
和
void f(char *a){
……
}
是一样的。这是编译器做的小动作的原因,数组在传入函数时,函数接收到的不是数组本身,而是转化后的指针,指针类型与数组名的类型相同。建议,在形参里用指针形式,不要用数组形式。即使用了数组形式,编译器最终也会将其转换为指针形式,这样做虽然形式上能让人更好理解——传入函数的是数组,但是在理解上却加了一层枷锁,所以我建议形参中用指针的形式。
在此说一个众所周知的点——数组名是常量,不能被赋值。
例如:
int a[6]={ 0, 1, 2, 3, 4, 5};
a++;
a=a+1;
都是错误的,编译器不允许。
二、数组的指针
数组的指针是一个抽象的概念,需细琢,看几个例证:
1、 一维数组:
int array[2] = {0,1};
由此数组我们可以分解出array的元素(array[0]/array[1])、数组名array、以及数组的指针&array,看下图:
例:
1 #include <stdio.h>
2
3 int main(int argc, char **argv){
4
5 int n_array[5]={0, 1, 2, 3, 4};
6 int i;
7 int *p=NULL;
8 for(i=0;i<5;i++){
9 printf("n_array[%d]=%d\n", i,n_array[i]);
10 }
11 p=n_array;
12 if((n_array+1)==&n_array[1]){
13 printf("size of n_array= %lu, sizeof p = %lu\n",sizeof(n_array),sizeof(p));
14 }else{
15 printf("(n_array+1)!=&n_array[1]\n");
16 }
17 if((&n_array+1)!=&&n_array[1]){//此处只能验证&n_array所指对象的大小不为sizeof(n_array[1]),在二维数组中验证
18 printf("&n_array !=&n_array[1]\n");
19 }
20 return 0;
21 }
执行结果:
n_array[0]=0
n_array[1]=1
n_array[2]=2
n_array[3]=3
n_array[4]=4
size of n_array = 20, sizeof p = 8
&n_array !=&n_array[1]
2、 二维数组:
Int array1[2][2] ={{0,1},{2,3}};
由此数组我们可以分解出array的元素(array[0][0]/array[0][1]/array[1][0]/ array[1][1])、降维后的数组array[0]和array[1]、降维后的数组的指针array、二维数组的指针&array,看下图:
例:
1 #include <stdio.h>
2
3 int main(int argc, char **argv){
4 int array_2D[2][2]={ { 0, 1}, { 2,3}};
5 int *p=NULL;
6 int **pp = NULL;
7 int ***ppp = NULL;
8 int i, j;
9 for(i=0;i<2;i++){
10 for(j=0;j<2;j++){
11 printf("p[%d][%d]=%d\n", i, j, array_2D[i][j]);
12 }
13 }
14
15 if((array_2D[0]+1) == &array_2D[0][1]){
16 printf("array[0] isnormal array{0, 1}'s first addr\n");
17 }else{
18 printf("(array[0]+1) !=&array[0][1]\n");
19 }
20 if((array_2D+1)==&array_2D[1]){
21 printf("array is normalarray{0, 1}'s array point\n");
22 }else{
23 printf("(array+1)!=&array[1]\n");
24 }
25 if((&array_2D+1)!=&array_2D[1]){
26 printf("(&array+1)!=&array[1]\n");
27 }
28 return 0;
29 }
执行结果:
p[0][0]=0
p[0][1]=1
p[1][0]=2
p[1][1]=3
array[0] is normal array{0, 1}'s first addr
array is normal array{0, 1}'s array point
(&array+1)!=&array[1]
这里简单介绍下指针数组:
指针数组:数组元素为指针类型。
三、 动态数组分配
还是那句话——在C语言中,只有指针算术和数组下标运算是等价的,指针和数组是不同的。指针运算和数组下标运算是等价的,根据这句话,就可以用malloc分配的内存来高效地模拟数组。
例如:
#include <stdlib.h>
int *array = (int*)malloc(10*sizeof(int));
当malloc申请内存成功以后,就可以像使用数组一样的使用array[i]。唯一的区别是sizeof不能给出分配空间的大小。
上面仅模拟了一维数组的动态分配,那多维数组如何动态分配呢?看几个例子:
例1:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #define ARRAY_ROWS_MAX 10
5 #define ARRAY_COLUMNS_MAX 8
6
7 int main(int argc, char **argv){
8 int i,j;
9 int **parray= (int **)malloc(ARRAY_ROWS_MAX*sizeof(int *));
10
11 if(parray){
12
13 for(i=0;i<ARRAY_ROWS_MAX;i++){
14 parray[i] = (int *)malloc(ARRAY_COLUMNS_MAX*sizeof(int));
15 if(!parray){
16 for(j=i-1;j>=0;j--)
17 free(parray[j]);
18 }
19 }
20 }else{
21 printf("alloc **parrayerror!\n");
22 return -1;
23 }
24 for(i=0;i<ARRAY_ROWS_MAX;i++){
25 for(j=0;j<ARRAY_COLUMNS_MAX;j++){
26 parray[i][j] = 1;
27 }
28 }
29
30 for(i=0;i<ARRAY_ROWS_MAX;i++){
31 for(j=0;j<ARRAY_COLUMNS_MAX;j++){
32 printf("parray[%d][%d]=%d\n", i, j, parray[i][j]);
33 }
34 }
35 return 0;
36 }
执行结果:全为1
也可以让数组的内容连续,但在后来重新分配行的时候有些阻力,得使用二维数组的相关指针运算:
例2:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #define ARRAY_ROWS_MAX 10
5 #define ARRAY_COLUMNS_MAX 8
6
7 int main(int argc, char **argv){
8
9 int**parray1 = (int **)malloc(ARRAY_ROWS_MAX*sizeof(int *));
10 int i, j;
11 if(parray1){
12 parray1[0] = (int *)malloc(ARRAY_ROWS_MAX*ARRAY_COLUMNS_MAX*sizeof(int));
13 if(!parray1[0])
14 free(parray1);
15 for(i=0;i<ARRAY_ROWS_MAX*ARRAY_COLUMNS_MAX;i++){
16 parray1[i] =parray1[0]+i*ARRAY_COLUMNS_MAX;
17 }
18 }else{
19 printf("alloc parry1error\n");
20 }
21
22 for(i=0;i<ARRAY_ROWS_MAX;i++){
23 for(j=0;j<ARRAY_COLUMNS_MAX;j++){
24 parray1[i][j]=1;
25 }
26 }
27
28 for(i=0;i<ARRAY_ROWS_MAX;i++){
29 for(j=0;j<ARRAY_COLUMNS_MAX;j++){
30 printf("parray1[%d][%d] = %d\n", i, j, parray1[i][j]);
31 }
32 }
33 return 0;
34 }
执行结果:全为1
以上两种情况,都可以用arrayx[i][j]来访问模拟的数组元素,其中他们的内存布局如下:
上面的两种分配方式对内存有两次间接访问,假如对程序执行时间要求非常高的系统,还可以用一个动态分配的一维数组来模拟二维数组,但是需要手动计算下标:
例3:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #define ARRAY_ROWS_MAX 10
5 #define ARRAY_COLUMNS_MAX 8
6
7 int main(int argc, char **argv){
8 int i, j;
9 int *parray2 = (int*)malloc(ARRAY_ROWS_MAX*ARRAY_COLUMNS_MAX*sizeof(int));
10
11 if(!parray2){
12 printf("alloc parray2error!\n");
13 return -1;
14 }
15 for(i=0;i<ARRAY_ROWS_MAX;i++){
16 for(j=0;j<ARRAY_COLUMNS_MAX;j++){
17 parray2[i*ARRAY_COLUMNS_MAX+j] = 1;
18 }
19 }
20
21 for(i=0;i<ARRAY_ROWS_MAX;i++){
22 for(j=0;j<ARRAY_COLUMNS_MAX;j++){
23 printf("parray[%d][%d] = %d\n", i, j,parray2[i*ARRAY_COLUMNS_MAX+j]);
24 }
25 }
26 return 0;
27 }
执行结果:全为1
还可以用一维数组指针来实现:
例4:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #define ARRAY_ROWS_MAX 10
5 #define ARRAY_COLUMNS_MAX 8
6
7 int main(int argc, char **argv){
8
9 int i, j;
10 int (*array)[ARRAY_COLUMNS_MAX]= (int (*)[ARRAY_COLUMNS_MAX])malloc(ARRAY_ROWS_MAX*sizeof(*array));
11
12 if(!array){
13 printf("alloc arrayerror!\n");
14 }
15
16 for(i = 0; i<ARRAY_ROWS_MAX; i++){
17 for(j=0;j<ARRAY_COLUMNS_MAX; j++){
18 array[i][j] = 1;
19 }
20 }
21
22 for(i = 0; i<ARRAY_ROWS_MAX; i++){
23 for(j=0; j<ARRAY_COLUMNS_MAX;j++){
24 printf("array[%d][%d]= %d\n", i, j, array[i][j]);
25 }
26 }
27 return 0;
28 }
执行结果:全为1
最后,也可以用二维数组指针来实现:
例5:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #define ARRAY_ROWS_MAX 10
5 #define ARRAY_COLUMNS_MAX 8
6
7 int main(int argc, char **argv){
8 int i, j;
9 int(*array2)[ARRAY_ROWS_MAX][ARRAY_COLUMNS_MAX] =(int (*)[ARRAY_ROWS_MAX][ARRAY_COLUMNS_MAX])malloc(sizeof(*array2));
10
11 if(!array2){
12 printf("alloc array2error!\n");
13 }
14 int *p = (int*)array2;
15 for(i=0;i<ARRAY_ROWS_MAX;i++){
16 for(j=0;j<ARRAY_COLUMNS_MAX;j++){
17 p[i*ARRAY_COLUMNS_MAX+j] = 1;
18 }
19 }
20
21
22 for(i=0;i<ARRAY_ROWS_MAX;i++){
23 for(j=0;j<ARRAY_COLUMNS_MAX;j++){
24 printf("array2[%d][%d]=%d\n", i, j, p[i*ARRAY_COLUMNS_MAX+j]);
25 }
26 }
27 return 0;
28 }
执行结果:全为1
所有以上技术都可以扩展到3维或更高维数组,会更加复杂!
四、函数和多维数组
在“数组与指针的基本关系”我们提到“数组在传入函数时,函数接收到的不是数组本身,而是转化后的指针”,这个转化不能递归应用,二维数组被编译器转化为数组的指针,而不是指针的指针。如果向函数传递二维数组,函数的声明必须匹配,原因是数组的维度总是在编译时确定的常量,不能通过函数的其他参数来确定。
例:
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 void func(int **test){
5 int i, j;
6 for(i=0;i<2;i++){
7 for(j=0;j<2;j++){
8 printf("test[%d][%d]=%d\n", i, j, test[i][j]);
9 }
10 }
11 }
12
13 int main(int argc, char **argv){
14
15 int array[2][2] = {{0,1},{2,3}};
16 func(array);
17 return 0;
18 }
编译会出warning:
当执行可执行文件汇报:Segmentationfault (core dumped),出现段错误了!
将voidfunc(int **test)改为void func(int (*test)[2])或者void func(int a[][2])就OK。
五、数组的大小
char a[5] = {0, 1, 2,3, 4, 5};
int i =sizeof(a);
i的值为5,也即数组的字节长度为5。
但是在函数中呢?
void func(char a[5]){
int i = sizeof(a);
printf(“%d\n”,i);
}
此时打印出的i的值为4,也就是指针的大小,为什么不是数组的字节长度呢?原因见“数组与指针的基本关系”。
还有怎么判断数组中有多少个元素,方法如下:
int array[] ={1,2,3};
int i =sizeof(array)/sizeof(array[0]);
更复杂的结构数组的元素个数也是这样计算的。
结束!
- 数组与指针详解
- 详解指针与数组
- 指针数组与数组指针详解
- 指针数组与数组指针详解
- 指针数组与数组指针详解
- 数组,函数与指针 详解
- C++数组与指针详解
- C语言指针详解-----数组与指针
- 数组详解,以及数组与指针,函数
- C/C++数组指针与指针数组详解
- 多维数组与指针运用详解
- C语言数组与指针详解
- C语言数组与指针详解
- C语言数组与指针详解
- C语言数组与指针详解 .
- C语言数组与指针详解
- C语言数组与指针详解
- C语言数组与指针详解
- 解决Xcode 7 无法推送 或者 打印错误 "remote-notification"
- 程序员常去的14个顶级开发社区
- 正则表达式30分钟入门教程
- setsockopt详解
- 属性 iOS笔记
- 数组与指针详解
- 鲁大师检测硬盘提示有问题,如下图,是怎么回事?硬盘坏了吗
- 关于PAT1004和1005的看法
- 认识标签
- XMPP框架 微信项目开发之XMPP框架目录作用详解——以及登录的Demo分析
- Codeforces 547B Mike and Feet
- SSIS初识
- ECMall模板解析语法与机制
- php操作Excel文件,将Excel中数据导入到数据库