概述
指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的储存空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
为了了解指针,我们首先需要讲解一下内存的概念。
内存
内存含义
存储器:在计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分。
内存:内部存贮器,暂存程序/数据——掉电丢失 SRAM、DRAM、DDR、DDR2、DDR3。
外存:外部存储器,长时间保存程序/数据—掉电不丢ROM、ERRROM、FLASH(NAND、NOR)、硬盘、光盘。
内存是沟通CPU与硬盘的桥梁
内存作用:
暂存放CPU中的运算数据
暂存与硬盘等外部存储器交换的数据
物理存储器和存储地址空间
有关内存的两个概念:物理存储器和存储地址空间。
物理存储器:实际存在的具体存储器芯片。
主板上装插的内存条
显示卡上的显示RAM芯片
各种适配卡上的RAM芯片和ROM芯片
我们所有的数据都存在内存中,对每一个物理存储单元,分配一个单元,我们称之为编码,可以根据分配的号码找到相应的存储单元,也称为寻址 。
存储地址空间:对存储器编码的范围。
编码:对每个物理存储单元(一个字节)分配一个号码 。
寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写 。
内存地址
在这里,我们将内存抽象成一个很大很大的一维字符数组。编码就是对内存的每一个字节去分配一个32位或64位的编号(位数与自己的处理器有关)。儿这个内存编号我们称其为内存地址。 内存中的每一个数据都会分配相应的地址,例如:char:占一个字节,分配一个地址 。int:占四个字节分配四个地址等。
指针和指针变量
内存区中的每一个字节都是有一个编号的,这个编号就是“地址”。如果我们在程序中定义了一个变量,在对这个程序进行编译或者运行的时候,系统会自动给这个变量分配内存单元,确定其地址。
我们将要学习的指针就是内存单元的编号,而指针变量就是存放地址的一个变量。我们平时通常会把指针变量称作指针,但是指针变量与指针的含义是完全不同的。
从这张图中就可以看出,b_point是指针,其存放了变量b的内存地址,也就是指针变量。
指针基础知识
上面我们了解完了内存以及内存和指针关系之后,那么接下来我们就来开始指针的学习吧!
指针变量的定义和使用
首先我们不要将指针想象的那么特殊,指针就是是一种数据类型,而指针变量也是一种变量,指针变量只想谁,就把谁的地址赋值给指针变量。我们使用“ * ”操作符操作指针变量指向的内存空间。而我们平时使用指针,主要是因为使用指针往往可以生成更高效、更紧凑的代码。
声明一个指针
上面说到指针就是一个变量所以,指针的声明方式与一般的变量声明方式没太大区别:
int *p ; // 声明一个 int 类型的指针 p char *p ; // 声明一个 char 类型的指针 p int *arr[10] // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指针。 int (*arr)[10] // 声明一个数组指针,该指针指向一个 int 类型的一维数组 int **p; // 声明一个指针 p ,该指针指向一个 int 类型的指针,也就是我们的二级指针。
初始化一个指针
声明一个指针变量并不会自动分配任何内存。在对指针进行间接访问之前,指针必须进行初始化,或是使他指向现有的内存,或者是给他动态分配内存,否则我们并不知道指针指向哪儿,这将是一个很严重的问题,也是我们后面所提到的野指针。所以我们在定义指针后一定要进行初始化,而初始化操作具体如下
方法1:使指针指向现有的内存
int n = 1; int *p = &n; // 指针 p 被初始化,指向变量 x ,其中取地址符 & 用于产生操作数内存地址
方法2:动态分配内存给指针
int *p ; p = (int *)malloc(sizeof(int) * 10) ; // malloc 函数用于动态分配内存 free(p) ; // free 函数用于释放一块已经分配的内存,常与 malloc 函数一起使用。
总结:
#include <stdio.h> int main() { int a = 0; char b = 100; printf("%p, %p\n", &a, &b); //输出a, b的地址 //int * 代表是一种数据类型,int*指针类型,p才是变量名 int *p; p = &a;//将a的地址赋值给变量p,p也是一个变量,值是一个内存地址编号 printf("%d\n", *p);//p指向了a的地址,*p就是a的值 char *p1 = &b; printf("%c\n", *p1);//*p1指向了b的地址,*p1就是b的值 return 0; }
附: &可以取得一个变量在内存中的地址。但是不能取寄存器变量,因为寄存器变量不在内存里,而在CPU里面,所以是没有地址的。
所以这里将上面所讲的部分总结为代码,也希望大家都可以去看懂并理解这些内容。
#include <stdio.h> int main() { int a = 0 ; int b = 11 ; int *p = &a ; *p = 100 ; printf("a = %d, *p = %d\n", a, *p) ; p = &b; *p = 22; printf("b = %d, *p = %d\n", b, *p) ; }
运行结果:
指针大小
测量指针大小时我们需要使用sizeof()。
sizeof()是一个关键字,它是一个编译时运算符,用于判断变量或数据类型的字节大小。
sizeof 运算符可用于获取类、结构、共用体和其他用户自定义数据类型的大小。其使用语法如下:
sizeof (data type)
其中,data type 是要计算大小的数据类型,其实包括类、结构、共用体以及其他用户自定义数据类型。
并且sizeof()测的是指针变量指向存储地址的大小,在32位平台,所有的指针(地址)都是32位(4字节),同理在64位平台,所有的指针(地址)都是64位(8字节) 。
#include <stdio.h> int main() { int *p1; int **p2; char *p3; char **p4; printf("sizeof(p1) = %d\n", sizeof(p1)); printf("sizeof(p2) = %d\n", sizeof(p2)); printf("sizeof(p3) = %d\n", sizeof(p3)); printf("sizeof(p4) = %d\n", sizeof(p4)); printf("sizeof(double *) = %d\n", sizeof(double *)); }
运行结果
其中我们可以看出,我们在定义了多级指针之后,通过输出发现,其内存大小是相同的,所以内存大小是不受指针级数的影响。