🌈 关于C指针和数组 相关的东西 今天被chj狠狠整顿了,现在回忆整理一下相关的知识点,既作分享,也加强记忆。本blog需要有基本的指针和数组相关的知识储备

下面是学习的点。配合代码食用更佳~

提要

  1. 二维数组 存储形式和一维数组一样
  2. a[2] [3]是左值是因为a[2] [3]等价于 * (a+2* 3* sizeof(int)+3*sizeof(int))
  3. (int)b【也就是强制转换后】是右值,数组名a等效于右值
  4. 关于 intc =((int )&a);
  5. int c= (char )a;
  6. 关于 float 类型 用%d读的时候 出现的0.0000问题

1.二维数组

首先二维数组 我们知道是a[m] [n]这样的形式,其实就是数组的数组,可以理解为类似于矩阵的形式。

🤔那二维数组的存储方式和一维数组一样具体怎么理解?也就是二维数组和一维数组一样,是连续的存储空间。下面用一个代码展示一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
int main(void){
int a[2][3]={{1,2,3},{4,5,6}};

printf("a[0]:%p\n",a[0]);
printf("a[0][1]:%p\n",&a[0][1]);
printf("a[0][2]:%p\n",&a[0][1]);
printf("a[0][3]:%p\n",&a[0][3]);

printf("a[1]:%p\n",&a[1]);
printf("a[1][0]:%p\n",&a[1][0]);
printf("a[1][1]:%p\n",&a[1][1]);

return 0;
}

思考一下,给出答案

也许有人会问a[0] [3]不是越界了嘛?我的理解: 但是其实越界这个概念的提出只是因为防止在连续的存储空间内没有这个变量,或者更改了其他的变量。而在这里,也就是连续的存储空间里,a[0] [2]下个值 在我们看来是a[1] [0],但其实写成a[0] [3]也没有关系,表示的就是a[0] [2]下个连续的地址。所以这边a[1] [0]和a[0] [3]是同一个东西。

答案如下~ 二维数组的存储形式 以sizeof(int) 个单位 依次递增,其存储形式表现的和一维数组是一样的形式。

1
2
3
4
5
6
7
a[0]:0061FEFC
a[0][1]:0061FF00
a[0][2]:0061FF04
a[0][3]:0061FF08
a[1]:0061FF08
a[1][0]:0061FF08
a[1][1]:0061FF0C

关于二维数组和指针的指针

我们知道,一维数组也就是a[],其数组名a是该数组的首地址,而指针变量是存储地址的变量类型,所以这个数组名a也可当作指针变量使用,那么二维数组是否也有类似的形式?

🌿对于一维数组而言

1
2
3
4
5
6
7
8
9
10
11
12
int d[3]={1,2,3};
int *p2=d;

printf("*(p2+1):%d\n",*(p2+1));
printf("*(p2+2):%d\n",*(p2+2));
printf("*(d+1):%d\n",*(p2+1));
printf("*(d+2):%d\n",*(p2+2));

printf("address:(p2+1):%d\n",(p2+1));
printf("address:(p2+2):%d\n",(p2+2));
printf("address:(d+1):%d\n",(d+1));
printf("address:(d+2):%d\n",(d+2));

此时答案应该显而易见。由于p是指向a的指针,也就是p存储的值是a的地址,a也可当作指针变量使用,所以他俩的地址一样,解引用后得到的值也一样。

1
2
3
4
5
6
7
8
*(p2+1):2
*(p2+2):3
*(d+1):2
*(d+2):3
address:(p2+1):6422264
address:(p2+2):6422268
address:(d+1):6422264
address:(d+2):6422268

🌾对于二维数组

🤔依葫芦画瓢,那么怎么表示指向一个二维数组的指针变量,同时也可以用这个指针变量达到和类似二维数组表现形式一样的效果呢?

设有二维数组int a[3] [4];那么是不是也声明一个指针的指针,类似于int** p=a?

非也非也。

上文提到,二维数组存储形式和一维数组一样,二维数组的存储形式以sizeof(int) 个单位 依次递增,其存储形式表现的和一维数组是一样的形式。而int ** p是指针的指针,具体而言,p是一个指针,假设它存储的是地址A,而这个A是另一个指针变量的地址,于是用 ** 表示指针的指针。二维数组是和一维数组一样的存储形式,即顺序存储形式,也就不能用这种** p直接进行表示。

具体也可以试试看。 ** p如果指向a的地址,那么 p第一次解引用是a[0] [0] ,第二次解引用呢?没有地址可以给他解引用了,所以这样肯定是不行的。

**指向二维数组的指针可以表示为 int (*p)[4] = a;**

也就是指向数组的指针,具体而言,这个p是一个指针变量,它指向int[4]类型的数组;p+1表示的是 从基地址a[0] [0] 开始向下偏移1个单元,这一个单元包含4个int变量,也就是4个sizeof(int);二维数组a[3] [4]可以看作是含有3个单元,其中这的每个单元含有4个int类型的变量。所以总共有3 *4 个sizeof(int)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int (*p1)[4]=a;

printf("p1:%p\n",p1);
printf("a:%p\n",a);
printf("a[0][0]:%p\n",&a[0][0]);
printf("------------------\n");

printf("p1+1:%p\n",p1+1);
printf("a+1:%p\n",a+1);
printf("a[0][4]:%p\n",&a[0][4]);
printf("------------------\n");

printf("p1+2:%p\n",p1+2);
printf("a+2:%p\n",a+2);
printf("a[1][4]:%p\n",&a[1][4]);
printf("------------------\n");

printf("*(p1+1):%p\n",*(p1+1));
printf("*(a+1):%p\n",*(a+1));
printf("a[1][0]:%p\n",a[1]);
printf("------------------\n");

printf("*(*(p1+1)+1):%d\n",*(*(p1+1)+1));
printf("a[1][1]:%d\n",a[1][1]);


答案如下~记得自己多敲几遍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
p1:0061FEE8
a:0061FEE8
a[0][0]:0061FEE8
------------------
p1+1:0061FEF8
a+1:0061FEF8
a[0][4]:0061FEF8
------------------
p1+2:0061FF08
a+2:0061FF08
a[1][4]:0061FF08
------------------
*(p1+1):0061FEF8
*(a+1):0061FEF8
a[1][0]:0061FEF8
------------------
*(*(p1+1)+1):5
a[1][1]:5

2. intc =((int )&a);

1
2
3
4
5
6
7
int a=9;
int *p3=(int*)&a;
int *p4=(char*)a;
printf("*p3:%d\n",*p3);
printf("address: p3 :%p\n",p3);
printf("address: a :%p\n",&a);
printf("==============================\n");

第二行:这句话的意思是把a转换为一个int*的指针,这个指针变量存储的是3,这个变量的地址还是 a的地址。最后赋值给c

第三行:意思是把a强制转为char的变量,注意,不管是什么指针,它都是8个字节的,所以它就把a,也就是3,作为 char 使用,存储的是3这个值,也就是指向的地址是3,而这个变量a的地址还是原来的地址。最后赋值给d。


结果

1
2
3
*p3:9
address: p3 :0061FED4
address: a :0061FED4

3. float 类型 用%d读 出现的0.0000问题

一切从一个程序说起,还是有点坑的

1
2
3
4
5
6
7
#include<stdio.h>
int main(void){
float a=999897.777;
int b=a;
printf("%d,%f,%d,%f\n",b,a,a,b);
return 0;
}

🤔仔细想想最后输出什么;


1
999897,999897.750000,-2147483648,0.000000

这段代码其实比较基础。

首先,前面几个不难得到。

1
int b=a; 

进行了一次隐式的转换,也就相当于int b=(int) a ,所以此时b=3;

前面的第一个b 和 a不疑惑。第三个值,a等于一个负值,其实也不难理解,由于浮点数存储的形式和整型是不一样的,所以它读到的整形变量上是一个赋值不奇怪。

最后一个0.00000就比较难理解了,需要和底层硬件联系起来。

🐳具体解答可以看看这篇blog

由于float 和 int 存储形式的差异,导致最终的结果为0;

flaot 存储的形式是 类似 整数+指数 这样的类型的,比如312.2怎么存储的?就是把3122放入整数中,然后指数为-1,也就类似于3122* 10(-1) 这样存储的 。