C语言学习1-数组、字符串

一、数组

一维数组

1. 数组定义

基本语法 正确示例:

// 数据类型 数组名[元素个数];
char arr[5];        // 字符数组
int arr[5];         // 整型数组  
double arr[5];      // 双精度浮点数组
float arr[5];       // 单精度浮点数组

// 使用常量表达式定义数组大小
int a[5];           // 整型常量
int b['A'];         // 字符常量(等价于 int b[65])
int c[3*4];         // 常量表达式(等价于 int c[12])

❌ 错误写法:

int[5] a;           // 错误:[]必须在数组名后面
int[] b;            // 错误:必须指定元素个数
int size = 5;
int d[size];        // 错误:C89标准不支持变量长度(C99支持但有限制)

重要规则:

  • [] 必须放在数组名后面
  • 元素个数必须是常量常量表达式
  • 大多数情况下不能省略元素个数(函数形参和初始化时除外)

2. 数组初始化

完全初始化:

char arr[5] = {90, 91, 92, 93, 94};  // 全部元素初始化

部分初始化:

char arr[5] = {90, 91};              // 前两个元素初始化,其余自动设为0
// 等价于:{90, 91, 0, 0, 0}

自动推断大小:

int arr[] = {1, 2, 3, 4, 5};         // 编译器自动推断大小为5

❌ 错误初始化:

int a[3];
a[3] = {1, 2, 3};    // 错误:定义后不能整体赋值
a = {1, 2, 3};       // 错误:数组名是常量指针,不能赋值

注意: 数组名代表整个数组的起始地址,是一个常量指针。

3. 数组访问

读取元素:

char arr[5] = {90, 91, 92, 93, 94};
char s1 = arr[0];    // 读取第0个元素:90
char s2 = arr[1];    // 读取第1个元素:91

修改元素:

arr[1] = 89;         // 修改第1个元素为89

⚠️ 重要警告:

int arr[5] = {1, 2, 3, 4, 5};
arr[5] = 6; // ❌ 数组越界!C语言不会检查边界

C语言编译器不会对数组下标进行越界检查,程序员必须自己确保访问安全。

4. 数组作为函数参数

#include <stdio.h>

// 函数声明:数组作为参数
void modifyArray(int arr[], int size) {
    for(int i = 0; i < size; i++) {
        arr[i] *= 2;  // 修改数组元素
    }
}

int main() {
    int numbers[5] = {1, 2, 3, 4, 5};
    
    printf("修改前: ");
    for(int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    
    // 传递数组给函数
    modifyArray(numbers, 5);
    
    printf("\n修改后: ");
    for(int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);  // 输出:2 4 6 8 10
    }
    
    return 0;
}

关键点: 数组名作为参数时,传递的是数组的起始地址,形参和实参指向同一内存空间。

二维数组

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

标准初始化:

// 3行4列的二维数组
int matrix[3][4] = {
    {1, 2, 3, 4},      // 第0行
    {5, 6, 7, 8},      // 第1行  
    {9, 10, 11, 12}    // 第2行
};

字符二维数组示例:

char arr[5][3] = {
    {91, 92, 93},      // 第0行
    {81, 82, 83},      // 第1行
    {71, 72, 73},      // 第2行
    {61, 62, 63},      // 第3行
    {51, 52, 53}       // 第4行
};

简化初始化:

// 可以省略第一维的大小
int arr[][3] = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};  // 编译器自动推断为 int arr[3][3]

2. 二维数组访问

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

// 访问单个元素
int element = matrix[0][0];  // 第0行第0列:1
matrix[1][2] = 99;          // 修改第1行第2列的元素

// 遍历二维数组
for(int i = 0; i < 3; i++) {         // 遍历行
    for(int j = 0; j < 4; j++) {     // 遍历列
        printf("%d ", matrix[i][j]);
    }
    printf("\n");
}

注意: 数组下标从 0 开始,matrix[m][n] 表示第 m 行第 n 列。

重要总结

核心要点

  1. 一维数组最重要:实际开发中一维数组使用频率远高于多维数组
  2. 内存连续性:数组元素在内存中是连续存储的
  3. 下标从0开始:C语言数组下标始终从0开始计数
  4. 边界自检:编译器不检查数组越界,需要程序员保证安全

使用建议

// 良好的编程习惯:使用常量定义数组大小
#define MAX_SIZE 100

int main() {
    int arr[MAX_SIZE];
    
    // 循环时使用明确的边界检查
    for(int i = 0; i < MAX_SIZE; i++) {
        // 安全操作
    }
    
    return 0;
}

实际应用优先级

  1. 一维数组:★★★★★(最常用)
  2. 二维数组:★★☆☆☆(较少使用)
  3. 高维数组:★☆☆☆☆(极少使用)

掌握一维数组的使用是学习C语言数据结构的基础,二维及以上数组在实际工程中应用场景有限。

二、字符数组和字符串

字符串的本质

C语言中没有专门的字符串数据类型,而是通过 字符数组 来模拟字符串的功能。

关键概念:

  • C语言中的字符串一定是 char 型数组
  • char 型数组不一定是字符串
  • 字符串 = 以数字 0(或 \0)结尾的字符数组

字符串的定义与区别

1. 字符 vs 字符数组 vs 字符串

#include <stdio.h>

int main() {
    // 1. 单个字符
    char ch = 'w';
    
    // 2. 普通字符数组 - 不是字符串!
    char arr[5] = {'h','e','l','l','o'};
    
    // 3. 字符串 - 以 \0 结尾
    char str1[] = {'h','e','l','l','o','\0'};  // 手动添加 \0
    char str2[] = {'h','e','l','l','o', 0};    // 等价写法,数字0 = \0
    
    // 4. 字符串字面量 - 编译器自动添加 \0
    char *str3 = "hello";                      // 字符串常量
    char str4[] = "hello";                     // 字符数组,自动添加 \0
    
    return 0;
}

2. 内存布局对比

普通字符数组: | 'h' | 'e' | 'l' | 'l' | 'o' | ? | ? | ... |
字符串:       | 'h' | 'e' | 'l' | 'l' | 'o' | \0 | ? | ... |

字符串的打印与遍历

打印示例

#include <stdio.h>

int main() {
    char arr[5] = {'h','e','l','l','o'};      // 普通字符数组
    char str[] = {'h','e','l','l','o','\0'};  // 字符串
    char str2[] = "hello";                    // 字符串
    
    // ❌ 危险:arr没有\0结尾,printf会越界读取!
    printf("arr: %s\n", arr);  // 可能打印乱码
    
    // ✅ 安全:str和str2以\0结尾
    printf("str: %s\n", str);   // 输出: hello
    printf("str2: %s\n", str2); // 输出: hello
    
    return 0;
}

遍历示例

#include <stdio.h>

int main() {
    char arr[5] = {'h','e','l','l','o'};
    char str[] = {'h','e','l','l','o','\0'};
    char str2[] = "hello";
    
    printf("遍历字符数组arr:\n");
    for (int i = 0; i < sizeof(arr); i++) {
        printf("%c", arr[i]);  // 输出: hello
    }
    printf("\n");
    
    printf("遍历字符串str:\n");
    for (int i = 0; i < sizeof(str); i++) {
        printf("%c", str[i]);  // 输出: hello
    }
    printf("\n");
    
    printf("遍历字符串str2:\n"); 
    for (int i = 0; i < sizeof(str2); i++) {
        printf("%c", str2[i]); // 输出: hello
    }
    printf("\n");
    
    return 0;
}

字符串长度与结束标志

字符串长度规则

  • 从头开始,到第一个 \0 结束
  • \0 之前的字符个数就是字符串长度

重要示例

#include <stdio.h>

int main() {
    char str1[30] = "http://c.biancheng.net";
    char str2[] = "C Language"; 
    char str3[30] = "You are a good\0 boy!";
    
    printf("str1: %s\n", str1);  // 输出: http://c.biancheng.net
    printf("str2: %s\n", str2);  // 输出: C Language  
    printf("str3: %s\n", str3);  // 输出: You are a good
    
    return 0;
}

解释:

  • str1, str2: 编译器自动在末尾添加 \0,正常输出整个字符串
  • str3: 遇到中间的 \0 就结束,不输出后面的 " boy!"

关键注意事项

1. 数组大小要足够

// ❌ 错误:数组太小,没有空间存放 \0
char str[5] = "hello";  // 错误!需要6个字节

// ✅ 正确:数组大小 = 字符数 + 1(给\0留位置)
char str[6] = "hello";  // |h|e|l|l|o|\0|
char str[] = "hello";   // 编译器自动计算为6

2. 必须正确终止

// ❌ 不是字符串(缺少\0)
char not_string[5] = {'h','e','l','l','o'};

// ✅ 是字符串(有\0终止)
char is_string[6] = {'h','e','l','l','o','\0'};
char is_string2[] = "hello";

3. \0 之后的内容被忽略

char msg[20] = "Hello\0World";
printf("%s", msg);  // 只输出 "Hello",遇到\0就停止

实用技巧总结

安全定义字符串

// 方法1:使用字符串字面量(推荐)
char str1[] = "Hello World";

// 方法2:显式添加\0
char str2[] = {'H','e','l','l','o','\0'};

// 方法3:先声明后初始化
char str3[20];
str3[0] = 'H';
str3[1] = 'i';
str3[2] = '\0';  // 重要:手动添加结束符

验证字符串完整性

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "hello";
    
    // 检查是否以\0结尾
    printf("Length: %zu\n", strlen(str));      // 输出: 5
    printf("Sizeof: %zu\n", sizeof(str));      // 输出: 6 (包含\0)
    printf("Last char: %d\n", str[5]);         // 输出: 0 (\0的ASCII值)
    
    return 0;
}

核心要点回顾

  1. 字符串本质:以 \0 结尾的字符数组
  2. 结束标志:数字 0\0,不是字符 '0'
  3. 数组大小:必须 ≥ 字符数 + 1(为 \0 预留空间)
  4. 打印行为printf 遇到 \0 就停止输出
  5. 安全第一:确保所有字符串正确以 \0 结尾
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容