指针的分析与理解

指针的分析与理解

    int a = 5;
	printf("a的地址为:%p, a的值为:%d\n",&a, a);
	其中&a指的是变量a在内存中的地址,a是变量的值。&是取值符号。

1. 关于指针的理解

int a = 5;
int *p;    // p 是未初始化的指针,指向随机内存地址
*p = a;    // 试图将 a 的值写入 p 指向的随机地址(危险!)

分析这段错误的代码
不要以为声明指针后就可以直接使用,必须需要先让指针指向有效的内存区域。
比如用p = &a; 或者动态分配内存。

  • 永远不要对未初始化的指针解引用(即 *p)。

  • 指针必须指向以下之一才能安全使用:

    1. 已存在变量的地址(如 p = &a)。
    2. 动态分配的内存(如 p = malloc(...))。
    3. 其他合法的内存区域(如数组的首地址)。
  • 关键问题:指针 p 未初始化,它存储的是一个随机的内存地址(垃圾值)。直接通过 *p = a 向这个随机地址写入数据,可能导致以下后果:

    • 覆盖其他程序或操作系统的重要数据(段错误,程序崩溃)。
    • 破坏内存,引发不可预测的行为(未定义行为)。

2. 正确用法

方法 1:让 p 指向合法内存地址
int a = 5;
int *p = &a; // p 指向变量 a 的地址
*p = 10;     // 合法操作:修改 a 的值为 10
方法 2:动态分配内存
int a = 5;
int *p = malloc(sizeof(int)); // 动态分配内存
if (p != NULL) {
    *p = a;   // 合法操作:将 a 的值写入动态分配的内存
    free(p);  // 使用后释放内存
}

3. 你的代码 vs 正确代码

你的代码 正确代码 区别
int *p;(未初始化) int *p = &a; 指针必须指向有效内存地址
*p = a;(危险操作) *p = a;(安全操作) 只有在 p 初始化后合法
总结来说,也就是我初始化指针以后,必须要有一个合法的内存地址,不然就是一个随机的,在进行赋值的时候,就会出现上述错误。
- 覆盖其他程序或操作系统的重要数据(段错误,程序崩溃)。 
- 破坏内存,引发不可预测的行为(未定义行为)。

4. 为什么代码错误?

  • 未初始化的指针类似于一个“野指针”,它指向的内存地址可能是:

    • 受保护的操作系统内存区域(导致程序崩溃)。

    • 其他变量或数据的内存地址(导致数据被意外覆盖)。

    • 无效地址(直接引发段错误)。


5. 实际例子

假设内存布局如下:

int a = 5;         // 地址 0x1000,值 5
int *p;            // 存储 p 的地址是 0x2000,值未初始化(假设是 0xABCD)
  • 你的代码 *p = a 会尝试将 5 写入地址 0xABCD,而 0xABCD 可能属于其他程序或无效内存,导致崩溃。
  • 属于是强行改变某一个未初始化的内存空间,通过 *p = a 改变这一个未初始化的内存空间的值,这样会导致错误。

6. 总结

  • 永远不要对未初始化的指针解引用(即 *p)。

  • 指针必须指向以下之一才能安全使用:

    1. 已存在变量的地址(如 p = &a)。

    2. 动态分配的内存(如 p = malloc(...))。

    3. 其他合法的内存区域(如数组的首地址)。
      建议修正你的代码为:

int a = 5;
int *p = &a;  // 先让 p 指向合法地址
*p = a;       // 安全操作


理解 int *p

	int *p;
	
	printf("%p\n", p);
	
	printf("%p\n", &p);

在 C/C++ 语言中,int *p; 的含义是:声明一个指向整型(int)数据的指针变量 p。以下是详细解释:
在 C/C++ 中,指针自身的地址(即 &p)是指针变量 p 在内存中存储的位置。理解这一概念需要区分以下两个关键点:

1. 指针的两个核心概念

  • p(指针的值):指针变量 p 存储的是它指向的变量的内存地址(例如 int x = 10; p = &x;,此时 p 的值是 x 的地址)。

  • &p(指针自身的地址):指针变量 p 自己作为一个变量,在内存中也有一个存储位置,这个位置的地址就是 &p

这里说白了就是内存地址,保存在一个变量中,而我们给这个变量起了一个名字叫指针,我们更多的是通过内存的操作,在某种程度上去操作一些数据。指针本身也需要一个地方去存储,因此就是指针的地址(这个内存空间一般是不需要操作的,因为是用来存储一些地址(指针的值)的,)。


2. 通过例子理解

示例代码
#include 

int main() {
    int x = 10;       // 假设 x 的地址是 0x1000
    int *p = &x;      // p 存储的是 x 的地址(0x1000)

    printf("x 的地址(p 的值):%p\n", (void*)p);   // 输出 0x1000
    printf("指针 p 自身的地址(&p):%p\n", (void*)&p); // 输出 0x2000(假设 p 的地址是 0x2000)

    return 0;
}
    int x = 10;

    int y = 20;

    int *p = &x;        // p 指向 x

    int **pp = &p;      // pp 存储 p 的地址(即 &p)

    // 输出变量 x 的地址
    printf("变量 x 的地址: %p\n", &x);

    // 指针 p 存储的地址,即变量 x 的地址
    printf("指针 p 存储的地址,即变量 x 的地址: %p\n", p);
    
    // 输出指针 p 所指向变量的值,即变量 x 的值
    printf("指针 p 所指向变量的值: %d\n", *p);  

    // 输出 存储指针p的地址
    printf("存储指针 p 的地址: %p\n", &p);

    // 输出指针 pp 存储的地址,即变量 p 的地址
    printf("指针 pp 存储的地址,也就是存储指针 p 的地址: %p\n", pp);
  
    // 存储指针 pp 的地址,
    printf("存储指针 pp 的地址: %p\n", &pp);
 
    *pp = &y;           // 通过 pp 修改 p 的指向,现在 p 指向 y
    // 输出变量 y 的地址
    printf("变量 y 的地址: %p\n", &y);

    // 输出指针 pp 存储的地址,即变量 y 的地址
    printf("指针 pp 存储的地址: %p\n", p);
内存布局
变量 内存地址(假设值) 存储的内容
x 0x1000 10
p 0x2000 0x1000(x 的地址)
  • p 的值0x1000(指向 x 的地址)。

  • &p 的值0x2000(指针变量 p 自身的内存地址)。


3. 指针自身的地址有什么用?

用途 1:动态修改指针的指向

可以通过二级指针(指向指针的指针)修改 p 的指向:

int x = 10;
int y = 20;
int *p = &x;        // p 指向 x
int **pp = &p;      // pp 存储 p 的地址(即 &p)

*pp = &y;           // 通过 pp 修改 p 的指向,现在 p 指向 y
printf("%d\n", *p); // 输出 20

用途 2:函数参数传递

如果需要在函数内部修改指针的指向(如动态内存分配),需要传递指针的地址:

void allocate_memory(int **ptr) {
    *ptr = malloc(sizeof(int)); // 通过二级指针修改外部指针的值
}

int main() {
    int *p;
    allocate_memory(&p); // 传递 p 的地址
    *p = 100;
    free(p);
    return 0;
}

4. 常见误区

误区 1:混淆 p&p 的类型
  • p 的类型是 int*(指向整型的指针)。

  • &p 的类型是 int**(指向指针的指针)。

误区 2:未初始化的指针的地址

即使指针未初始化,&p 仍然是合法的:

int *p; // 未初始化
printf("%p\n", (void*)&p); // 合法,输出 p 自身的地址(如 0x2000)
printf("%p\n", (void*)p);  // 危险!p 的值是未定义的(垃圾值)

5. 总结

  • 指针的值(p:指向某个数据的内存地址。

  • 指针自身的地址(&p:指针变量本身在内存中的位置。

  • 关键区别

int x = 10;
int *p = &x;

// p 是 x 的地址(0x1000)
// &p 是 p 自身的地址(0x2000)

理解这一概念是掌握指针操作(如动态内存管理、函数传参)的基础。



进一步深层次理解

1. 语法分解

  • int:表示指针指向的数据类型是整型(int)。

  • *:表示这是一个指针变量。

  • p:指针变量的名称。

因此,int *p; 可以理解为:
p 是一个指针,它能存储某个 int 类型变量的内存地址”

指针也就是地址,地址也就是指针。


2. 指针的默认值

  • 未初始化的指针(如你的代码):
    指针 p 的值是未定义的(即“野指针”),它可能指向内存中的任意地址(垃圾值)。
    直接操作未初始化的指针(如 *p = 5;)会导致 未定义行为(程序崩溃、数据损坏等)。

  • 安全用法:始终初始化指针:

int x = 10;
int *p = &x;  // p 指向变量 x 的地址

3. 指针的作用

指针 p 的核心作用是 间接访问内存

  • 通过 p = &x; 可以存储变量 x 的地址。

  • 通过 *p 可以读写 x 的值(即解引用)。

int x = 10;
int *p = &x;  // p 存储 x 的地址

printf("x 的值:%d\n", x);    // 输出 10
printf("通过 p 访问 x:%d\n", *p); // 输出 10

*p = 20;  // 通过 p 修改 x 的值
printf("修改后 x 的值:%d\n", x); // 输出 20

4. 指针的大小

  • 指针的大小与它指向的数据类型无关,只与操作系统位数有关:

    • 32 位系统:指针占 4 字节(如存储地址 0x7fff2a01b3dc)。

    • 64 位系统:指针占 8 字节(如存储地址 0x7ffee34a9b3c)。


5. 常见误解

误区 1:int* p, q;pq 都是指针?
  • 错误q 是普通 int 变量,只有 p 是指针。

  • 正确写法:

int *p, *q;  // p 和 q 都是指针
误区 2:* 符号是变量名的一部分?
  • 错误* 符号属于类型声明,不是变量名。
    以下两种写法等价:
int* p;    // 强调类型是 int*
int *p;    // 强调变量 p 是指针(更常见写法)

6. 指针的典型用途

  • 动态内存分配
int *p = malloc(sizeof(int)); // 动态分配内存
*p = 100;
free(p);  // 释放内存
  • 函数参数传递(修改实参)
void change(int *p) { *p = 20; }

int x = 10;
change(&x);  // x 的值变为 20
  • 操作数组
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;  // p 指向数组首元素
printf("%d\n", p[0]); // 输出 1

7. 注意事项

  • 避免野指针:未初始化的指针要置空:
int *p = NULL;  // 安全初始化
  • 避免悬空指针:释放内存后及时置空:
free(p);
p = NULL;  // 防止误用已释放的内存

总结

  • int *p; 声明了一个整型指针变量 p,它专门用来存储 int 类型变量的内存地址。

  • 指针的核心是 间接访问内存,需初始化后才能安全使用。


你可能感兴趣的:(数据结构,c语言,c++,单片机,stm32,51单片机)