你必须知道的指针基础-2.指针的声明和使用及数组和指针的关系
一、计算机知道数据类型吗?
1.1 神奇的数据类型
At first,计算机中绝大部分数据都放到内存中的,不同的数据放到不同的内存区域中。But,内存角度没有数据类型,只有二进制;数据以字节(8位二进制)为单位存取。不同数据类型占据不同的字节,例如在32位系统中:int 为4个字节,short为2个字节(下面如未特殊声明,均为32为系统环境下的说明)。下面我们看看int类型、short类型和double类型的数字分别在内存中如何存储:
①int类型:99999
由于int类型占4个字节,每个字节8位,所以在内存中会有如上图所示的四个字节来存储99999的二进制。其中,0001~0004是内存中的编号。为了验证,我们可以通过科学计算器来将99999转换为二进制看看:
由于int类型在内存占四个字节,每个字节8位,那么一共应该有32位。所以,可以看到内存编号0001中是自动补充的0,而0002中则补充了7个0。后面0003和0004则是转换后的二进制数。验证结束,发现跟上图所示的一模一样。
②short类型:8888
由于short类型只占2个字节,所以在内存中会有如上图所示的两个字节来存储8888的二进制。
③long类型:9999
由于long类型占8个字节,所以内存中会出现8个字节来存储9999的二进制。
PS:由于内存只管存储,不知道数据类型,所以我们可以知道,所谓的数据类型都是程序语言编译器在识别和标注。也就是我们所说的,要取几个字节,从哪里开始到哪里结束,都是编译器在帮我们做,内存是不管的。
1.2 C程序中神奇的&
假如有下面一段代码,声明了两个int类型的整数,其中&i表示:获得变量i所指向内存的地址,地址也是数字。
int i = 5; printf("%d\n",&i); int j = 5; printf("%d\n",&j);
那么,我们可以通过程序输出的结果来验证在内存中的地址是否是一个数字,以及是否满足上面所说的字节长度:
可以看出,两个int类型的变量的内存地址的确是数字,并且相差4个字节。(这里要说明的是在堆栈中,内存地址的分配是从高位到低位,所以这里第一个变量的内存地址比第二个变量的内存地址的数字要高)
PS:在.NET中,数据类型分为值类型和引用类型。其中,值类型被分配在栈上,引用类型被分配在堆(托管堆)上。
(1)栈高效而灵活,并且可以自动销毁变量,回收空间,但对于处理大容量的数据还是不够;
(2)堆可以存储大容量的数据,但是无法自动回收内存,需要借助于GC(垃圾回收机制)来实现,于是也就有了.NET中的托管堆。
既然&是取地址,那么就可以用int类型存储指针地址:
int j=5; printf("%d\n",&j); int iAddr = &j; printf("%d\n",iAddr);
查看结果后会发现,iAddr也会显示地址。其实,short、long、char等的地址都可以用int表示。But,如果用int表示各种指针,那么就不知道内存中到底放的是什么类型(其实内存中也不知道是什么类型,都是一堆字节数据而已)。所以,也就有了指针,在实际中一般用“类型指针”来表示,其结果是一样的。
int *iPtr = &i; printf("%d\n",iPtr);
二、指针的声明与使用
2.1 神奇的*号
(1)声明
在C中,可以用*号来声明一个指针,通常都是用具体要指向的类型的指针来指向目的变量。这样的指针就叫做“类型指针”,也就使程序语言知道要从内存中取几个字节了。
int i = 5; int* iPtr = &i; // 类型化的指针,知道是从内存中取几个字节 printf("%d\n",iPtr); int i1 = *iPtr; printf("i1=%d\n",i1); // 取iPtr指针指向的内存中的数据
(2)使用
可以使用*取指针指向的内存数据,如上面代码中的 i1 = *iPtr。下面来看看代码的运行结果:
还可以通过指针修改变量的值,例如我们可以写以下代码操作iPtr指针来修改i的值:
*iPtr = 100; printf("i=%d\n",i);
这下i的值也变成了100。(可以理解为把100存入到iPtr所指向的内存中)
2.2 小结
*的两个用途:
(1)声明的时候用来声明指针变量: int *iPtr;
(2)除了声明变量的时候,其他时候*用来表示获取指针指向的数据。
&用来获取变量的地址。
三、数组和指针
3.1 一块连续的内存区域
我们经常听说:数组在内存中是一块连续的内存区域,那么来验证一下,声明一个数组,并依此输出其内存地址:
int nums[] = {33,55,66,77,88}; for(int i=0;i<5;i++) { printf("%d\n",&(nums[i])); }
输出结果如下图所示:可以看出,各个内存地址数字均只差4个字节,是连续的。
3.2 指针如何指向数组
在开发中一般使用第二种方式,即数组元素的名字即是数组第0个元素的内存地址。
int* iptr1 = &(nums[0]); //获取第0个元素的内存地址 int* iptr2 = nums; //一般这样用,数组元素的名字就是“第0个元素的内存地址”
3.3 字符串即字符数组
在计算机中没有字符串的概念,都是用字符数组在表示字符串。因此,字符串也就是“以\0结尾的一段连续的char内存区域”。
char str1[] = "helloedison"; char* str2 = str1; printf("str1=%c\n",str1[0]); printf("str2=%c\n",*str2);
可以看到,使用指针str2指向了str1的第0个元素的地址,输出结果验证了一致性:
参考资料
如鹏网,《C语言也能干大事(第三版)》