当前位置:首页 > 技术 > 正文内容

C语言二维数组、变长数组与基础算法实战解析

访客 技术 2026年8月10日 1

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

当一维数组的元素类型本身也是数组时,即构成二维数组。其本质是数组的嵌套,常用于表示矩阵、表格等具有行列结构的数据。

声明语法如下:

数据类型 标识符[行数常量][列数常量];

示例:int matrix[3][5]; 表示创建一个包含3行、每行5个整型元素的二维结构。C语言规定行列索引均从 0 开始。

初始化规则:

  • 不完全初始化:未显式赋值的元素会自动补零。
  • 完全初始化:提供所有元素的值,或按行分组赋值。
  • 维度省略:编译器可根据初始化列表自动推导行数,但列数必须显式指定。
// 剩余元素自动填充为 0
int grid_a[3][5] = {1, 2};
int grid_b[3][5] = {0};

// 完整赋值(扁平化写法)
int grid_c[3][5] = {1,2,3,4,5, 6,7,8,9,10, 11,12,13,14,15};

// 按行分组赋值,结构更清晰
int grid_d[3][5] = {{1, 2}, {3, 4}, {5, 6}};

// 省略行数,由编译器自动计算为 1 行
int grid_e[][5] = {10, 20, 30};

2. 二维数组的数据交互

通过嵌套循环可逐行逐列访问元素。外层控制行索引,内层控制列索引。

#include <stdio.h>
#define ROW_COUNT 3
#define COL_COUNT 5

int main(void) {
    int data_table[ROW_COUNT][COL_COUNT] = {0};

    // 逐行录入数据
    for (int r = 0; r < ROW_COUNT; ++r) {
        for (int c = 0; c < COL_COUNT; ++c) {
            scanf("%d", &data_table[r][c]);
        }
    }

    // 矩阵格式输出
    for (int r = 0; r < ROW_COUNT; ++r) {
        for (int c = 0; c < COL_COUNT; ++c) {
            printf("%4d", data_table[r][c]);
        }
        putchar('\n');
    }
    return 0;
}

3. 内存布局与连续性

二维数组在物理内存中并非真正的"二维"结构,而是采用行优先(Row-Major)的线性连续存储方式。即第一行存完后,紧接着存储第二行,依此类推。

#include <stdio.h>
#define M 3
#define N 4

int main(void) {
    int buffer[M][N] = {0};
    for (int row = 0; row < M; ++row) {
        for (int col = 0; col < N; ++col) {
            printf("&buffer[%d][%d] = %p\n", row, col, (void*)&buffer[row][col]);
        }
    }
    return 0;
}

运行上述代码可观察到:同行相邻元素地址差值为 sizeof(int)(通常为4字节)。跨行边界处(如 buffer[0][3]buffer[1][0])的地址同样相差4字节。这证实了多维数组在底层是一块连续的内存区域,行列划分仅是编译器提供的逻辑抽象。

4. 变长数组(VLA)特性

C99 标准引入了变长数组(Variable Length Array),允许在运行时使用变量动态指定数组尺寸。需注意以下核心限制:

  • 尺寸在声明时确定,之后不可更改(并非动态扩容)。
  • 禁止在声明时进行初始化,必须后续赋值。
  • 编译器兼容性差异:GCC/Clang 默认支持,MSVC 对 C99 VLA 支持有限,跨平台项目需谨慎使用。
#include <stdio.h>

int main(void) {
    int size = 0;
    scanf("%d", &size);
    
    // 运行时确定长度,不可写为 int vla[size] = {0};
    int vla[size];
    for (int i = 0; i < size; ++i) {
        vla[i] = i * 10;
    }
    return 0;
}

5. 实战:字符向中心汇聚动画

利用双指针技术,从字符串两端向中间逐步替换掩码字符,配合延时函数可实现动态视觉效果。

#include <stdio.h>
#include <string.h>
#include <windows.h> // Linux环境请替换为 <unistd.h> 并使用 sleep(1)

int main(void) {
    const char* target = "Welcome to C World!";
    char canvas[64];
    size_t len = strlen(target);
    
    // 初始化掩码层
    memset(canvas, '#', len);
    canvas[len] = '\0';

    int head = 0;
    int tail = (int)len - 1;

    while (head <= tail) {
        canvas[head] = target[head];
        canvas[tail] = target[tail];
        printf("%s\n", canvas);
        
        Sleep(600); // 控制刷新频率
        head++;
        tail--;
    }
    return 0;
}

6. 实战:有序序列检索算法

线性扫描:逐个比对,时间复杂度 O(N),适用于无序或小规模数据。

二分查找(Binary Search):依赖数据有序性,每次排除一半区间,时间复杂度 O(log N)。计算中点时推荐使用 low + ((high - low) >> 1) 防止整型溢出。

#include <stdio.h>

int main(void) {
    int sequence[] = {5, 12, 19, 24, 33, 41, 58, 67, 72, 89};
    int key = 0;
    printf("Enter target value: ");
    scanf("%d", &key);

    int low = 0;
    int high = (int)(sizeof(sequence) / sizeof(sequence[0])) - 1;
    int result_idx = -1;

    while (low <= high) {
        int mid = low + ((high - low) >> 1);

        if (sequence[mid] == key) {
            result_idx = mid;
            break;
        } else if (sequence[mid] > key) {
            high = mid - 1; // 目标在左半区
        } else {
            low = mid + 1;  // 目标在右半区
        }
    }

    if (result_idx != -1) {
        printf("Located at index: %d\n", result_idx);
    } else {
        printf("Target not found.\n");
    }
    return 0;
}

7. 函数模块化设计基础

C语言中的函数(Function)本质是独立执行的子程序单元。通过将庞大逻辑拆解为多个职责单一的函数,可显著提升代码的可读性、可测试性与复用率。

工程实践中通常将函数划分为两类:

  • 标准库函数:由编译器厂商提供,封装于头文件中(如 <stdio.h><string.h>),直接调用即可。
  • 用户自定义函数:开发者根据业务需求编写,需明确定义返回类型、函数名、形参列表及函数体。合理封装自定义函数是构建中大型C项目的核心基石。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。