【C语言】 字符数组与多维数组

#C基础

字符数组

字符数组与字符串的区别

字符数组是元素为字符型(char)的数组。字符串则是字符的序列,以空字符 \0(ASCII 码为 0)作为结束标志。字符数组不一定是字符串,只有以 \0 结尾的字符数组才能被视为字符串。

示例:

c 复制代码
char arr1[] = {'h', 'e', 'l', 'l', 'o'};        // 字符数组,不是字符串
char arr2[] = {'h', 'e', 'l', 'l', 'o', '\0'};  // 字符串
char arr3[] = "hello";                          // 字符串,自动添加 \0

字符数组的初始化方式

常规初始化

c 复制代码
char a[6] = {'h', 'e', 'l', 'l', 'o', '\0'};

字符串初始化(推荐)

c 复制代码
char b[6] = "hello";  // 等价于上述常规初始化
char c[] = "hello";   // 数组长度自动推断为 6(包含 \0)

部分初始化

c 复制代码
char d[10] = "hello";  // 前6个字符为 'h','e','l','l','o','\0',后4个为 '\0'

字符数组的内存存储与可修改性

  • 使用字符数组存储字符串时,字符串存储在栈内存中,内容可读写。
  • 使用字符指针指向字符串常量时,字符串存储在常量区,内容只读。
c 复制代码
char arr[] = "hello";
arr[0] = 'H';  // 允许修改

char *ptr = "hello";
// ptr[0] = 'H';  // 错误:试图修改常量区数据

栈区与常量区的区别:

特性 字符数组(栈区) 字符指针(常量区)
存储位置 栈内存 常量区
可修改性 可读写 只读
生命周期 函数作用域内 程序运行期间
内存分配 编译时确定 编译时确定

字符数组与字符串的转换

有时我们需要将数字转换为字符串,或者将字符串转换为数字。可以使用 sprintfsscanf 函数。

使用 sprintf 将格式化的数据写入字符串

c 复制代码
char str[100];
int num = 123;
float f = 3.14;
sprintf(str, "整数:%d,浮点数:%.2f", num, f);  // str 现在为 "整数:123,浮点数:3.14"

使用 sscanf 从字符串中读取格式化的数据

c 复制代码
char str[] = "123 3.14";
int num;
float f;
sscanf(str, "%d %f", &num, &f);  // num=123, f=3.14

字符数组的常见应用

  • 用户输入处理(getsfgetsscanf
  • 文本文件的读写
  • 字符串解析与分割(strtok
  • 简单加密与编码转换

二维数组

二维数组的定义与理解

二维数组可视为"数组的数组"。例如:

c 复制代码
int a[3][4];  // 3行4列的二维数组

理解方式:

  • a 是一个数组,包含 3 个元素:a[0]a[1]a[2]
  • 每个元素又是一个长度为 4 的 int 数组

二维数组的地址与指针类型

以下代码说明了二维数组中不同表达式的类型与地址计算方式:

c 复制代码
#include <stdio.h>
int main(void) {
    int a[3][4];

    printf("&a     = %p\n", &a);          // int (*)[3][4],指向整个二维数组
    printf("&a+1   = %p\n", &a + 1);      // 跳过一个 3×4 的 int 数组(48字节)

    printf("a      = %p\n", a);           // int (*)[4],指向第一行
    printf("a+1    = %p\n", a + 1);       // 跳过一个 int[4](16字节)

    printf("a[0]   = %p\n", a[0]);        // int *,指向第一个元素
    printf("a[0]+1 = %p\n", a[0] + 1);    // 跳过一个 int(4字节)

    printf("&a[0][0]   = %p\n", &a[0][0]);    // int *
    printf("&a[0][0]+1 = %p\n", &a[0][0] + 1); // 跳过一个 int
    return 0;
}

二维数组的初始化

标准初始化(按行)

c 复制代码
int a[3][4] = {
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {8, 9, 10, 11}
};

省略第一维长度

c 复制代码
int b[][4] = {
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {8, 9, 10, 11}
};

顺序初始化(按内存顺序)

c 复制代码
int c[3][4] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

指定元素初始化(C99 支持)

c 复制代码
int d[3][4] = {
    [1] = {4, 5, 6, 7},
    [2][2] = 10
};

二维数组的访问方式

c 复制代码
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        printf("a[%d][%d] = %d\n", i, j, a[i][j]);
        // 等价于:
        // *(*(a + i) + j)
        // *(a[i] + j)
    }
}

二维数组与指针的关系

二维数组名可以看作是指向数组的指针,即指向第一行(一个一维数组)的指针。

例如,对于 int a[3][4];

  • a 的类型是 int (*)[4],即指向含有4个整数的数组的指针。
  • a[0] 的类型是 int *,指向第一个整型元素。

我们可以使用指针来遍历二维数组:

c 复制代码
int a[3][4] = {0};
int *p = &a[0][0];
for (int i = 0; i < 3*4; i++) {
    *(p+i) = i;   // 通过指针访问
}

// 或者使用数组指针
int (*ptr)[4] = a;  // ptr指向第一行
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        printf("%d ", ptr[i][j]);  // 通过数组指针访问
    }
}

动态创建二维数组

动态创建二维数组通常有两种方法:

使用指针数组

先创建一个指针数组,每个指针指向一个一维数组。

c 复制代码
int rows = 3, cols = 4;
int **arr = (int **)malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
    arr[i] = (int *)malloc(cols * sizeof(int));
}

// 使用
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        arr[i][j] = i * cols + j;
    }
}

// 释放
for (int i = 0; i < rows; i++) {
    free(arr[i]);
}
free(arr);

使用一维数组模拟二维数组

分配一个一维数组,然后通过索引计算来访问。

c 复制代码
int rows = 3, cols = 4;
int *arr = (int *)malloc(rows * cols * sizeof(int));

// 使用
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        arr[i * cols + j] = i * cols + j;
    }
}

// 释放
free(arr);

多维数组

多维数组的定义与内存布局

三维数组:

c 复制代码
int arr3D[2][3][4];  // 2页,每页3行4列

内存中是按行优先顺序连续存储的,即:

复制代码
arr[0][0][0], arr[0][0][1], ..., arr[0][2][3], arr[1][0][0], ...

四维数组:

c 复制代码
int arr4D[2][3][4][5];  // 2块,每块3页,每页4行5列

多维数组作为函数参数

传递多维数组时,除第一维外,其余维度必须指定大小

c 复制代码
void func(int arr[][4], int rows);           // 正确
// void func(int arr[][], int rows);         // 错误
// void func(int **arr, int rows, int cols); // 需使用指针传递动态数组

多维数组的应用场景

  • 二维数组:矩阵运算、图像像素处理、游戏地图
  • 三维数组:立体数据结构、三维图像处理、RGB 色彩空间
  • 四维及以上:时间序列数据、多通道图像、科学计算中的多维场

常见问题与编程建议

字符数组常见错误

  • 忘记 \0 结尾
  • 数组越界
  • 试图修改字符串常量
  • 使用 strcpy 前未确保目标数组足够大

多维数组的内存优化

  • 对于大型多维数组,考虑使用一维数组模拟,以提高访问效率。
  • 使用指针访问多维数组时,注意指针类型与步长。
相关推荐
pp起床2 小时前
贪心算法 | part01
算法·贪心算法
咩咩不吃草2 小时前
机器学习不平衡数据处理三招:k折交叉验证、下采样与过采样实战
人工智能·算法·机器学习·下采样·过采样·k折交叉验证
weixin_452159552 小时前
模板编译期条件分支
开发语言·c++·算法
多恩Stone2 小时前
【3DV 进阶-11】Trellis.2 数据处理与训练流程图
人工智能·pytorch·python·算法·3d·aigc·流程图
老师用之于民2 小时前
【DAY20】数据结构基础:(算法)排序、折半查找的函数实现
数据结构·算法·排序算法
时时三省2 小时前
【时时三省】(C语言基础)共用体/联合体
c语言
一起养小猫2 小时前
Flutter for OpenHarmony 进阶:推箱子游戏算法与关卡设计深度解析
算法·flutter·游戏
民乐团扒谱机2 小时前
【微实验】Zhang-Suen 快速并行细化算法与MATLAB实现
人工智能·学习·算法·计算机视觉·数学建模·matlab
iAkuya2 小时前
(leetcode)力扣100 60单词搜索(回溯)
算法·leetcode·职场和发展