# 深入理解 C 语言数组

一、数组的概念

数组是相同类型数据的有序集合,可用于批量存储、管理同类型数据,比如用 int 类型数组存班级学生成绩,用 char 类型数组存字符串 。

二、一维数组的创建、初始化与使用

2.1 一维数组创建

语法:类型 数组名[常量表达式];,如:

// 存 5 个 int 型数据,元素默认随机值 
int arr1[5]; 
// 存 10 个 char 型数据 
char arr2[10]; 
  • 常量表达式:可用#define定义的符号常量或字面常量(如510 ),C99 支持变长数组(后续讲),但旧编译器可能不支持。

2.2 一维数组初始化

  • 静态初始化:创建时直接赋值,如:
// 明确元素值,依次为 1、2、3、4、5 
int arr1[5] = {1, 2, 3, 4, 5}; 
// 只给前 2 个赋值,剩余元素默认 0 
int arr2[5] = {1, 2}; 
  • 动态初始化:依赖输入或程序运行中赋值,如:
int arr[5];
// 从键盘给数组赋值 
for (int i = 0; i < 5; i++) {
    scanf("%d", &arr[i]); 
}

2.3 一维数组使用

  • 数组下标:数组下标从 0 开始,范围是 0 ~ 数组长度 - 1 ,通过 数组名[下标] 访问元素,如:
int arr[5] = {1, 2, 3, 4, 5};
// 访问第 3 个元素(值为 3 )
printf("%d\n", arr[2]); 
  • 数组元素打印:遍历数组打印元素:
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
    // 依次打印 1 2 3 4 5 
    printf("%d ", arr[i]); 
}
  • 数组输入:通过循环结合 scanf 赋值:
int arr[3];
for (int i = 0; i < 3; i++) {
    // 输入 3 个数存到数组 
    scanf("%d", &arr[i]); 
}

三、一维数组内存存储与 sizeof 应用

3.1 内存存储

数组元素在内存连续存放,地址随下标增大而增大,且每个元素占字节数由类型决定(如 int 占 4 字节 )。代码验证:

int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
    // 打印元素地址,差值为 4(int 占 4 字节) 
    printf("&arr[%d] = %p\n", i, &arr[i]); 
}

3.2 sizeof 计算元素个数

公式:元素个数 = sizeof(数组名) / sizeof(数组元素类型) ,示例:

int arr[5] = {1, 2, 3, 4, 5};
// 总字节数 / 单个 int 字节数 → 5 
int count = sizeof(arr) / sizeof(int); 
printf("元素个数:%d\n", count); 
  • 注意:数组名做函数参数时,传递的是地址,sizeof(数组名) 会退化为计算指针大小(通常 4/8 字节,取决于系统)。

四、二维数组的创建、初始化与使用

4.1 二维数组概念与创建

二维数组可看作数组的数组,用于存储表格类数据(如行表示学生,列表示科目成绩 )。
创建语法:类型 数组名[行常量表达式][列常量表达式]; ,如:

// 3 行 4 列,存 int 型数据 
int arr[3][4]; 

4.2 二维数组初始化

  • 不完全初始化:未完全赋值时,剩余元素默认 0 ,如:
// 第一行:1、2、3、0;第二行:4、5、0、0;第三行:0、0、0、0 
int arr[3][4] = {{1, 2, 3}, {4, 5}}; 
  • 完全初始化:全部元素赋值,如:
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}}; 
  • 按行初始化:明确每行元素,如上述示例,是常用方式。
  • 省略行初始化:可省略行数,编译器自动根据元素数量计算行数,如:
// 自动算 2 行(6 个元素 ÷ 3 列 )
int arr[][3] = {{1, 2, 3}, {4, 5, 6}}; 
  • 列不能省略:因元素按行存储,需列数确定每行布局。

4.3 二维数组使用

  • 下标访问:通过 数组名[行下标][列下标] 访问,行、列下标均从 0 开始,如:
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
// 访问第二行第三列元素(值为 6 ) 
printf("%d\n", arr[1][2]); 
  • 输入输出:嵌套循环实现,示例:
int arr[2][3];
// 输入 
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        scanf("%d", &arr[i][j]); 
    }
}
// 输出 
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        printf("%d ", arr[i][j]); 
    }
    printf("\n"); 
}

五、二维数组内存存储

二维数组在内存连续存储,按行优先方式排布,即先存第一行元素,再存第二行…… 可通过打印地址验证:

int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        // 地址连续,差值为 4(int 占 4 字节) 
        printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]); 
    }
}

六、C99 变长数组(VLA)

C99 支持创建时用变量指定数组长度(运行时确定大小 ),如:

int n;
scanf("%d", &n);
// 变长数组,长度由 n 决定 
int arr[n]; 
  • 特点:
    • 不能初始化(编译时未知大小,无法赋值 );
    • 作用域结束后释放内存;
    • 旧编译器(如 MSVC )可能不支持,需确认环境兼容性。

七、数组实战练习

7.1 练习 1:二分查找(查找目标值下标)

需求:在有序(升序)数组中,用二分法找目标值,找到返回下标,否则返回 -1 。
代码

#include 
int binarySearch(int arr[], int size, int target) {
    int left = 0;
    int right = size - 1;
    while (left <= right) {
        // 防止溢出,等价 (left + right)/2 
        int mid = left + (right - left) / 2; 
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            // 目标在右半段 
            left = mid + 1; 
        } else {
            // 目标在左半段 
            right = mid - 1; 
        }
    }
    return -1;
}
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int target = 7;
    int index = binarySearch(arr, sizeof(arr)/sizeof(int), target);
    if (index != -1) {
        printf("找到,下标:%d\n", index);
    } else {
        printf("未找到\n");
    }
    return 0;
}

关键逻辑

  • 利用有序数组特性,通过 mid 分割区间,缩小查找范围;
  • 处理 leftright 边界,确保覆盖所有可能下标。

7.2 练习 2:字符从两端向中间移动(动态展示字符串变化)

需求:将字符串内容从两端向中间逐步替换到另一个字符串,动态展示过程(类似动画效果 )。
代码

#include 
#include 
#include   // 用于 Sleep 和 system 函数
int main() {
    char arr1[] = "welcome to bit!!!!!!";
    char arr2[] = "******************";
    int left = 0;
    int right = strlen(arr1) - 1;

    while (left <= right) {
        arr2[left] = arr1[left];
        arr2[right] = arr1[right];
        // 打印当前 arr2 状态
        printf("%s\n", arr2); 
        // 暂停 100 毫秒,营造动态效果
        Sleep(100); 
        // 清理屏幕,下次输出覆盖
        system("cls"); 
        left++;
        right--;
    }
    // 最后再打印一次最终结果(避免被清屏)
    printf("%s\n", arr2); 
    return 0;
}

动态效果实现

  • Sleep(100) 控制每次更新间隔,让变化肉眼可见;
  • system("cls") 清屏,使每次输出位置固定,模拟动画;
  • 注意:system("cls") 在 Windows 环境生效,Linux/Mac 需替换为 system("clear")

八、重要知识点总结

知识点 核心要点 易错点/注意事项
数组创建 类型 + 数组名 + 常量表达式(C99 支持变长数组 ) 变长数组不能初始化,旧编译器可能不支持
数组初始化 静态、动态初始化,可不完全赋值(剩余默认 0 ) 字符数组初始化字符串时,注意 '\0' 自动添加(如 char str[] = "abc" 实际存 'a' 'b' 'c' '\0'
数组下标 从 0 开始,范围 0 ~ 长度-1 下标越界无编译报错,但会引发运行时未定义行为(如修改未知内存 )
sizeof 计算 可算数组总字节数、元素个数;数组传参后失效 函数传数组本质传地址,sizeof(数组名) 结果是指针大小(4/8 字节 )
二维数组操作 行优先存储,支持省略行数初始化;通过双下标访问 初始化时列数必须明确,否则无法确定元素布局;嵌套循环处理输入输出时,注意行列边界
二分查找 依赖有序数组,通过缩小范围查找;需处理边界和 mid 计算 数组无序时不能用;left <= right 条件不能漏,否则漏判元素;mid 计算用 left + (right-left)/2 防溢出
动态字符串效果 结合 Sleep 和清屏函数,实现可视化过程 清屏函数(cls/clear )依赖操作系统,跨平台需适配;注意最终结果打印避免被清屏覆盖

通过对数组基础语法、内存布局、实战练习的梳理,相信你已掌握数组核心用法。多结合实际场景(如数据统计、算法实现 )练习,就能更灵活地运用数组解决问题啦~ 后续可拓展学习数组排序(冒泡、选择等 )、多维数组复杂操作等内容 。

你可能感兴趣的:(# 深入理解 C 语言数组)