内存地址
- 字节:字节是内存的容量单位,英文称为 byte,一个字节有8位,即 1byte = 8bits
- 地址:系统为了便于区分每一个字节而对它们逐一进行的编号,称为内存地址,简称地址。
- 注:地址是按字节编号的,正常是十六进制编号,但是不管十六进制还是八进制还是二进制什么进制编号,加一都是加一个字节
基地址
- 单字节数据:对于单字节数据而言,其地址就是其字节编号。
- 多字节数据:对于多字节数据而言,其地址是其所有字节中编号最小的那个,称为基地址、入口地址。
取址符
- 每个变量都是一块内存,都可以通过取址符 & 获取其地址
- 例如:
int a = 100;
printf("整型变量 a 的地址是: %p\n", &a);
char c = 'x';
printf("字符变量 c 的地址是: %p\n", &c);
double f = 3.14;
printf("浮点变量 f 的地址是: %p\n", &f);
- 注意:
- 虽然不同的变量的尺寸是不同的,但是他们的地址的尺寸确实一样的。
- 不同的地址虽然形式上看起来是一样的,但由于他们代表的内存尺寸和类型都不同,因此它们在逻辑上是严格区分的,根据指针的类型加以区分。
- 地址的尺寸即系统寻址范围,即字长
指针基础
- 指针的概念:
- 地址。比如 &a 是一个地址,也是一个指针,&a 指向变量 a。
- 指针是一个专门用于存储地址的变量,又称指针变量。
- 指针的定义:
int *p1; // 用于存储 int 型数据的地址,p1 被称为 int 型指针,或称整型指针
char *p2; // 用于存储 char 型数据的地址,p2 被称为 char 型指针,或称字符指针
double *p3; // 用于存储double型数据的地址,p3 被称为 double 型指针
注意:
-
-
- 以上指针p1 .p2 .p3 都是指针,因此他们都用于存储某一个数据的入口地址。
- 地址的大小由系统的位数决定。
- 指针的大小都是一样的。
- 唯一的不同是他们存储的地址的类型有差异。
- 指针的类型并不影响指针本身的大小,而是决定了如何解析指针中存储的内存的二进制编码
-
- 指针的赋值:赋给指针的地址,类型需跟指针的类型相匹配。
int a = 100;
p1 = &a; // 将一个整型地址,赋值给整型指针p1
char c = 'x';
p2 = &c; // 将一个字符地址,赋值给字符指针p2
double f = 3.14;
p3 = &f; // 将一个浮点地址,赋值给浮点指针p3
- 指针的索引:通过指针,取得其指向的目标
*p1 = 200; // 将 p1 指向的目标(即a)修改为200,等价于 a = 200;
*p2 = 'y'; // 将 p2 指向的目标(即c)修改为'y',等价于 c = 'y';
*p3 = 6.6; // 将 p3 指向的目标(即f)修改为6.6,等价于 f = 6.6;
- 指针的尺寸
- 指针尺寸指的是指针变量所占内存的字节数
- 指针所占内存,指针用于存储内存地址因此取决于地址的长度,而地址的长度则取决于系统寻址范围,即字长(系统位数)
- 结论:指针尺寸只跟系统的字长有关,跟具体的指针的类型无关
野指针
- 概念:指向一块未知区域(或者已经释放的区域)的指针,被称为野指针。野指针是危险的。
- 危害:
-
- 引用野指针,相当于访问了非法的内存,常常会导致段错误(segmentation fault)
- 引用野指针,可能会破坏系统的关键数据,导致系统崩溃等严重后果
- 产生原因:
-
- 指针定义之后,未初始化,因此他的值是随机的
- 指针所指向的内存,被系统回收
- 指针越界
- 如何防止:
-
- 指针定义时,及时初始化
- 绝不引用已被系统回收的内存
- 确认所申请的内存边界,谨防越界
空指针
很多情况下,我们不可避免地会遇到野指针,比如刚定义的指针无法立即为其分配一块恰当的内存,又或者指针所指向的内存被释放了等等。一般的做法就是将这些危险的野指针指向一块确定的内存,比如零地址内存。
- 概念:空指针即保存了零地址的指针,亦即指向零地址的指针。
- 示例:
// 1,刚定义的指针,让其指向零地址以确保安全:
char *p1 = NULL;
int *p2 = NULL;
// 2,被释放了内存的指针,让其指向零地址以确保安全:
char *p3 = malloc(100); // a. 让 p3 指向一块大小为100个字节的内存
free(p3); // b. 释放这块内存,此时 p3 相当于指向了一块非法内存
p3 = NULL; // c. 让 p3 指向零地址
指针运算
- 指针加法意味着地址向上(高)移动若干个目标
- 指针减法意味着地址向下(低)移动若干个目标
- 目标的单位大小取决于指针本身的类型,与他所指向的数据地址无关
- 示例:
int a = 100;
int *p = &a; // 指针 p 指向整型变量 a
int *k1 = p + 2; // 向上移动 2 个目标(2个int型数据)
int *k2 = p - 3; // 向下移动 3 个目标(3个int型数据)
由实验可以得出,指针的加减,只与指针本身的类型相关,如果指针是int 类型那么在加减的时候每+1或-1加减的是4个字节,如果指针的类型是 short 在加减的时候每+1或-1加减的是2个字节.
结语
在本篇博客中,我们初步探索了指针的概念及其在编程中的重要性。指针作为一种强大的工具,能够直接操作内存地址,从而提高程序的灵活性与效率。通过理解指针的定义、基本操作以及实际应用场景,我们为日后的编程之旅打下了坚实的基础。
掌握指针不仅能帮助我们更好地理解数据结构如链表和树,更能深入了解内存管理和优化程序性能。尽管指针的使用可能会增加一定的复杂性,但它们所带来的灵活性和强大功能,使得我们在编程中能够更加高效和有创意。
希望你能在实践中继续探索指针的奥秘,并将所学知识应用于实际项目中。感谢你的阅读,期待在未来的博客中和你分享更多有趣的编程话题与技巧!