c语言数组名和指针的区别&&多维数组
之前就知道数组名和指针不同,因为运算符sizeof对数组名和指向数组的指针的运算结果是不同的—sizeof数组名得到的是数组空间的大小,而sizeof指向数组的指针获得的是指针自身占据空间大小。而如果是对指针进行解引用,即对象是指针指向的,那么sizeof得到的是这个对象的大小。
举例:
char aszFoo_Name[4];
char *pcFoo_Name = aszFoo_Name;
sizeof(aszFoo_Name) = 4;
sizeof(pcFoo_Name) = 8;
sizeof(*pcFoo_Name) = 1;
今天恰好看到关于linux(注意可移植性)里面对于0数量成员的数组的一些解释,举例:
typedef struct tagFoo_xxx
{
char cT1;
char acT2[0];
}Foo_xxx;
acT2就是我说的这种成员数量为0的数组,sizeof(Foo_xxx)得到的大小是1,可以验证acT2这种数组是不占空间的,引用别人博客的一段话:
char a[1]里面的a和char *b的b相同吗?《 Programming Abstractions in C》(Roberts,
E. S.,机械工业出版社,2004.6)82页里面说:“arr is defined to be identical to
&arr[0]”。也就是说,char a[1]里面的a实际是一个常量,等于&a[0]。而char *b是有一个实实在在的指针变量b存在。
所以,a=b是不允许的,而b=a是允许的。
上面这种东西的用法大致如下,也是从别人的博客搞来的:
#include <stdlib.h>
#include <string.h>
struct line {
int length;
char contents[0]; // C99的玩法是:char contents[]; 没有指定数组长度
};
int main(){
int this_length=10;
struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
thisline->length = this_length;
memset(thisline->contents, 'a', this_length);
return 0;
}
上面这段代码的意思是:我想分配一个不定长的数组,于是我有一个结构体,其中有两个成员,一个是
length,代表数组的长度,一个是contents,代码数组的内容。后面代码里的this_length(长度是10)代表是我想分配的数据的长度。(这看上去是不是像一个C++的类?)这种玩法英文叫:FlexibleArray,中文翻译叫:柔性数组。
需要注意的是后面根的缓冲区不能自己释放,所以要手动释放,防止内存泄露。。。
我感觉可以结合一下理解下数组名是什么东西。
而且对数组名取地址和数组名的值是相同的,那么 **数组名指向的是首个元素的地址,数组名取地址指向的是数组的地址**,所以这两者的值是相同的。结合数组指针来理解一下:
char acTest[3]={1,2,3};
char (*pacTest1)[3]=&acTest;
char *pacTest2= acTest;
printf("%d",acTest);
printf("%d",&acTest);
printf("%d",*acTest);
printf("%d",*(&acTest));
数组指针pacTest1是指向数组的一个地址,所以将数组名的取地址的值赋给它,同样char指针代表指向一个char的地址,这里用数组名赋值。而四个printf的输出结果,一二四是相同的,都是同一地址,而第三输出为1.这样就可以佐证上面的说法是正确的。
那么二维数组又是怎样的呢:
char acTest[2][3]={1,2,3,4,5,6};
char (*pacTest1)[3]=acTest;
char (*pacTest2)[2][3]=&acTest;
printf("%d",acTest);
printf("%d",&acTest);
printf("%d",*acTest);
printf("%d",*(&acTest));
这次四个表达式的值是相同的。二维数组可分解为多个一维数组,即以一维数组为成员,如二维数组acTest可分解为拥有三个成员:acTest[0],acTest[1],acTest[2]的数组,所以数组名直接指向的是其成员:一维数组,而对其取地址得到的才是二维数组地址,这和上面的结论相同。那么四个值相同,acTest指向一维数组acTest[0],其为一维数组的地址。&acTest指向二维数组本身。acTest即acTest[0],也就是第一个一维数组的第一个成员的地址。最后一个与第一个相同。
**数组名取地址的类型是一个数组指针类型,但它不是数组指针;而数组名的类型为这个数组的成员的类型的指针的类型,它同样不是指针**(好绕。。)因为指针的sizeof都是指针自身空间的大小~~~这个也可以结合数组下标访问的语法糖来理解,a[i]=(a+i),a[i][j]=(a+i)[j]=(*(a+i)+j)所以a也必定是指向其成员的指针的类型。这里gdb里看一下对应的类型:
char a[2][3]={1,2,3,4,5,6};
(gdb) ptype &&a
A syntax error in expression,near '&&a'.
(gdb) ptype &a
char (*)[2][3]
(gdb) ptype a
char [2][3]
(gdb) ptype &a[0]
char (*)[3]
(gdb) ptype &a[0][0]
char *
&a是二维数组指针类型,a是一维数组指针类型,&a[0]是一维数组指针类型,&a[0][0]是指向基本数组元素的指针类型。还有一点,在第一行取值的时候,对数组名取两次地址,显示原型错误,这应该是代表数组名取两次地址没有意义,所以不给打印。假设有一个n维数组,a[b1][b2]…[bn],其中bi为第i维数组的长度(i=1,2,…n),那么元素a[j1][j2]…[jn]的数值为一递归函数:
f(n)=*(f(n-1)+jn)
其中f(0)=a,n>=1
或者另一中方式, a[j1][j2]…[jn]的位置为:
LOC(a[j1][j2]...[jn]) = LOC(a)+(b2*b3*...bn*j1+b3*b4*...bn*j2+...+jn)L
其中LOC(x)代表x的位置,L为数组基本元素的长度
今天又看到两种模拟二维数组的方法,也可以拿过来理解一下:
一、利用一个二级指针来实现
#include<stdio.h>
#include<malloc.h>
int main()
{
//5行2列的数组
int **p = (int **)malloc(sizeof(int *) * 5);
for (int i = 0; i < 5; ++i)
{
p[i] = (int *)malloc(sizeof(int) * 2);
}
for (int i = 0; i < 5; ++i)
{
for (int j = 0; j < 2; ++j)
{
//输出数组每个元素地址
printf("%p\n", &p[i][j]);
}
}
for (int i = 0; i < 5; ++i)
free(p[i]);
free(p);
return 0;
}
二级指针指向了一块五个int的空间,每个int空间里面储存了两个Int空间的地址。
当使用语法糖访问时,p[i][j]先通过i定位到第几个int空间,p[i]就是一个Int,而后j则可以在指向的空间内移动,每次移动sizeof(int)大小。
二、利用数组指针来实现
#include<stdio.h>
#include<malloc.h>
int main()
{
//申请一个5行2列的整型数组
int(*p)[2] = (int(*)[2])malloc(sizeof(int) * 5 * 2);
for (int i = 0; i < 5; ++i)
{
for (int j = 0; j < 2; ++j)
{
//输出数组每个元素地址
printf("%p\n", &p[i][j]);
}
}
free(p);
return 0;
一维数组指针是一个指向一维数组的指针,当p+i,每次移动整个数组所占空间大小,p[i]即相当于一维数组地址,也是指向第一个成员的地址,而j可以确定在数组中成员的位置