当前位置: 首页 > news >正文

C语言入门教程(第5讲):数组详解——一次性搞懂一维数组、二维数组与内存布局

🌱 文章摘要

程序学会了判断与循环后,下一步就要学会“记忆”
数组是C语言中最基础也最强大的数据结构,
让你的程序能批量存储、集中处理数据


这一讲,我们将系统掌握数组的定义、初始化、内存布局与二维数组结构
彻底搞懂数组的本质,为后续的函数与指针打下坚实基础。


💬 导读

如果说变量是一个“盒子”,那数组就是一整排整齐的“盒子”
它能让你的程序从“操作一个值”变成“处理一组值”
实现更高效、更优雅的数据管理。
我们将从一维数组出发,逐步走进二维数组与变长数组
在最后的实战环节,还会带你实现炫酷的“字符汇聚动画”与基础算法“二分查找”


准备好了吗?让我们正式进入 C 语言的数据世界!


📘 建议收藏本专栏:后续章节会持续更新,
包括控制语句、函数、指针、数组、文件操作等完整体系。

如果你能坚持学完,你会发现——
C语言不仅是工具,更是理解计算机世界的第一扇门。


目录

一、从变量到数组:让程序“记得更多”

1.1 数组是什么?

1.2 列举样例

二、数组的声明语法

2.1 基本语法格式

2.2 错误示例

三、一维数组的创建与初始化

3.1 方式1:完整初始化

3.2 方式2:部分初始化

3.3 方式3:让编译器自动推导长度

3.4 方式4:全部元素为 0

3.5 错误示例

四、数组元素的访问

4.1 示例1

五、一维数组的使用

5.1 示例1:输入并输出5个整数

5.2 示例2:计算数组元素的平均值

六、一维数组的内存存储机制

6.1 原理概述

6.2 示例1:查看数组元素地址

6.3 示例2:数组名与指针的关系

6.4 注意

七、sizeof 与元素个数计算

7.1 示例1:查看数组大小

7.2 示例2:求数组元素个数

7.3 应用:自动遍历数组

八、二维数组的定义与初始化

8.1 基本定义格式

8.2 初始化方式

方式1:完整初始化

方式2:行内展开初始化

方式3:部分初始化

方式4:让编译器推导列数

九、二维数组的访问与内存布局

9.1 示例1:打印二维数组

9.2 内存布局示意

9.3 示例:输出每个元素的地址

十、C99 的变长数组(VLA)

10.1 示例

十一、实战练习

11.1 实战1:字符汇聚动画(welcome to bit)

11.2 实战2:二分查找(Binary Search)

十二、总结

十三、下一讲预告

13.1 下一讲预告

13.2 提前准备建议


一、从变量到数组:让程序“记得更多”

还记得前几讲中我们写过这样一段代码吗?

int a, b, c;
scanf("%d %d %d", &a, &b, &c);
printf("平均值为:%d\n", (a + b + c) / 3);

这当然没问题,但如果现在要输入 100个整数 呢?
是不是要写:

int a1, a2, a3, ..., a100;

太累了!

所以,C语言为我们提供了一个更好的工具——数组(Array)


1.1 数组是什么?

数组是一组类型相同、连续存储的变量集合。
你可以把它理解为“一排连在一起的储物格子”,
每个格子都能放一个数据。


1.2 列举样例

int score[5];

含义:

  • 声明一个数组 score

  • 能存放 5 个 int 类型的元素;

  • 下标(索引)从 0 到 4

  • 每个元素都像是一个独立变量:

    • score[0]、score[1]、score[2]、score[3]、score[4]

数组 = 一串相同类型的变量 + 连续的内存空间 + 下标访问。


二、数组的声明语法

2.1 基本语法格式

类型说明符 数组名[常量表达式];

例如:

int num[10];       // 存放10个整数
float score[50];   // 存放50个浮点数
char name[20];     // 存放20个字符

关键点:

  1. 下标必须是整数常量或常量表达式;

  2. 数组元素类型必须相同;

  3. 定义后每个元素初值不确定(可能是随机值)。


2.2 错误示例


int n = 5; int arr[n]; // 在C90标准中不允许

(C99标准开始支持“变长数组”,后面章节会讲)

声明数组时要告诉编译器它的“类型”和“长度”,
这样系统才能在内存中分配一整块连续的空间。


三、一维数组的创建与初始化

C语言允许在定义数组时进行初始化。
初始化方式灵活多样,我们逐个来看。


3.1 方式1:完整初始化

int num[5] = {1, 2, 3, 4, 5};

说明:

  • 定义时立即赋值;

  • 元素个数与初始化值个数一致;

  • 下标范围为 0~4。


3.2 方式2:部分初始化

int num[5] = {1, 2};

说明:

  • 前两个元素为 1 和 2;

  • 其余元素自动初始化为 0。


3.3 方式3:让编译器自动推导长度

int num[] = {10, 20, 30, 40};

说明:

  • 系统会根据初始化列表自动计算长度;

  • 等价于 int num[4] = {10, 20, 30, 40};


3.4 方式4:全部元素为 0

int num[5] = {0};

说明:

  • 初始化列表中只有一个 0

  • 编译器会将所有元素都设为 0。


3.5 错误示例

int num[5];
num = {1,2,3,4,5}; // 错误!数组不能整体赋值

数组在定义时可以整体初始化,
但定义完成后,只能逐个元素赋值,不能整体重新赋值。


四、数组元素的访问

数组的每个元素通过下标访问。下标从 0 开始,到长度 - 1 结束。


4.1 示例1

#include <stdio.h>int main() {int num[5] = {10, 20, 30, 40, 50};printf("第一个元素:%d\n", num[0]);printf("第三个元素:%d\n", num[2]);printf("最后一个元素:%d\n", num[4]);return 0;
}

输出:

第一个元素:10
第三个元素:30
最后一个元素:50

原理:
数组名 + 下标 = 对应位置元素。
num[2] 的含义是:

从数组首地址起偏移 2 个元素。

访问数组元素时务必注意下标范围,
超出范围会造成内存越界错误,属于C语言最常见Bug之一。


五、一维数组的使用

数组是循环结构的最佳拍档。
它让我们可以批量输入、输出和处理数据。


5.1 示例1:输入并输出5个整数

#include <stdio.h>int main() {int num[5];int i;// 输入阶段for (i = 0; i < 5; i++) {printf("请输入第 %d 个整数:", i + 1);scanf("%d", &num[i]);}// 输出阶段printf("你输入的数字是:\n");for (i = 0; i < 5; i++) {printf("%d ", num[i]);}printf("\n");return 0;
}

输出示例:

请输入第 1 个整数:10
请输入第 2 个整数:20
请输入第 3 个整数:30
请输入第 4 个整数:40
请输入第 5 个整数:50
你输入的数字是:
10 20 30 40 50

解读:

  • 使用循环可以轻松遍历数组;

  • num[i] 表示第 i+1 个元素;

  • 下标从 0 开始,范围是 0~4


5.2 示例2:计算数组元素的平均值

#include <stdio.h>int main() {int score[5];int i, sum = 0;for (i = 0; i < 5; i++) {printf("请输入第 %d 个分数:", i + 1);scanf("%d", &score[i]);sum += score[i];}printf("平均分为:%.2f\n", sum / 5.0);return 0;
}

输出:

请输入第 1 个分数:90
请输入第 2 个分数:85
请输入第 3 个分数:78
请输入第 4 个分数:92
请输入第 5 个分数:88
平均分为:86.60

使用 for 循环配合数组,能批量处理数据,
极大提高程序可读性与可维护性。


六、一维数组的内存存储机制

理解数组,必须了解它在内存中的结构。

6.1 原理概述

  • 数组在内存中是连续存储的;

  • 元素之间紧密排列;

  • 数组名代表第一个元素的地址


6.2 示例1:查看数组元素地址

#include <stdio.h>int main() {int a[5] = {10, 20, 30, 40, 50};for (int i = 0; i < 5; i++) {printf("a[%d] 的地址是:%p\n", i, &a[i]);}return 0;
}

输出示例:

a[0] 的地址是:000000000061FE10
a[1] 的地址是:000000000061FE14
a[2] 的地址是:000000000061FE18
a[3] 的地址是:000000000061FE1C
a[4] 的地址是:000000000061FE20

解读:

  • 每个 int 占 4 字节;

  • 地址之间差 4;

  • 表示数组在内存中一格接一格存储。


6.3 示例2:数组名与指针的关系

#include <stdio.h>int main() {int a[3] = {10, 20, 30};printf("a 的值:%p\n", a);printf("&a[0] 的值:%p\n", &a[0]);printf("a[0] 的内容:%d\n", a[0]);return 0;
}

输出(示例):

a 的值:000000000061FE10
&a[0] 的值:000000000061FE10
a[0] 的内容:10

结论:

数组名 a 实际上等价于 &a[0]
即第一个元素的地址。


6.4 注意

  • 数组名是常量指针,不能被修改;

  • 例如:

    int a[5];
    a = a + 1; // 错误:数组名不是变量
  • 但我们可以定义一个“普通指针”指向它:

    int *p = a;
    p++;
    

数组的本质是连续的内存块;
数组名其实是一个“指针常量”。


七、sizeof 与元素个数计算

sizeof 是一个非常实用的运算符,
它能告诉我们变量或数据类型占用的字节数。


7.1 示例1:查看数组大小

#include <stdio.h>int main() {int a[5] = {1, 2, 3, 4, 5};printf("数组a占用字节数:%zu\n", sizeof(a));printf("一个元素占字节数:%zu\n", sizeof(a[0]));return 0;
}

输出:

数组a占用字节数:20
一个元素占字节数:4

解读:

  • 每个 int 占 4 字节;

  • 5 个元素总共 20 字节;

  • %zu 是打印 size_t 类型的标准格式符。


7.2 示例2:求数组元素个数

int count = sizeof(a) / sizeof(a[0]);
printf("数组元素个数为:%d\n", count);

输出:

数组元素个数为:5

元素个数 = 数组总大小 / 单个元素大小

这在实际编程中非常常用,尤其是在循环中遍历数组时。


7.3 应用:自动遍历数组

#include <stdio.h>int main() {int a[] = {3, 6, 9, 12, 15};int count = sizeof(a) / sizeof(a[0]);for (int i = 0; i < count; i++) {printf("a[%d] = %d\n", i, a[i]);}return 0;
}

输出:

a[0] = 3
a[1] = 6
a[2] = 9
a[3] = 12
a[4] = 15

优点:

  • 不必手动数长度;

  • 程序更通用;

  • 避免“越界访问”风险。

sizeof 不仅能测大小,还能让代码更安全、通用。
记住:数组遍历推荐使用 sizeof 计算元素数量。


八、二维数组的定义与初始化

一维数组能存放一列数据,
而二维数组就像是“表格”一样,可以存放行与列的数据。


8.1 基本定义格式

类型说明符 数组名[行数][列数];

例如:

int a[3][4];  // 3行4列的整型数组

理解方式:

  • a[0][0] 表示第1行第1列;

  • a[2][3] 表示第3行第4列;

  • 二维数组本质上是“数组的数组”,
    即:每一行又是一个一维数组。


8.2 初始化方式

方式1:完整初始化

int a[2][3] = {{1, 2, 3},{4, 5, 6}
};
  • 第一行:{1,2,3}

  • 第二行:{4,5,6}


方式2:行内展开初始化

int a[2][3] = {1, 2, 3, 4, 5, 6};
  • 编译器自动按行填充;

  • 与上例效果完全相同。


方式3:部分初始化

int a[2][3] = { {1}, {4, 5} };
  • 未给出的元素自动补 0。


方式4:让编译器推导列数

int a[][3] = { {1,2,3}, {4,5,6} };
  • 第一维(行数)可以省略;

  • 第二维(列数)必须明确。


二维数组的初始化与一维数组类似,只是多了一层“行”的概念。


九、二维数组的访问与内存布局

二维数组的访问同样使用下标:

a[行][列]

9.1 示例1:打印二维数组

#include <stdio.h>int main() {int a[2][3] = {{1, 2, 3},{4, 5, 6}};for (int i = 0; i < 2; i++) {for (int j = 0; j < 3; j++) {printf("%d ", a[i][j]);}printf("\n");}return 0;
}

输出:

1 2 3
4 5 6
  • 外层循环控制行;

  • 内层循环控制列;

  • 访问顺序为行优先(row-major order)。


9.2 内存布局示意

在内存中,二维数组也是连续存放的:

a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]

C语言中,二维数组按“行优先”顺序存储。


9.3 示例:输出每个元素的地址

#include <stdio.h>int main() {int a[2][3] = { {1,2,3}, {4,5,6} };for (int i = 0; i < 2; i++) {for (int j = 0; j < 3; j++) {printf("&a[%d][%d] = %p\n", i, j, &a[i][j]);}}return 0;
}

输出示例:

&a[0][0] = 000000000061FE10
&a[0][1] = 000000000061FE14
&a[0][2] = 000000000061FE18
&a[1][0] = 000000000061FE1C
...
  • 地址之间连续;

  • 每次偏移4字节(int类型);

  • 表示二维数组在底层其实就是“一维线性块”。

二维数组的本质是一维数组的嵌套,所有元素仍在一块连续内存中。


十、C99 的变长数组(VLA)

C99标准引入了一个新特性——变长数组(Variable Length Array, VLA)

它允许我们使用变量来定义数组长度,而不再局限于常量。


10.1 示例

#include <stdio.h>int main() {int n;printf("请输入数组长度:");scanf("%d", &n);int arr[n]; // 可行! 变长数组(C99支持)for (int i = 0; i < n; i++) {arr[i] = i + 1;}for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}return 0;
}

输出示例:

请输入数组长度:6
1 2 3 4 5 6
  • 变长数组只在C99及之后标准有效;

  • 栈内存动态分配,退出函数时自动释放;

  • 不能用初始化列表赋值;

  • 不能使用 sizeof 计算长度。

变长数组提供了灵活性,但使用时要注意兼容性与内存开销。


十一、实战练习

下面通过两个经典案例,让你彻底掌握数组的应用。


11.1 实战1:字符汇聚动画(welcome to bit)

#include <stdio.h>
#include <string.h>
#include <windows.h>int main() {char str[] = "welcome to bit";int left = 0;int right = strlen(str) - 1;while (left <= right) {printf("\r");  // 光标回到行首for (int i = 0; i < strlen(str); i++) {if (i < left || i > right)printf("%c", str[i]);elseprintf(" ");}fflush(stdout);Sleep(200);left++;right--;}printf("\n动画结束!\n");return 0;
}

核心逻辑:

  • 使用字符数组保存字符串;

  • 通过下标控制字符显示;

  • Sleep() 产生动画效果;

  • 演示数组在字符串控制中的应用。


11.2 实战2:二分查找(Binary Search)

#include <stdio.h>int main() {int arr[] = {1, 3, 5, 7, 9, 11, 13};int key, left = 0, right = 6;printf("请输入要查找的数字:");scanf("%d", &key);while (left <= right) {int mid = (left + right) / 2;if (arr[mid] == key) {printf("找到啦!下标 = %d\n", mid);return 0;} else if (arr[mid] < key)left = mid + 1;elseright = mid - 1;}printf("未找到该数字。\n");return 0;
}

解读:

  • 数组 + 循环 + 判断 的完美结合;

  • 体现算法思维;

  • 展现数组作为数据载体的核心作用。

实战是最好的理解。
通过字符动画与查找算法,你已经能在C语言中使用数组做出“有逻辑的程序”。


十二、总结

这一讲,我们走完了C语言数据存储的第一步。

你已经掌握了:

  • 一维数组与二维数组的定义与使用;

  • 数组的内存布局与下标访问原理;

  • sizeof 的应用与元素个数计算;

  • C99 的变长数组(VLA);

  • 数组的典型实战案例。

从这一讲开始,程序真正学会了“记忆”。
数据不再是一次性输入输出,而是可以被保存、分析与操作的。

接下来,你将学会让程序“有条理地思考”——那就是函数的世界。


十三、下一讲预告

13.1 下一讲预告

《C语言入门教程(第6讲):函数详解——让程序会“思考”与“复用”》

在下一讲中,我们将全面讲解:

  • 函数的定义、声明与调用;

  • 参数传递与返回值;

  • 数组作为函数参数;

  • 作用域与存储类型(static、extern);

  • 函数嵌套与递归初步。

一句话总结:

数组让程序能记,函数让程序能“想”。


13.2 提前准备建议

  1. 预先练习编写一个计算平方的函数:

    int square(int x) {return x * x;
    }
  2. 观察函数调用过程,理解输入输出的“流动”;

  3. 阅读前几讲中的数组案例,思考如何将逻辑封装为函数;

  4. 熟悉 return 与参数类型的匹配规则。

提示:

你可以把函数想象成一个“工厂”——
接收原料(参数),加工处理,输出结果(返回值)。


📘 系列结尾

本文属于《C语言入门教程》系列专栏第 5 讲
下一讲将带你进入C语言的“函数世界”
让你的程序从“能存数据”进化为“能思考、能复用”。

点赞 + 收藏 + 关注专栏,别错过更新,
你的支持,是我持续更新的最大动力 💪

http://www.dtcms.com/a/467228.html

相关文章:

  • c++ static_cast用法
  • 广东省建设工程总监扣分查询网站无极网络
  • 龙华建设网站公司wordpress 3.7
  • 淘宝客怎么做自己的网站学习做网站的网站
  • 手机百度网站证书过期外贸网站推广方法之一
  • 网站登录后不显示内容试玩网站设计建设
  • Visual Basic.NET 关键词
  • 数据结构--------树二叉树
  • 郑州网站开发培训班o2o网站系统建设
  • Lampiao渗透项目学习记录
  • 新功能来袭——支持导出MIDI文件,AI音乐从此进入新篇章
  • 网站推广活动方案权威网站排名
  • Google 智能体设计模式:工具使用(函数调用)
  • 网站开发的技术类型有哪些网络服务代码1001
  • Redis-string
  • 网站网页的收录数量赤峰建设厅官方网站
  • 做灯箱的网站wordpress nginx apache
  • (7)100天python从入门到拿捏《迭代器和生成器》
  • 花卉网站建设策划书核酸二维码
  • 00--VSCode配置
  • 光明区建设局网站抖音代运营成功案例
  • 宁波论坛建站模板珠海市建设工程信息网
  • 温州做网站价格外贸推广软件
  • 自己建的网站也要注册域名吗电子商务网站建设与维护试卷答案
  • Java项目:基于SSM框架实现的连锁干洗店管理系统(ssm+B/S架构+源码+数据库+毕业论文)
  • 国外装饰公司网站郑州网站seo厂家
  • 如何建立免费个人网站盘龙区网站建设外包
  • 供热设施网站搭建教程品牌推广名词解释
  • 算法社Python基础入门面试题库(新手版·含答案)
  • 网站建设与管理 自考wordpress邮箱社交