1.4. 数组与字符串
数组和字符串是 C 语言中最基础的数据集合形式。
1.4.1. 一维数组
1.4.1.1. 定义与初始化
// 声明并初始化
int arr[5] = {1, 2, 3, 4, 5};
// 部分初始化(未指定元素默认为 0)
int arr2[5] = {1, 2}; // {1, 2, 0, 0, 0}
// 由编译器推断大小
int arr3[] = {1, 2, 3, 4}; // 大小 = 4
// 不初始化(值不确定)
int arr4[10];
// 全部初始化为 0
int arr5[5] = {0};
1.4.1.2. 访问与遍历
int arr[5] = {10, 20, 30, 40, 50};
// 索引访问(从 0 开始)
printf("%d\n", arr[0]); // 10
printf("%d\n", arr[2]); // 30
// 修改元素
arr[1] = 25;
// 遍历
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
警告
C 不检查数组越界!访问 arr[5] 或 arr[-1] 不会报编译错误,
但会访问到内存中相邻位置的数据,产生未定义行为。
int arr[3] = {1, 2, 3};
arr[3] = 100; // 越界!可能破坏其他变量或导致崩溃
1.4.1.3. 数组名与指针的关系
int arr[5] = {1, 2, 3, 4, 5};
// 数组名是首元素地址(常量指针)
printf("%p\n", arr); // 数组首地址
printf("%p\n", &arr[0]); // 同上
printf("%d\n", *arr); // 1(对首地址解引用)
// 数组名不是指针变量,不能赋值
// arr = &something; // 编译错误!
1.4.1.4. 获取数组长度
int arr[] = {2, 4, 6, 8, 10};
// 方法:总字节数 ÷ 单个元素字节数
int length = sizeof(arr) / sizeof(arr[0]);
printf("数组长度: %d\n", length); // 5
小心
当数组作为函数参数传递时,会退化为指针,sizeof 不再有效:
void print_len(int arr[]) {
// arr 已退化为指针,sizeof(arr) = 4 或 8
int len = sizeof(arr) / sizeof(arr[0]); // 错误!
printf("%d\n", len); // 结果不是数组长度
}
正确的做法是将数组长度作为单独参数传入。
1.4.2. 多维数组
// 2×3 二维数组
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// 访问元素
printf("%d\n", matrix[0][1]); // 2
printf("%d\n", matrix[1][2]); // 6
// 遍历
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// 内存布局:行优先存储
// matrix[0][0] [0][1] [0][2] [1][0] [1][1] [1][2]
1.4.3. 字符数组与字符串
C 语言中字符串用 char 数组表示,以 \0 (空字符) 结尾。
1.4.3.1. 字符串定义
// 方式 1:字符数组(可修改)
char str1[] = {'H', 'e', 'l', 'l', 'o', '\0'};
char str2[] = "Hello"; // 自动添加 \0,大小 = 6
// 方式 2:字符指针(只读,不可修改内容)
char *str3 = "Hello"; // 存储在只读区
// 方式 3:指定大小的字符数组
char str4[20] = "Hello"; // 剩余空间填充 \0
警告
char *str3 = "Hello" 指向字符串字面量,不可修改。
str3[0] = 'h' 会导致运行时错误。
1.4.3.2. 字符串操作函数
需要包含 <string.h>:
#include <string.h>
char dest[20] = "Hello";
char src[] = " World";
// 字符串长度(不含 \0)
int len = strlen(dest); // 5
// 字符串拷贝
strcpy(dest, "Hi"); // dest = "Hi"
// 字符串连接
strcat(dest, " there"); // dest = "Hi there"
// 字符串比较(相等返回 0)
if (strcmp("abc", "abc") == 0) { ... }
// 安全版本(VC10 推荐)
strcpy_s(dest, sizeof(dest), "Hi");
strcat_s(dest, sizeof(dest), " there");
1.4.3.3. 字符串输入
char name[50];
printf("请输入姓名: ");
// scanf 以空白字符分隔,不能读取带空格的字符串
scanf("%s", name); // 危险!可能越界
// 安全读取
scanf_s("%s", name, (unsigned)sizeof(name));
// 或使用 gets_s(读取整行,含空格)
gets_s(name, sizeof(name));
1.4.3.4. 常见字符串处理
#include <stdio.h>
#include <string.h>
int main(void) {
char str[100] = "Hello, C Language!";
char copy[100];
// 复制
strcpy_s(copy, sizeof(copy), str);
// 转大写
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] >= 'a' && str[i] <= 'z') {
str[i] -= 32; // ASCII 码差 32
}
}
printf("大写: %s\n", str); // HELLO, C LANGUAGE!
// 查找子串
char *pos = strstr(str, "C");
if (pos != NULL) {
printf("找到位置: %d\n", pos - str);
}
// 分割字符串
char text[] = "apple,banana,orange";
char *token = strtok(text, ",");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ",");
}
return 0;
}
小技巧
VC10 中许多传统字符串函数(strcpy、strcat 等)会触发安全警告。
使用 _s 后缀的安全版本(如 strcpy_s)可消除警告并增强安全性。
也可在源文件开头定义 #define _CRT_SECURE_NO_WARNINGS 禁用警告。