简图记录学习~
C语言常见难点就是指针和数组的使用,概念和使用上都常容易混淆
首先明确一点,指针和数组没有关系,只是从访问上有看上去类似用法,其实是两个概念,一定要区分清楚;
指针 | 数组 | |
概念 | 一种特殊数据类型,它的值是作为内存地址,常用于对指向地址内存数据按所指向类型进行访问 | 一种构造数据类型,由多个数组元素组成,每个元素是基本类型或者构造类型。编译器在定义一个数组的时候,分配了sizeof(元素类型)*元素个数 大小的内存,命名为数组名;(注意:一旦内存匹配好就不可再分开,且如定义数组int a[5] 其中a[0]、a[1]是一种访问元素方式,并非元素内存名叫a[0],元素是没有名字的) |
声明 | 指针的声明必须是指针,如char *p只能用extern char *p不能用extern char p[]; | char a[100];必须以extern char a[]或者extern char a[100]声明,不能以extern char *a; |
大小 | 大小置和cpu当前运行模式下的寻址位数相关,32位就是4byte | int a[5]为例子: sizeof(a)大小为sizeof(int)*5; sizeof(a[0])和sizeof(a[5])大小为sizeof(int) (注意这里a[5]是a[4]后面 一个元素,已越界) |
访问 | 可以 指针方式*(p+i) 或者 下标方式 p[i]访问指向内存数据。 本质是对地址=P值+i*sizeof(类型)内存进行访问;(指针计算:注意p+i相当于p值加sizeof(类型)*i而非p值直接加i) | 可以 指针方式*(a+i) 或者 下标方式a[i] 访问a[i]元素。 本质是对地址=首元素a[0]地址值+i*sizeof(类型)元素进行访问; a做右值时表示首元素地址相当于&a[0],数组首地址为&a,注意虽然二者值一样但概念不同。 |
二级指针/二维数组 | 二级指针就是指向一个指针类型数据的指针. 常用与当希望通过函数修改某个指针数据时如内存分配获取新地址,若使用一级指针做函数入参只是传递指针值。而通过对指针取地址以二级指针做入参,则可以通过指针地址修改内容 | 如int a[3][4],由于内存是线性的,编译器其实将二维数组看做一维数组,每个元素为一个数组连续存储。 可以按下标 方式a[i][j]或者指针方式*(*(a+i)+j)访问指定位置数组元素;本质就是对 地址=首元素a[0][0]地址值+i*(sizeof(int)*4)+j*sizeof(int)的元素访问; |
其他重要信息 | 函数指针:指向函数的指针。 使用*p可调用函数,如char (*p)(char *a); 例如: (*(void(*)())0)(); //将0地址强转为void(*)()型函数指针并调用; char (*p[10])(char *a); //函数指针数组 char (*(*p)[10])(char *a); //函数指针数组指针 | |
数组和指针关联 | (1)数组指针和指针数组 数组指针:int (*p)[10] 指针数组:int *p[10] (注意[]优先级比*高 先结合就表示为数组) (2)数组做函数入参 一维数组做函数入参时,编译器将其解析为指向其首元素的指针 如void test(char a[10])或者void test(char a[])等价于void test(char *a)。原因:C语言中所以非数组类型做实参时均以传值方式,数组传值引起的时间和空间开销太大因此解析为指针方式访问。同理 函数的返回值也不能为数组。 针对超过一维数组做入参时,只能将第一维数组解析为指向首元素的指针 如void test(char a[3][4])或者void test(char a[][4])等价为void test(char (*a)[4])。 (注意指针数据解析 void test(char *p[5])等价为void test(char **p)) |
四、几练习题
1、求哪几个地址哪几个相同
int a[5]={1,2,3,4,5};
void print_test(int *p)
{
print("addr1=%p addr2=%p\n",&p,&p[0]);
}
int main()
{
print_test(a);
print("addr3=%p addr4=%p\n",&a,&a[0]);
}
解析:addr2、addr3、addr4相同。&p为指针变量P在栈上的地址,addr2为通过p[0]取出指向内存(相当于a[0])然后取地址。&a为数组首地址、&a[0]为数组首元素a[0]地址;
2、求*(a+1)、*(p1-1)、*p2值
int a[5]={1,2,3,4,5};
int *p1=(int *)(&a + 1);
int *p2=(int *)((int)a + 1);
解析:
*(a+1)就是a[1]为2;
&a+1实际为数值取地址+1相当于int (*)[5]型指针+1,就相当于p1指向了a[5]已越界,由于P为int*型,p-1地址减sizeof(int)和a[4]一样,因此值为5;
(int)a+1由于转化为,实际地址只加了1,相当于从a[0]元素第2个byte到a[1]前3个byte构成一个int数据,考虑到字节序,在小端系统上为0x2000000;
3、求&p[4][2]-&a[4][2]
int a[5][5];
int (*p)[4];
p=a;
解析:&p[4][2]值=p值+4*(sizeof(int)*4)+2*sizeof(int),&a[4][2]值为=a[0]地址+4*(sizeof(int)*5)+2*sizeof(int),因此为-4;