完结】【CS50精翻·中文语音】哈佛大学《计算机科学导论》(2023)

🔗资源链接

4 - Memory_cn_哔哩哔哩_bilibili

✍我的笔记

一、内存的本质

  • 所有变量都存储在内存中,每个字节对应唯一地址(如房间号)
  • %p 格式化符专门用于打印地址值,如:
    int n = 42;
    printf("n 的地址:%p\n", &n); // 输出类似 0x7ffee3a5c78c
  • 十六进制表示(0 x 前缀)是内存地址的标准格式,如 0 x 1 A 3 F 对应十进制 6719
  • %p,&n 可以打印出内存地址

二、指针的哲学

  1. 指针即地址

    int *p = &n;  // p 存储n的地址
    • 指针变量大小固定(32 位系统 4 字节,64 位系统 8 字节)
    • 类型声明 int* 告知编译器如何解析内存内容
  2. 解引用操作

    *p = 100;  // 通过指针修改原始变量
    printf("%d", *p); // 读取指针指向的值
  3. 多级指针

    int **pp = &p;  // 指向指针的指针

三、数组与指针的量子纠缠

  1. 数组名本质

    int arr[5] = {1,2,3,4,5};
    printf("%p\n", arr);  // 输出首元素地址
    • arr[i] 等价于 *(arr + i)(编译器自动转换)
  2. 关键区别

    特性数组指针
    sizeof返回总字节数返回指针大小
    赋值操作不可整体赋值可修改指向
    地址常量是(无法改变)否

四、字符串的真相

  1. 存储本质

    char *str = "Hello"; // 实际内存:H e l l o \0
    • 空字符 \0(ASCII 0)作为终止符
    • 字符串字面量存储在只读段,修改会导致段错误
  2. 字符访问

    printf("%c %c", str[0], *(str+1)); // 输出 H e
  3. 动态字符串示例

    char arr[] = {'W','o','r','l','d','\0'}; // 可修改的字符数组

五、内存操作的危险性

  1. 越界访问示例

    int arr[3] = {1,2,3};
    printf("%d", arr[5]); // 访问未分配内存
  2. 缓冲区溢出攻击原理

    char buffer[4];
    gets(buffer); // 输入超过3字符将覆盖其他内存区域

六、高级类型操作

  1. typedef 创建类型别名

    typedef unsigned char byte;  // 定义新类型
    typedef int (*Comparator)(int, int); // 函数指针类型
  2. 结构体指针

    typedef struct {
        int x;
        int y;
    } Point;
     
    Point p = {3,4};
    Point *ptr = &p;
    printf("坐标:(%d,%d)", ptr->x, (*ptr).y);

七、安全编程实践

  1. 指针初始化原则

    int *p = NULL;  // 未使用时初始化为空
    if(p != NULL) {
        *p = 42;    // 安全访问检查
    }
  2. 现代替代方案

    • 使用 C++的智能指针(unique_ptr 等)
    • 优先使用标准库安全函数(如 strncpy 代替 strcpy)

八、内存布局可视化

高地址
┌─────────────┐
│   栈区       │ ← 局部变量(自动管理)
├─────────────┤
│   堆区       │ ← malloc分配(手动管理)
├─────────────┤
│ 全局/静态数据区 │ 
├─────────────┤
│   代码区      │ ← 字符串字面量(只读)
低地址

理解指针是掌握 C 语言的核心关键。这种直接操作内存的能力既是 C 语言强大性能的源泉,也是其危险性的根本。现代编程中,建议在需要底层控制时才使用指针,其他情况优先考虑更安全的抽象机制。 知道了指针后,可以探索所有计算机中的内存,也是黑客所利用的计数,通过计算机来运行代码,私处查看(这也是 C 语言的恐怖之处)