指针,无疑是 C 语言的精华,没有掌握指针,也就没有掌握 C 语言。指针让 C 语言更像是结构化的语言,所有在内存中的数据结构均可用指针来访问,指针让 C 语言带来了 更简便的操作,更优的效率,更快的速度。他是天使,也是魔鬼。指针可以和所有的类型结合在一起进行使用和操作,这也是指针灵活性的体现。因为指针类型可以指向任何一种数据类型,并且指向变量的地址,所以在操作的时候可以使得代码更加简洁,操作更加方便,并且在一些情况下,我们之后运用指针才能解决,C指针是C语言的灵魂,我们在这里对于指针有一个比较简单的认识和一些基本操作。
内存编址
在讲解指针之前,我们简单复习一下内存的知识,前面我们学习的一维数组也好,二维数组也好都仅是一种逻辑上的体现,最终数据是要保存到内存中的,而内存是线性的,内存的线性是物理基础。
内存中的每个字节都对应一个地址,通过地址才能找到每个字节,也就是说8个位组成一个字节是编址的最小单位。
基本知识(定义与访问)
首先给大家介绍一个关系:
指针 == 地址
如何理解:
我们整个内存空间的每一个单元格都除了要保存数据外,每一个单元格都有一个地址,就像房子能够容纳各种各样的人,但是每一个房子都必须要有门牌号。这个门牌号标记着房子的地址,那么指针就标记这内存空间的地址,如此理解就比较形象了。
现在初步理解了,那么我们的问题来了 我们知道数据的地址有什么,我们定义变量无非就是要保存数据和读取数据,我们如何简单的运用指针呢,首先指针是一个指针类型的变量,就像我们定义基本类型一样。
那么同样 我们定义指针类型只需要在类型前面加一个* 就定义了指针类型的变量。
定义地址了指针类型的变量之后 我们就可以用指针类型指向其他变量来保存其他变量的地址,那么如何指向其他变量的地址并且我们如何把指向变量里面的数据取出来呢。
在这里 我们介绍两个符号:
取地址符号 &
取值符号 * (解引用符)
那么当我们用定义的指针指向变量时,我们只需要使用取值符号*就可以取到指向的变量里面存放的值。
指针变量存放地址 我们就可以定义指针变量指向变量的地址
例如
int a = 13;
int *p = &a;
*p = 20;
在这里我们专门解释一下上面第二行和第三行
我们定义指针类型的的时候的格式是:
类型名 * 变量名;
在指针初始化的时候 ,也就是给指针赋值的时候,赋值的是变量的地址,也可以理解为指针指向变量。
我们在适合用指针的时候,之后在定义的时候在类型名后面带上* 表示是指针类型的变量,在之后的使用过程中就不需要加 * 因此第三行代码的意思是对指针变量p运用了取值符号 所以是将p所指向的变量a的值从13改变为为20。
我们也可以这样写:
Int a = 10; //定义整型变量a 赋值为10
Int * p; // 定义整型指针 指针名为p
P = &a; // 指针p存放的是变量a的地址 也可以说指针p指向a
*p = 30; // 通过对指针*进行取值 将p所指向的变量(也就是a)的值赋值为30
上面的例子也是将a的值从10改变为30,这样我们就学习到了基本指针的定义与访问。
指针变量的大小
指针变量所占内存的大小:在我的内存模型和编码规则博客中已经介绍了内存的编址问题,这里我们直接提出结论,不懂的朋友可以返回查看。在32位操作系统,能够识别的最大内存是4G的内存(实际不到4G,其他设备驱动和内核占用内存不能被访问到)内存分配的逻辑(虚拟)地址是从0X000000到0XFFFFFF 这是内存种的所有地址范围。那么8位的16进制数组就可以访问内存种所有的地址单元,8位16进制数据也就是32位2进制数据,所有我们只需要32位2进制数据就可以访问到所有的内存地址,32位就是4个字节,所有类型的指针都是32位,4字节。
char a; short b; int c; double d;
printf("sizeof(&a) = %d\n", sizeof(&a));
printf("sizeof(&b) = %d\n", sizeof(&b));
printf("sizeof(&c) = %d\n", sizeof(&c));
printf("sizeof(&d) = %d\n", sizeof(&d));
测试结果为:
通过运算的方式,我们可以求得变量的地址。32 位机的情况下,无论是什么类 型大小均是 4。而 64 位机大小均是 8。这是由当前机型的地址总线决定的。
我们在开始的时候已经提出了&和*的使用
那么对于我们普通变量来说,char类型占一个字节,那就占一个地址,那么对于其他类型来说包含多个字节也就拥有多个地址,当我们对于其他类型变量取地址的时候拿到的是哪一个字节呢?取地址的时候取的是低位字节地址。我们先给出答案,再进行验证。
首先我们先来打印不同类型的变量的地址:
char a; short b; int c; double d;
printf("%#x\t%d\n", &a,&a);
printf("%#x\t%d\n", &b,&b);
printf("%#x\t%d\n", &c,&c);
printf("%#x\t%d\n", &d,&d);
打印结果为:
理解:当变量不是一个字节的时候,指针指向变量的低地址:
char a = 1;
short b = 2;
int c = 3;
double d = 4;
char* pa = &a;
short* pb = &b;
int* pc = &c;
double* pd = &d;
图示理解为:
我们在这里作以说明,测试平台为vs在每次打印之后,即使相同变量再次打印,地址都会改变,通过这样的方式可以更加灵活,保证安全性,但是之后我们需要通过不改变变量地址来测试一些知识,我们将会在devc++平台下进行测试。使用的时候会向读者提示到。
我们在上面已经讲到了通过&取到变量的地址,通过*访问指针所指向的变量,并且知道了指针变量的大小。接下来我们通过一点代码进行本章博客的总结
char a = 1;
short b = 2;
int c = 3;
long d = 4;
float e = 1.2;
double f = 2.3;
printf("打印出变量地址为:\n");
printf("&a = %p\n", &a);
printf("&b = %p\n", &b);
printf("&c = %p\n", &c);
printf("&d = %p\n", &d);
printf("&e = %p\n", &e);
printf("&f = %p\n", &f);
printf("打印出变量地址的大小为:\n");
printf("sizeof(&a) = %d\n", sizeof(&a));
printf("sizeof(&b) = %d\n", sizeof(&b));
printf("sizeof(&c) = %d\n", sizeof(&c));
printf("sizeof(&d) = %d\n", sizeof(&d));
printf("sizeof(&e) = %d\n", sizeof(&e));
printf("sizeof(&f) = %d\n", sizeof(&f));
printf("通过取地址和解引用访问到变量的值为:\n");
printf("a = %d\n", *(&a));
printf("b = %d\n", *(&b));
printf("c = %d\n", *(&c));
printf("d = %ld\n", *(&d));
printf("e = %f\n", *(&e));
printf("f = %f\n", *(&f));
打印结果为: