Skip to content

数组与字符串(Arrays & Strings)

数组用于存储一组相同类型的元素,字符串是 C 语言中以 \0 结尾的字符数组。它们是批量数据处理的基础工具。


1. 数组——同类型数据的集合

1.1 为什么需要数组

假设要存储 100 个学生的成绩,不用数组的结果是:

int s0, s1, s2, s3, ..., s99;   // 声明 100 个独立变量——不现实

用数组只需一行:

int scores[100];   // 100 个连续的 int 空间

1.2 声明与初始化

int arr[5];                              // 未初始化(元素为垃圾值)
int arr2[5] = {10, 20, 30, 40, 50};      // 全部指定
int arr3[5] = {1, 2, 3};                 // 部分指定,剩余自动补 0 → {1,2,3,0,0}
int arr4[]  = {1, 2, 3, 4};              // 自动推断大小为 4

数组在内存中是连续存放的:

arr2:  ┌────┬────┬────┬────┬────┐
       │ 10 │ 20 │ 30 │ 40 │ 50 │
       └────┴────┴────┴────┴────┘
索引:    0    1    2    3    4
地址:  低地址 → → → → → → → → 高地址

1.3 访问元素

int arr[5] = {10, 20, 30, 40, 50};

arr[0] = 100;          // 修改第 1 个元素
int x = arr[2];        // 读取第 3 个元素 → 30
printf("%d\n", arr[4]); // 输出最后一个元素 → 50
  • 索引从 0 开始,到 n-1 结束(n 为元素个数)。
  • 索引超出范围(如 arr[5]arr[-1])是未定义行为,编译器不报错但运行时会出问题。

1.4 获取数组长度

int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);  // 总字节数 / 单个元素字节数 = 元素个数
printf("数组长度:%d\n", length);            // 5

⚠️ 这种方法仅在数组定义的同一作用域内有效。将数组传给函数后,sizeof 返回的是指针大小而非数组大小(详见"函数"篇)。


2. 多维数组

int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

printf("%d\n", matrix[1][2]);   // 第 2 行第 3 列 → 7

内存布局(C 语言采用行优先存储):

逻辑视图:             内存中的实际排列(按行存放):
┌─────────────┐        ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
│ 1   2   3   4│        │1 │2 │3 │4 │5 │6 │7 │8 │9 │10│11│12│
│ 5   6   7   8│        └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
│ 9  10  11  12│        第0行      第1行      第2行
└─────────────┘
// 遍历二维数组
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        printf("%3d ", matrix[i][j]);
    }
    printf("\n");
}

3. 字符串——以 \0 结尾的字符数组

C 语言没有独立的字符串类型。字符串本质上是末尾带有 \0(空字符,ASCII 值 0)的 char 数组。

3.1 声明与初始化

char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};    // 手动逐字符
char str2[6] = "Hello";                              // 等价写法(编译器自动加 \0)
char str3[]  = "Hello";                              // 自动推断大小为 6(5 字符 + \0)

内存布局:

str3:  ┌───┬───┬───┬───┬───┬───┐
       │ H │ e │ l │ l │ o │\0 │
       └───┴───┴───┴───┴───┴───┘
索引:    0   1   2   3   4   5

\0 是字符串结束的标志。库函数(如 printfstrlen)靠它来确定字符串边界。如果缺少 \0,函数会一直向后读取直到碰巧遇到一个 0 字节,导致错误

3.2 使用 printfscanf

char name[50];
printf("请输入姓名:");
scanf("%49s", name);         // 限制最多读 49 字符,保留 1 位给 \0
printf("你好,%s\n", name);

⚠️ scanf("%s") 遇到空格就会停止读取。如需读取含空格的整行,用 fgetsc char line[100]; fgets(line, sizeof(line), stdin); // 安全读取一行


4. 常用字符串函数(<string.h>

#include <string.h>

char s1[50] = "Hello";
char s2[50] = "World";
char s3[50];

4.1 获取长度

size_t len = strlen(s1);       // 5(不包含 \0)

4.2 复制

strcpy(s3, s1);                // 将 s1 复制到 s3,包括 \0
strncpy(s3, s1, 3);           // 最多复制 3 个字符,需手动补 \0
s3[3] = '\0';

⚠️ strcpy 不检查目标缓冲区大小,如果源字符串更长会导致缓冲区溢出。优先使用 strncpysnprintf

4.3 拼接

strcat(s1, " ");               // s1 = "Hello "(确保 s1 有足够空间)
strcat(s1, s2);                // s1 = "Hello World"

4.4 比较

if (strcmp(s1, s2) == 0) { printf("相等\n"); }
if (strcmp(s1, s2) < 0)  { printf("s1 < s2\n"); }
if (strcmp(s1, s2) > 0)  { printf("s1 > s2\n"); }

⚠️ 不能用 == 比较字符串s1 == s2 比较的是两个数组的地址而非内容。必须用 strcmp


5. 字符处理函数(<ctype.h>

#include <ctype.h>

char ch = 'A';
isalpha(ch);    // 是否为字母 → 真
isdigit(ch);    // 是否为数字 → 假
isspace(ch);    // 是否为空白字符 → 假
islower(ch);    // 是否为小写 → 假
isupper(ch);    // 是否为大写 → 真
toupper('a');   // 转大写 → 'A'
tolower('Z');   // 转小写 → 'z'

6. 数组与字符串常见操作

6.1 反转数组

void reverse(int arr[], int n)
{
    for (int i = 0; i < n / 2; i++) {
        int temp = arr[i];
        arr[i] = arr[n - 1 - i];
        arr[n - 1 - i] = temp;
    }
}

6.2 字符串转数字

#include <stdlib.h>

int    a = atoi("42");        // 字符串 → int
double b = atof("3.14");      // 字符串 → double
long   c = atol("1000");      // 字符串 → long

7. 总结

要点 说明
数组索引从 0 开始 arr[0] 是第一个元素,arr[n-1] 是最后一个
不能越界访问 编译器不检查,运行时可能崩溃或产生错误结果
sizeof 获取数组大小 仅在数组定义的作用域内有效
字符串以 \0 结尾 分配空间时需额外留 1 字节
strcmp 比较字符串 == 只比较地址
优先用 strncpy/fgets 带长度限制的版本更安全
二维数组行优先存储 内层索引变化快,与外层循环对应

作者: Qing | 修改日期: 2026-07-18