最近写代码总是被基础知识卡住,十分耽误时间,今天又卡住了。所以下定决心一定要弄清楚这个问题,然后不好的是网上的资料总是解决的并不清楚,总是在纠结什么是指针数组什么是数组指针,看了《C缺陷和陷阱》也没有我想要的东西。要么就是C和C++混为一谈,new int[5]这种语法C肯定是错误的,所以找个机会总结一下,然后希望以后不要再栽在基础上。
定义
指针数组:int p[10] 数组指针 int(*p)[5]。[]的优先级高所以 int *p[10]表示数组中都是int ,而p指向数组首地址。没图说个p
反之int(*p)[5]表示 *p指向一个大小为5的数组,p则是二维数组的首地址。
使用
指针数组-本质是一个数组
int *p[10];
printf("%d\n",sizeof(p) );//是40
for (i = 0; i < 10; ++i)
{
p[i] = malloc(sizeof(int)*i);
}
for (i = 0; i < 10; ++i)
{
free(p[i]);
}
从sizeof(p)=40可以看出来p指向的是数组的首地址,数组也被分配到了栈上,所以只需要对其中10个int *分配就ok,free也是。对应的二维矩阵像是p[10][i]
数组指针-本质是一个指针
int (*p)[10];
p = malloc(sizeof(int *)*2);
printf("%d\n",sizeof(p) );
free(p);
从sizeof(p)=4看出来这就是个指针,指针就一定要分配内存了,但是sizeof里面只需要分配指针大小就可以。free对着p。这时p被分配到了堆。对应的二维矩阵像是p[i][10]
结构体
当然了只使用基本类型没什么难度,遇到结构体有可能就懵逼了。基本的原理都是一样的
typedef struct block
{
int a;
int b;
int c;
}block_t;
建议不要这么定义结构体
typedef struct block
{
int a;
int b;
int c;
}block_t *;
莫名给自己加个指针这种代码我怎么也看不懂。。。。而且对你理解问题会造成困扰。如果你得到的是block_t *那么使用->a其实计算的是对应首地址的偏移量,如果是block_t那么使用.表示里面的变量。
指针数组
block_t *x[5];
for ( i = 0; i < 5; ++i)
{
x[i] = malloc(sizeof(block_t)*i);
}
x[2][1].a = 1;
printf("%d\n", (*(x+2)+1)->a);
数组指针
block_t (*x)[5];
int *a;
x = malloc(sizeof(block_t *)*2);
x[1][5].a = 5;
x[1][3].b = 1;
printf("%d\n",x[1][5].a );
printf("%d\n",sizeof(x[1]));
printf("%p %p %p %p\n",a,x,x[0],x[1] );
这个基本就可以看出是在栈还是堆分配的内存了
函数调用
往往我们还需要传入函数,这时指针可能会让你头晕,或者不清楚是否传入的是引用还是地址。
先从简单说起吧,如果我们在函数中需要改变p,而我们的函数是这样的
void fun(int p);
那肯定改变不了,这是最基本的知识了。
我们应该这么做
void fun(int *p);
然后
fun(&p);
没有任何问题吧。
那我们这样行吗?
int *p;
void fun(int p);
fun(*p);
别晕,不行,不是有了指针就ok了,传入的依然是*p指向的int的引用。有了这点基础,再加上我们上面对指针数组和数组指针本质的理解就应该可以想明白函数参数应该有几个指针了。看个例子
void for_each(block_t *b)
{
int i;
for (i = 0; i < 5; ++i)
{
printf("%d\n",b[i].a );
}
}
void test_fun(block_t *b)
{
b = malloc(sizeof(block_t)*5);
int i;
for (i = 0; i < 5; ++i)
{
b[i].a = i;
}
}
int main()
{
block_t *b;
int i;
test_fun(b);
for_each(b);
}
上面的例子能达到目的么?不能,为什么因为b是指向一个数组的首地址,传入参数传入的是引用,不是这个首地址的地址。。(真特么绕,意思就是传入的这个参数实际是引用,因为他是数组的首地址)怎么办,加指针啊!
void for_each(block_t *b)
{
int i;
for (i = 0; i < 5; ++i)
{
printf("%d\n",b[i].a );
}
}
void test_fun(block_t **b)
{
*b = malloc(sizeof(block_t)*5);
int i;
for (i = 0; i < 5; ++i)
{
(*b+i)->a = i;
}
}
int main()
{
block_t *b;
int i;
test_fun(&b);
for_each(b);
}
ok搞定!
上面的例子其实都是一维指针,然后传入二维地址,跟我们的指针数组和数组指针(二维)没有关系。但是记住,确定指针的维数然后再做就会减少犯错的机会。还有就是指针数组的地址是在栈分配的,所以如果在函数中分配记得分配至堆上或者从调用者传入,(int **p)。
数组指针多用于二维数组传入函数时的函数签名中的类型定义,什么意思
int a[][5];
void fun(int (*a)[]);
fun(a);
C里面必须定义二维数组的宽度,而且必须是编译时可以确定的!C++是不一样的