C语言基本笔记(9)—— 数组和指针

以下是C语言的C99标准中有关数组和指针的一些理论。

数组

变长数组(VLA,Variable-Length Array)

  • 定义:允许数组长度在运行时确定(需编译器支持)。
  • 示例
    int n = 10;
    int arr[n];  // 合法(C99支持)
    
  • 限制
    • 不能初始化(如 int arr[n] = {0}; 非法)。
    • 作用域内长度不可变。
    • C11中变为可选特性,需注意编译器和平台兼容性。

数组类型退化(Decay)

  • 数组名在大多数表达式中退化为指向首元素的指针(例外:sizeof(arr)&arr)。
  • 示例
    int arr[3] = {1, 2, 3};
    int *p = arr;      // 等价于 int *p = &arr[0];
    size_t size = sizeof(arr); // 返回数组总字节数(非指针大小)
    

指针的核心特性(C99标准)

  1. restrict 关键字

    • 用途:声明指针是访问数据的唯一方式,帮助编译器优化。
    • 示例
      void copy(int *restrict dest, const int *restrict src, int n);
      
  2. 指针算术与数组遍历

    • 指针算术规则
      int arr[5];
      int *p = arr;
      p++;  // 指向arr[1](地址增加sizeof(int))
      
    • 遍历优化
      for (int *p = arr; p < arr + 5; p++) {
          printf("%d ", *p);
      }
      
  3. 动态多维数组

    • 堆内存分配
      int rows = 3, cols = 4;
      int **matrix = malloc(rows * sizeof(int*));
      for (int i = 0; i < rows; i++) {
          matrix[i] = malloc(cols * sizeof(int));
      }
      
    • 连续内存分配(推荐)
      int (*matrix)[cols] = malloc(rows * sizeof(*matrix)); // C99支持
      

数组与指针的关系

  1. 数组名与指针的区别

    特性 数组名 指针
    内存分配 静态分配(栈或全局区) 动态或静态(指向其他内存)
    sizeof 结果 数组总字节数 指针变量大小(如4/8字节)
    可修改性 不可重新赋值 可指向其他地址
  2. 函数参数传递

    • 数组作为函数参数时退化为指针:
      void func(int arr[]);  // 等价于 void func(int *arr);
      
    • 传递多维数组需明确列数(C99支持变长):
      void func(int rows, int cols, int mat[rows][cols]);
      

字符数组与字符串的关系

1. 字符数组的定义与初始化

  • 字符数组存储字符串
    char str1[] = "Hello";     // 自动包含'\0',大小为6
    char str2[6] = "Hello";    // 等价于str1
    char str3[5] = "Hello";    // 错误!缺少空间存放'\0'
    char str4[5] = {'H', 'e', 'l', 'l', 'o'}; // 合法,但非字符串(无'\0')
    
    注意事项
    • 若字符数组用于存储字符串,必须预留空间给结尾的 '\0'
    • 未显式初始化的字符数组内容不确定,需手动添加 '\0' 才能作为字符串使用。

2. 字符串字面量初始化

  • 隐式添加 '\0'
    char str[] = "abc"; // 等价于 {'a', 'b', 'c', '\0'}
    
    • 若数组大小不足,会静默截断且可能不添加 '\0'
      char str[3] = "abc"; // 错误!未包含'\0',可能导致越界
      

3. 字符数组与字符串的操作

  • 字符串函数依赖 '\0'

    char str[5] = "Hello"; // 错误!越界(实际需要6字节)
    printf("%s", str);     // 可能导致未定义行为
    

    正确做法

    char str[6] = "Hello"; // 正确,包含'\0'
    printf("%s", str);     // 安全
    
  • 手动添加 '\0'

    char str[5] = {'H', 'e', 'l', 'l', 'o'};
    str[4] = '\0'; // 手动终止,但数组长度不足,仍会越界!
    

常见错误

1. 数组越界访问

int arr[3] = {1, 2, 3};
printf("%d", arr[3]); // 越界访问,未定义行为
  • 解决方法:始终检查索引范围(0 <= index < size)。

2. 混淆数组与指针

int arr[3] = {1, 2, 3};
int *p = arr;
printf("%zu", sizeof(arr)); // 输出12(假设int为4字节)
printf("%zu", sizeof(p));   // 输出指针大小(如4或8)

3. 字符串初始化陷阱

char str[5];
strcpy(str, "Hello"); // 错误!目标缓冲区不足(需要6字节)

4. 变长数组(VLA)的局限性

  • VLA不能用于全局变量或静态变量:
    int n = 10;
    static int vla[n]; // 错误!VLA不能是静态的
    

5. 多维数组的内存布局

  • 多维数组是连续存储的,但动态分配时需注意内存连续性:
    int (*matrix)[cols] = malloc(rows * sizeof(*matrix)); // 连续内存(C99)
    
posted @ 2025-04-16 21:42  故渊Y  阅读(11)  评论(0)    收藏  举报