C 语言数组进阶指南:二维数组、字符数组与字符串实战

C 语言数组进阶指南:二维数组、字符数组与字符串实战

上一篇我们详解了一维数组的基础用法,本篇将承接前文,深入数组进阶知识 ------ 涵盖二维数组的定义与应用、字符数组与字符串的本质区别,以及字符串常用函数的使用与手动实现,结合实战案例与避坑技巧,帮你彻底掌握数组的全场景用法。

一、二维数组:多维数据的存储与操作

二维数组本质是 "数组的数组",适用于存储表格化数据(如矩阵、学生成绩表),核心特点是 "行 + 列" 的二维索引结构。

1. 二维数组的定义与初始化

(1)定义格式

c

运行

ini 复制代码
类型 标识符[行长度][列长度];
  • 必须指定列长度,行长度可省略(由初始化内容自动推导);
  • 示例:int matrix[3][3];(3 行 3 列的整型矩阵)、int matrix[][3] = {1,2,3,4,5,6};(自动推导为 2 行 3 列)。
(2)初始化方式

二维数组支持多种初始化形式,核心规则与一维数组一致(未赋值元素默认补 0):

c

运行

less 复制代码
// 1. 连续赋值(按行优先存储)
int num1[3][3] = {1,2,3,4,5,6,7,8,9};

// 2. 分段赋值(按行分组,更直观)
int num2[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};

// 3. 部分初始化(未赋值元素补0)
int num3[3][3] = {{1,2}, {4}, {7,8,9}}; // 等价于{{1,2,0}, {4,0,0}, {7,8,9}}

// 4. 全0初始化(简洁高效)
int num4[3][3] = {0};

// 5. 省略行长度(由初始化内容推导)
int num5[][3] = {1,2,3,4,5,6}; // 推导为2行3列

2. 二维数组的实战应用:输入与输出

二维数组需通过 "外层循环控制行、内层循环控制列" 的双层循环操作,示例如下:

c

运行

arduino 复制代码
#include <stdio.h>

int main() {
    int num[3][3]; // 3行3列数组

    // 循环输入:行i从0~2,列j从0~2
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 3; j++) {
            printf("请输入第%d行第%d列数据:", i+1, j+1);
            scanf("%d", &num[i][j]); // 取第i行第j列元素的地址
        }
    }

    // 循环输出:按行打印
    printf("\n输入的3×3矩阵为:\n");
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 3; j++) {
            printf("%d\t", num[i][j]); // 制表符分隔,格式整洁
        }
        printf("\n"); // 每行结束换行
    }
    return 0;
}

3. 二维数组的空间计算

与一维数组一致,总空间大小 = 单个元素类型大小 × 行长度 × 列长度:

c

运行

perl 复制代码
int num[3][3];
// 总空间:4字节(int)×3×3=36字节
printf("总空间:%zu\n", sizeof(num)); 
// 一行的空间:4×3=12字节
printf("一行空间:%zu\n", sizeof(num[0])); 
// 单个元素空间:4字节
printf("单个元素空间:%zu\n", sizeof(num[0][0])); 

二、字符数组与字符串:本质与区别

字符数组是存储字符的一维数组,而字符串是 "以\0(字符串结束符)结尾的字符数组"------ 这是两者的核心区别。

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

c

运行

arduino 复制代码
// 1. 单个字符赋值
char ch1[5] = {'h', 'e', 'l', 'l', 'o'};

// 2. 字符串简化赋值(自动添加'\0')
char ch2[6] = "hello"; // 需预留'\0'的空间(5个字符+1个结束符)

// 3. 省略长度(自动推导为6,包含'\0')
char ch3[] = "hello"; 
  • 关键:用""赋值时,编译器会自动在末尾添加\0,数组长度需比字符数多 1,否则会导致字符串溢出。

2. 字符串的核心:\0结束符

\0是 ASCII 码为 0 的特殊字符,用于标记字符串结束,无实际显示效果:

  • 示例:char ch[6] = "hello"的内存存储为h e l l o \0
  • 若缺少\0char ch[5] = "hello"(无\0),使用%s输出时会超出数组范围,打印垃圾值。

3. 字符串的两种操作方式

(1)数组下标操作(逐字符处理)

c

运行

perl 复制代码
char ch[] = "hello";
// 循环遍历每个字符(直到'\0'结束)
for(int i = 0; ch[i] != '\0'; i++) {
    printf("%c", ch[i]); // 输出:hello
}
(2)字符串格式操作(%s直接处理)

c

运行

perl 复制代码
char ch[] = "hello";
// %s从数组首地址开始,直到'\0'结束
printf("%s\n", ch); // 输出:hello

三、字符串常用函数(string.h

C 语言提供string.h头文件,包含字符串长度计算、复制、拼接、比较等常用函数,以下是核心函数的用法与手动实现(加深理解)。

1. 计算字符串长度:strlen

  • 功能:返回字符串的实际长度(不包含\0);
  • 用法:size_t strlen(const char *str);
  • 示例与手动实现:

c

运行

arduino 复制代码
#include <stdio.h>
#include <string.h> // strlen需要包含该头文件

// 手动实现strlen
int my_strlen(char *str) {
    int len = 0;
    // 遍历到'\0'结束
    while(str[len] != '\0') {
        len++;
    }
    return len;
}

int main() {
    char ch[] = "hello";
    // 库函数实现:返回5
    printf("库函数长度:%zu\n", strlen(ch)); 
    // 手动实现:返回5
    printf("手动实现长度:%d\n", my_strlen(ch)); 
    return 0;
}

2. 字符串复制:strcpy

  • 功能:将源字符串复制到目标字符串(覆盖目标字符串);
  • 用法:char *strcpy(char *dest, const char *src);
  • 注意:目标字符串需有足够空间,否则会溢出;
  • 手动实现:

c

运行

scss 复制代码
// 手动实现strcpy:dest接收方,src提供方
void my_strcpy(char *dest, char *src) {
    int i = 0;
    // 复制字符直到src的'\0'
    while(src[i] != '\0') {
        dest[i] = src[i];
        i++;
    }
    dest[i] = '\0'; // 手动添加结束符
}

int main() {
    char src[] = "hello";
    char dest[10] = {0};
    my_strcpy(dest, src);
    printf("复制结果:%s\n", dest); // 输出:hello
    return 0;
}

3. 字符串拼接:strcat

  • 功能:将源字符串拼接在目标字符串末尾;
  • 用法:char *strcat(char *dest, const char *src);
  • 手动实现:

c

运行

scss 复制代码
// 手动实现strcat
void my_strcat(char *dest, char *src) {
    int i = 0, j = 0;
    // 找到dest的'\0'位置
    while(dest[i] != '\0') {
        i++;
    }
    // 拼接src的字符
    while(src[j] != '\0') {
        dest[i] = src[j];
        i++;
        j++;
    }
    dest[i] = '\0'; // 补结束符
}

int main() {
    char dest[20] = "hello";
    char src[] = " world!";
    my_strcat(dest, src);
    printf("拼接结果:%s\n", dest); // 输出:hello world!
    return 0;
}

4. 字符串比较:strcmp

  • 功能:按 ASCII 码比较两个字符串,返回差值;
  • 用法:int strcmp(const char *str1, const char *str2);
  • 返回值:0(相等)、>0(str1>str2)、<0(str1<str2);
  • 手动实现:

c

运行

arduino 复制代码
// 手动实现strcmp
int my_strcmp(char *str1, char *str2) {
    int i = 0, res = 0;
    while(str1[i] != '\0' || str2[i] != '\0') {
        res = str1[i] - str2[i];
        // 差值不为0,直接返回
        if(res != 0) {
            break;
        }
        i++;
    }
    return res;
}

int main() {
    char str1[] = "hello";
    char str2[] = "hellow";
    printf("比较结果:%d\n", my_strcmp(str1, str2)); // 输出:-119('o'-'w'的ASCII差值)
    return 0;
}

5. 字符串输入:scanffgets

  • scanf("%s", str):遇到空格或回车结束,无法输入含空格的字符串;
  • fgets(str, size, stdin):支持输入含空格的字符串,size为最大读取长度(含\0),推荐使用:

c

运行

scss 复制代码
char str[1024] = {0};
// 读取最多1023个字符(预留'\0')
fgets(str, 1024, stdin); 
printf("输入内容:%s", str); // 可输出含空格的字符串

四、数组进阶避坑指南

  1. 二维数组行 / 列缺失错误 :定义时必须指定列长度,如int num[][3]合法,int num[3][]非法;
  2. 字符串结束符遗漏 :手动赋值字符数组时,需手动添加\0,否则%s输出异常;
  3. 字符串函数溢出风险strcpystrcat需确保目标字符串空间足够,建议提前计算长度;
  4. gets函数禁用gets无长度限制,易导致溢出,已被淘汰,改用fgets
  5. 字符数组与字符串混淆char ch[5] = "hello"(无\0)不是合法字符串,需定义为char ch[6]

五、数组应用场景总结

  • 一维数组:批量存储同类型数据(如成绩、编号),配合循环实现排序、查找;
  • 二维数组:存储表格化数据(如矩阵、二维坐标),适用于数学运算、数据统计;
  • 字符数组 / 字符串:处理文本数据(如用户名、描述信息),依赖string.h函数实现高效操作。

总结

数组是 C 语言处理批量数据的核心工具,从一维到二维、从普通数组到字符数组,其核心逻辑始终是 "连续内存 + 索引访问 + 循环操作"。掌握二维数组的行名列操作、字符串的\0结束符规则,以及常用字符串函数的原理,能解决大部分数据存储与处理场景。新手需重点规避 "下标越界""结束符遗漏""空间溢出" 三大坑,通过多练习排序、字符串拼接等实战案例,可快速夯实数组进阶基础。

相关推荐
点云SLAM1 分钟前
凸优化(Convex Optimization)理论(1)
人工智能·算法·slam·数学原理·凸优化·数值优化理论·机器人应用
jz_ddk20 分钟前
[学习] 卫星导航的码相位与载波相位计算
学习·算法·gps·gnss·北斗
放荡不羁的野指针31 分钟前
leetcode150题-动态规划
算法·动态规划
+VX:Fegn089531 分钟前
计算机毕业设计|基于springboot + vue在线音乐播放系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
sin_hielo34 分钟前
leetcode 1161(BFS)
数据结构·算法·leetcode
一起努力啊~34 分钟前
算法刷题-二分查找
java·数据结构·算法
水月wwww1 小时前
【算法设计】动态规划
算法·动态规划
code bean1 小时前
Flask图片服务在不同网络接口下的路径解析问题及解决方案
后端·python·flask
+VX:Fegn08951 小时前
计算机毕业设计|基于springboot + vue律师咨询系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
努力的小郑1 小时前
2025年度总结:当我在 Cursor 里敲下 Tab 的那一刻,我知道时代变了
前端·后端·ai编程