指针的概念与引用,索引
一、内存地址
字节:
- 定义: 字节(byte)是内存容量的一个单位,一个字节包含8个位(bit)。
地址:
- 定义: 内存地址是系统为了方便区分每一个字节的数据而对内存进行的逐一编号。该编号就是内存地址。
基地址:
- 单字节的数据: 如
char
类型,其所在地址的编号就是该数据的地址。 - 多字节的数据: 如
int
类型,它占用4个连续的内存地址编号,其中最小的地址编号称为该变量的基地址。
取地址符号:
- 定义: 每一个变量实际上都对应了一片内存,可以通过取地址符号
&
来获取其地址。
示例:
#include <stdio.h>
int main() {
int a;
char c;
// 输出变量的地址
printf("a的地址为:%p\n", (void*)&a);
printf("c的地址为:%p\n", (void*)&c);
return 0;
}
运行结果:
a的地址为:0x7fffd5595d84
c的地址为:0x7fffd5595d83
注意事项:
- 地址大小: 虽然不同的
数据类型
占用的内存空间不同,但是它们的地址所占用的内存空间
(即地址的大小)是恒定的,由系统的位数
决定(32位系统或64位系统)。 - 地址和数据类型: 不同地址在表面上看似相同,但它们所代表的
内存尺寸不同
(与存放的数据类型相关)。访问这些地址时需要严格区分它们的逻辑关系。
总结:
- 每个变量在内存中都有一个地址,可以通过取地址符号
&
来获得。 - 地址的大小取决于系统的位数,而不是数据类型。
- 需要严格区分不同数据类型的地址,以正确访问内存中的数据。
二、 指针基础
2.1 指针的概念
- 地址:
&a
表示变量a
的地址,这也被称为指针。指针是专门用来存放地址的变量,其大小由系统的位数决定(例如,32位系统中指针大小为4字节,64位系统中为8字节)。 - 指针变量: 用来存储内存地址的变量,称为指针。
指针的定义语法
int a; // 定义一个整型变量 a--> 定义一片内存,名字为a,约定好该内存用来存放整型变量
int *p; // 定义一个指向整型数据的指针 p --> 定义一片内存名字叫p,约定好该内存用来存放整形数据的地址
char *p1; // 定义一个指向字符数据的指针 p1
double *p2; // 定义一个指向双精度数据的指针 p2
注意事项:
- 指针类型: 不决定该指针的大写哦,指针的类型用来告诉编译器如何解读和访问指针所指向的内存地址的数据类型和大小。
- 指针大小: 指针的大小取决于系统的位数,而不是指向的数据类型。
2.2 指针的赋值与初始化
#include <stdio.h>
int main() {
int a = 100;
int *p = &a; // 定义并初始化指向整型数据的指针 p
double d = 1024.1234;
double *p1 = &d; // 定义并初始化指向双精度数据的指针 p1
float f;
float *p2; // 定义但未初始化指向浮点数据的指针 p2
p2 = &f; // 给指针 p2 赋值
// 打印指针和指向的值
printf("地址 p 指向的值: %d\n", *p);
printf("地址 p1 指向的值: %f\n", *p1);
return 0;
}
注意事项:
- 类型匹配: 不同类型的指针应该指向相应类型的变量地址。例如,
int *
应该指向int
类型的变量,double *
应该指向double
类型的变量。 - 初始化: 指针在定义时最好初始化,否则其默认值是未定义的,可能会指向不合法的内存地址,导致程序错误。
示例代码解析
#include <stdio.h>
int main() {
int a = 100;
int *p = &a; // p 是指向 a 的指针,存储 a 的地址
double d = 1024.1234;
double *p1 = &d; // p1 是指向 d 的指针,存储 d 的地址
float f;
float *p2; // p2 是未初始化的指针
p2 = &f; // 给 p2 赋值,使其指向变量 f
// 打印地址和指向的值
printf("a 的地址: %p, 值: %d\n", (void*)p, *p);
printf("d 的地址: %p, 值: %f\n", (void*)p1, *p1);
printf("f 的地址: %p\n", (void*)p2);
return 0;
}
运行结果:
a 的地址: 0x7ffee4b96f5c, 值: 100
d 的地址: 0x7ffee4b96f58, 值: 1024.123400
f 的地址: 0x7ffee4b96f54
2.3 指针的解引用与索引
**解引用(Dereferencing)**指针是通过指针获得它所指向的数据,即通过指针访问或修改指针所指向的变量的值。解引用操作符是 *
。
2.3.1 通过指针解引用与索引示例
#include <stdio.h>
int main() {
int a = 100; // 定义一个整型变量 a 并赋值为 100
int *p = &a; // 定义一个指针 p,指向变量 a 的地址
// 通过指针 p 修改 a 的值
*p = 250; // 解引用 p,将 a 的值修改为 250
// 打印解引用指针 p 后的值
printf("*p: %d\n", *p); // 输出 *p 的值,即 a 的新值 250
// 打印变量 a 的值
printf("a: %d\n", a); // 输出 a 的值,应该是 250
return 0;
}
-
定义与初始化:
int a = 100; int *p = &a;
这里定义了一个整型变量
a
并赋值为100
,然后定义一个指针p
,并将p
初始化为a
的地址。 -
通过指针修改变量的值:
*p = 250;
解引用指针
p
,即访问p
所指向的内存地址,并将该地址的值修改为250
。这相当于直接将变量a
的值修改为250
。 -
打印指针解引用后的值:
printf("*p: %d\n", *p);
打印
*p
的值,即指针p
所指向地址的值。因为前一步将a
的值改为250
,所以*p
的值也是250
。 -
打印变量
a
的值:printf("a: %d\n", a);
打印变量
a
的值,确认a
的值已经被修改为250
。
运行结果:
*p: 250
a: 250
重要注意事项:
- 指针的类型: 指针的类型决定了解引用时访问内存的方式和数据类型。例如,
int *p
表示指向一个整型数据的指针,通过*p
可以访问到一个整型变量。 - 指针初始化: 在使用指针前应确保其已经被初始化。如果指针未初始化就解引用,可能会导致
未定义行为
(访问非法内存)。 - 解引用操作符
*
: 解引用操作符*
用于获取指针指向地址的值,即通过指针访问内存中存储的数据。
2.4 野指针
野指针指的是一个指向未知或未分配内存区域的指针。使用野指针可能会导致程序崩溃、数据损坏,甚至系统崩溃。通常会出现段错误(Segmentation fault)。
2.4.1 野指针的危害
- 段错误(Segmentation fault): 程序试图访问非法内存区域,导致崩溃。
- 数据损坏: 野指针可能修改关键数据,导致数据不一致或程序行为异常。
- 系统崩溃: 在极端情况下,野指针可能修改系统关键数据,导致系统崩溃。
2.4.2 产生原因
-
未初始化指针:
int *p;
-
指向已释放的内存:
int *p = malloc(sizeof(int)); free(p); *p = 10; // p 已经指向被释放的内存
-
指针越界:
int arr[5]; int *p = arr + 10; // 越界访问
2.4.3 防止野指针的措施
-
指针初始化: 定义指针时进行初始化。
int *p = NULL;
-
避免访问已释放的内存: 释放指针后,将指针置为
NULL
。free(p); p = NULL;
-
检查数组边界: 确保指针访问不越界。
2.5 空指针
空指针是指保存了地址值为零的指针。使用空指针可以避免一些野指针问题。
2.5.1 空指针的定义和使用
int *p = NULL; // 定义一个指针并初始化为空指针
-
访问空指针会导致段错误:
*p = 250; // 段错误
2.5.2 打印空指针
printf("%p - %d\n", NULL, NULL); // 输出: (nil) - 0
2.6 指针运算
指针可以进行加法和减法操作,这意味着地址的移动。
2.6.1 指针加法和减法
- 指针加法: 地址向上移动若干个目标类型的单位。
- 指针减法: 地址向下移动若干个目标类型的单位。
示例代码:
#include <stdio.h>
int main() {
long l;
long *p = &l;
int i;
int *p1 = &i;
printf("p: %p\n", p);
printf("p+1: %p\n", p + 1); // 向上移动一个 long 类型的大小
printf("p1: %p\n", p1);
printf("p1+1: %p\n", p1 + 1); // 向上移动一个 int 类型的大小
return 0;
}
运行结果:
p: 0x7fffcb862610
p+1: 0x7fffcb862618 // p 向上移动 8 字节(long 的大小)
p1: 0x7fffcb86260c
p1+1: 0x7fffcb862610 // p1 向上移动 4 字节(int 的大小)
注意事项:
- 指针类型决定了指针运算的单位: 指针在加减运算中,实际移动的大小取决于指针类型。
- 避免使用未初始化的指针: 未初始化的指针可能指向任意位置,导致不可预测的行为。
- 指针的类型和数据类型一致: 确保指针类型和所指向的数据类型一致,以避免错误的内存访问。