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

C语言数组专题:从一维到二维,吃透内存与指针

数组是 C 语言最核心的基础知识点二维数组更是衔接一维数组、指针与函数的关键枢纽。本文由浅入深梳理一维到二维数组完整知识点并总结高频易错点帮你彻底学懂学透。1. 一维数组基础1.1 什么是一维数组一维数组是C语言中最基础的线性数据结构它是相同类型数据的连续内存集合。//语法类型 数组名[数组长度]; int arr[5]; // 定义了一个能存放5个int数据的数组1.2 一维数组的初始化完全初始化int arr[5] {1,2,3,4,5};部分初始化int arr[5] {1,2}; // 未初始化元素自动为0省略长度初始化int arr[] {1,2,3,4,5}; // 长度由初始化列表决定1.3 一维数组的访问与遍历数组元素通过下标索引访问下标从0开始int arr[5] {1,2,3,4,5}; // 访问元素 printf(%d\n, arr[0]); // 输出1 // 遍历数组 for(int i0; i5; i){ printf(%d , arr[i]); }1.4 一维数组的内存存储一维数组在内存中是连续存储的数组名arr是数组首元素的地址printf(%p\n, arr); // 数组首地址 printf(%p\n, arr[0]);// 和arr的值完全相同 // 元素地址连续 printf(%p\n, arr[0]); printf(%p\n, arr[1]); // 地址相差一个int的大小(4字节)2. 二维数组二维数组可以理解为“数组的数组”即一个数组的每个元素又是一个一维数组。2.1 定义与初始化2.1.1 定义语法// 类型 数组名[行数][列数]; int arr[3][4]; // 3行4列的二维数组可存放12个int数据行数表示有多少个一维数组列数每个一维数组的长度2.1.2 初始化方式完全初始化分行初始化int arr[3][4] { {1,2,3,4}, {5,6,7,8}, {9,10,11,12} };2. 连续初始化按行填充int arr[3][4] {1,2,3,4,5,6,7,8,9,10,11,12};3. 部分初始化// 只初始化前两行第三行自动补0int arr[3][4] {{1,2}, {3,4,5}};4.省略行数初始化// 行数由初始化列表的元素个数和列数决定int arr[][4] {1,2,3,4,5,6,7,8}; // 自动识别为2行4列2.2 下标访问与遍历2.2.1 下标访问二维数组元素通过数组名[行标][列标]访问行标和列标都从0开始int arr[3][4] {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; printf(%d\n, arr[0][0]); // 输出第一行第一列的元素1 printf(%d\n, arr[2][3]); // 输出第三行第四列的元素122.2.2 遍历二维数组最常用的方式是双层循环int arr[3][4] {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; // 外层循环控制行内层循环控制列 for(int i0; i3; i){ for(int j0; j4; j){ printf(%d , arr[i][j]); } printf(\n); // 每一行结束换行 }2.3 内存存储行优先二维数组在内存中并不是“二维”的而是按行优先的顺序连续存储的。2.3.1 行优先存储规则存储顺序是先存第0行的所有元素再存第1行再存第2行……比如上面的arr[3][4]在内存中的存储顺序是arr[0][0] → arr[0][1] → arr[0][2] → arr[0][3] → arr[1][0] → ... → arr[2][3]2.3.2 地址验证int arr[3][4] {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; printf(arr[0]地址%p\n, arr[0]); printf(arr[1]地址%p\n, arr[1]); // 比arr[0]大16(4个int) printf(arr[2]地址%p\n, arr[2]); // 比arr[1]大16 printf(arr[0][0]地址%p\n, arr[0][0]); printf(arr[0][1]地址%p\n, arr[0][1]); // 比arr[0][0]大43. 指针与二维数组核心二维数组和指针的关系是C语言的重点和难点核心在于理解行指针和数组名的含义。3.1 二维数组名的含义arr二维数组名是指向第一行的指针类型为int (*)[4]指向包含4个int元素的一维数组的指针arr[0]二维数组第0行的数组名是第0行首元素的地址类型为int*arr整个二维数组的地址类型为int (*)[3][4]3.2 下标与指针的等价关系二维数组的下标访问本质上是指针运算// 以下写法完全等价arr[i][j]*(arr[i] j)*(*(arr i) j)理解arr i指向第i行的地址行指针运算每次移动一整行的大小*(arr i)等价于arr[i]得到第i行的首元素地址*(arr i) j第i行第j个元素的地址普通指针运算每次移动一个int的大小*(*(arr i) j)解引用得到第i行第j个元素的值3.3 行指针的定义与使用行指针是专门用来指向二维数组某一行的指针int arr[3][4] {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; // 定义一个指向包含4个int元素的一维数组的指针行指针 int (*p)[4] arr; // 通过行指针访问元素 printf(%d\n, *(*(p 1) 2)); // 等价于arr[1][2]输出74. 二维数组作函数参数在函数中处理二维数组时有三种常见的写法本质上都是传递行指针。4.1 三种传参方式方式1完整写法明确行列数void printArr(int arr[3][4], int row, int col){ for(int i0; irow; i){ for(int j0; jcol; j){ printf(%d , arr[i][j]); } printf(\n); } } // 调用 int main(){ int arr[3][4] {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; printArr(arr, 3, 4); return 0; }方式2省略行数必须指定列数函数参数中二维数组的行数可以省略但列数必须指定void printArr(int arr[][4], int row, int col){ // 函数体和上面一样 }方式3行指针写法最本质void printArr(int (*arr)[4], int row, int col){ // 函数体和上面一样 }4.2 注意事项为什么列数必须指定因为编译器需要知道每行的大小才能正确计算行指针的偏移量函数内部不能用sizeof(arr)/sizeof(arr[0])计算行数因为arr此时是一个指针不是数组5. 二维字符数组字符串数组二维字符数组本质上是字符串的数组常用于存储多个字符串。5.1 定义与初始化// 定义一个能存放3个字符串每个字符串最大长度为10的数组 char names[3][10] { zhangsan, lisi, wangwu };等价于char names[3][10] { {z,h,a,n,g,s,a,n,\0}, {l,i,s,i,\0}, {w,a,n,g,w,u,\0} };5.2 访问与遍历字符串数组// 访问单个字符串 printf(%s\n, names[0]); // 输出zhangsan // 遍历所有字符串 for(int i0; i3; i){ printf(%s\n, names[i]); }5.3 二维字符数组 vs 字符指针数组类型定义内存特点适用场景二维字符数组char arr[3][10]所有字符串连续存储每个字符串长度固定字符串长度固定可修改字符串内容字符指针数组char *arr[3]存储的是字符串的地址字符串本身存储在常量区字符串长度不固定只读字符串6. 动态二维数组拓展前面的二维数组都是静态数组大小在编译时确定。如果需要在运行时确定大小可以使用动态二维数组。6.1 方式1用一维数组模拟二维数组int rows 3, cols 4; // 申请连续的内存 int *arr (int*)malloc(rows * cols * sizeof(int)); // 访问元素 arr[i][j] → arr[i*cols j] for(int i0; irows; i){ for(int j0; jcols; j){ arr[i*cols j] i*cols j; } } // 使用完释放 free(arr);6.2 方式2指针数组实现动态二维数组int rows 3, cols 4; // 1. 先申请行指针数组 int **arr (int**)malloc(rows * sizeof(int*)); // 2. 为每一行申请内存 for(int i0; irows; i){ arr[i] (int*)malloc(cols * sizeof(int)); } // 访问元素和静态二维数组一样 arr[i][j] ... // 使用完释放顺序和申请相反 for(int i0; irows; i){ free(arr[i]); } free(arr);6.3 方式3用行指针实现连续存储的动态二维数组int rows 3, cols 4; // 申请一整块连续内存 int (*arr)[cols] (int(*)[cols])malloc(rows * cols * sizeof(int)); // 访问元素 arr[i][j] ...和静态二维数组完全一样 // 释放 free(arr);7. 数组高频易错点总结7.1 一维数组常见易错点易错1下标越界C语言不做数组下标越界检查编译不报错运行随机崩溃、乱码、篡改其他变量。int arr[3] {1,2,3}; arr[5] 99; // 严重越界编译器不报警运行隐患极大避坑循环边界严格写成 i 数组长度不要写成 i 长度。易错2数组名不能整体赋值数组名是地址常量不能直接赋值、不能自增。int a[5], b[5]; a b; // 错误 a; // 错误避坑只能循环逐个元素赋值或用 memcpy 内存拷贝。易错3函数内用sizeof求数组长度数组传参退化为指针sizeof得不到真实数组大小。void fun(int arr[]) { // 永远是指针大小 4/8字节得不到元素个数 int n sizeof(arr) / sizeof(arr[0]); }避坑手动把长度当参数传入函数。易错4部分初始化默认补0只针对全局/局部数组int arr[5] {1}; // 等价 {1,0,0,0,0}未显式初始化元素自动置07.2 二维数组核心易错点易错1初始化时省略列数行数可以省列数绝对不能省int arr[][] {1,2,3,4}; // 直接编译报错 int arr[][2] {1,2,3,4}; // 正确自动2行2列原因编译器需要列数计算每行内存偏移没有列数无法寻址。易错2分不清arr、arr[0]、arrint arr[3][4];arr行指针int (*)[4]1 跳过一整行arr[0]首行首元素地址int *1 跳过一个intarr整个二维数组地址int (*)[3][4]三者数值相同类型和步长完全不同是面试高频坑。易错3把二维数组传给函数随意省略列void print(int arr[][]) // 错误void print(int arr[][4]) void print(int (*arr)[4]) // 正确易错4混淆行指针与二级指针int **p不能直接接收二维数组名int arr[3][4]; int **p arr; // 类型不匹配编译警告运行崩溃正确接收只能用int (*p)[4] arr;易错5双层循环遍历颠倒行列外行循环控制列、内层控制行逻辑全乱打印排版错乱。固定规则外层i行内层j列。7.3 二维字符数组 易错坑易错1二维字符数组与字符指针数组混用char strs[3][10] {abc,123,xyz}; char *p[3] {abc,123,xyz};strs[3][10]内存可修改每个字符串有固定空间char *p[3]指向字符串常量区不能修改内容p[0][0] A; // 运行崩溃常量区不可写易错2字符串不手动补\0逐字符赋值时不主动加 \0printf 打印乱码、越界乱走。char s[10];s[0] a;s[1] b; // 少了 s[2] \0; 直接printf乱码、7.4 动态二维数组 易错坑易错1malloc后不判空内存申请失败返回NULL直接使用程序崩溃。int **arr (int**)malloc(n * sizeof(int*)); if(arr NULL) { perror(malloc fail); return -1; }易错2内存释放顺序颠倒、漏释放申请先申请行指针数组 → 再逐行申请释放必须先逐行free → 再free行指针数组顺序错、少free都会内存泄漏。易错3用一维模拟二维时下标算错公式i行j列 i * 列数 j容易写成 i * 行数 j全部错位。7.5 面试常考总结一句话一维数组名是首元素地址二维数组名是行地址二维数组传参列数必固定只能用行指针接收数组传参变指针函数内不能用sizeof求长度int** 不等于 二维数组指针绝对不能互相赋值字符指针数组指向常量字符串不可修改内容。总结二维数组本质是“数组的数组”内存按行优先连续存储数组名是行指针arr[i][j]等价于*(*(arri)j)二维数组作函数参数时列数必须指定本质传递的是行指针二维字符数组适合存储多个字符串注意和字符指针数组的区别动态二维数组有多种实现方式根据场景选择合适的方式

相关文章:

C语言数组专题:从一维到二维,吃透内存与指针

数组是 C 语言最核心的基础知识点,二维数组更是衔接一维数组、指针与函数的关键枢纽。本文由浅入深梳理一维到二维数组完整知识点,并总结高频易错点,帮你彻底学懂学透。1. 一维数组(基础)1.1 什么是一维数组一维数组是…...

用Requests和BeautifulSoup4爬取豆瓣电影Top250:手把手教你构建个人电影数据库

构建个人电影数据库:从豆瓣Top250到数据分析全流程实战 每次打开豆瓣电影Top250页面,总会被那些经典影片吸引。作为影迷,你是否想过拥有一个专属的电影数据库?不仅能随时查阅,还能进行个性化分析?本文将带你…...

用ICode闯关游戏学Python:range函数的15个实战用法(附避坑指南)

用ICode闯关游戏学Python:range函数的15个实战用法(附避坑指南) 在编程学习的道路上,枯燥的语法常常成为初学者的绊脚石。而ICode国际青少年编程竞赛却为我们打开了一扇趣味学习的大门——通过游戏化的闯关模式,让Pyth…...

深度盘点2026年三大高口碑碳带生产厂家,权威推荐选购指南

碳带作为工业打印的核心耗材,其选择直接关系到打印效率与标识稳定性。当前市场上,碳带适配性、耐候性和打印精度是决策者最关注的三大维度。2026年,随着智能制造和跨行业应用需求的增长,碳带技术正朝着高兼容性和极端环境适应性方…...

构建AI智能体驱动的个人操作系统:从工作流自动化到认知增强

1. 项目概述:构建你的智能体驱动个人操作系统如果你和我一样,每天被各种待办事项、项目想法、学习笔记和临时任务淹没,感觉自己的数字生活像一团乱麻,那么是时候重新思考我们与计算机的交互方式了。传统的操作系统管理的是文件和进…...

35岁+软件测试从业者:打破年龄魔咒,延续技术生命

在软件行业的快速迭代浪潮中,35岁似乎成了一道无形的门槛,横亘在众多技术从业者面前。对于软件测试从业者而言,这道门槛带来的焦虑尤为明显:一边是行业对自动化、智能化测试技术的需求激增,另一边是体力精力下滑、学习…...

基于Flappy框架构建生产级AI智能体:从工具封装到任务规划实战

1. 项目概述与核心价值最近在GitHub上看到一个挺有意思的项目,叫“pleisto/flappy”。乍一看名字,你可能会联想到那个经典的像素鸟游戏,但点进去才发现,这其实是一个关于“Flappy”的AI智能体框架。作为一个在AI和自动化领域摸爬滚…...

基于ChatGPT API与LaTeX的智能简历生成工具开发实践

1. 项目概述:当传统简历写作遇上AI每次更新简历,你是不是也和我一样头疼?对着空白的文档,明明有一肚子工作经验,却不知道如何把它们组织成专业、简洁、又能通过ATS(求职者追踪系统)筛选的文字。…...

如何用Hitboxer解决游戏键盘的终极痛点:告别按键冲突,提升竞技水平

如何用Hitboxer解决游戏键盘的终极痛点:告别按键冲突,提升竞技水平 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 在竞技游戏的激烈对抗中,每一次按键延迟或冲突都可能导致整…...

仅限前500名R工程师获取:Tidyverse 2.0自动化报告模板库(含FDA/ISO/金融监管合规元数据框架)

更多请点击: https://intelliparadigm.com 第一章:Tidyverse 2.0自动化报告范式的演进与合规价值 Tidyverse 2.0 不再仅是函数语法的迭代,而是以 rmarkdown、quarto 和 gt 为核心构建的可审计、可复现、可嵌入治理流程的自动化报告基础设施…...

Transformer库实战:从原理到NLP应用部署

1. 理解Transformer库的核心价值第一次接触Transformer库时,我被它处理自然语言任务的效率震惊了。这个由Hugging Face团队维护的开源库,已经成为NLP领域的标准工具集。不同于早期需要从零实现模型的日子,现在只需几行代码就能调用BERT、GPT等…...

统信UOS远程协助实战:从内网到外网,手把手教你用自带工具搞定远程桌面

统信UOS远程协助全场景实战指南:内网穿透与公网直连的终极解决方案 在数字化转型浪潮中,远程办公已成为企业运营和个人工作的刚需。统信UOS作为国产操作系统的佼佼者,其内置的远程协助工具凭借原生集成、安全稳定和操作简便三大优势&#xff…...

计算机系统——模拟病毒感染ELF可执行文件

事先声明:本文所述制作简易病毒的操作,只适用于计算机系统这门课中加深对于ELF可执行文件的理解,是一个等价的“安全实验版本”,禁止用于其他违反法律的用途!我们的目的是感染干净程序,让被感染的程序先输出…...

动手学深度学习(PyTorch版)深度详解(5):深度学习计算核心 —— 卷积操作、填充步幅、汇聚层与 LeNet 完整精讲

前言在深度学习的学习体系中,多层感知机(MLP)是基础入门模型,依托全连接层实现对数据特征的拟合,能够处理简单的表格数据、一维结构化数据分类与回归任务。但当我们面对图像、视频、二维空间序列这类具备空间结构特征的…...

Node.js统一LLM接口开发指南:多模型切换与生产实践

1. 项目概述:为什么我们需要一个统一的LLM接口? 如果你和我一样,在过去一两年里深度折腾过各种大语言模型(LLM)的API,那你一定对下面这个场景不陌生:今天项目要用OpenAI的GPT-4,明天…...

别再硬编码了!用Simulink.Parameter对象管理模型参数的保姆级教程

别再硬编码了!用Simulink.Parameter对象管理模型参数的保姆级教程 第一次接触Simulink建模时,我像大多数新手一样,直接在模块参数框里填写数值。直到某次修改一个电机控制模型,需要在20多个地方调整同一个参数值,才意识…...

SERA代码代理训练框架:低成本高效AI辅助编程方案

1. 项目概述:SERA代码代理训练框架 在当今AI辅助编程领域,代码代理(Coding Agents)正逐渐成为提升开发效率的核心技术。这类系统能够模拟开发者行为,通过理解代码库上下文、分析问题描述并生成有效的代码修改方案。然而传统训练方法面临两大瓶…...

期货量化模拟转实盘检查清单:延迟、成交偏差与异常处理

前言 模拟阶段表现稳定,转实盘后突然失真,是期货量化最常见的落地断层。 问题通常不在策略公式,而在执行链路细节:延迟、成交偏差、异常处理。转实盘前如果没有检查清单,团队容易把环境问题误判成策略失效。 一、延迟检…...

告别VSCode卡顿与插件冲突:一份详细的缓存与插件数据清理指南(附一键清理脚本)

深度优化VSCode性能:精准清理缓存与插件数据的终极指南 每次打开VSCode都要等待漫长的加载时间?插件突然失效却找不到原因?编辑器响应越来越迟钝?这些问题往往源于长期积累的缓存数据和插件残留。本文将带你深入理解VSCode存储机制…...

ARM SVE指令集:SMAX/SMIN极值运算原理与优化实践

1. ARM SVE指令集概述在当今处理器架构设计中,向量处理能力已成为衡量计算性能的关键指标。ARM SVE(Scalable Vector Extension,可扩展向量扩展)作为ARMv8-A架构的重要扩展,突破了传统SIMD指令集的固定宽度限制&#x…...

通过环境变量为Hermes Agent配置Taotoken自定义模型提供方的详细方法

通过环境变量为Hermes Agent配置Taotoken自定义模型提供方的详细方法 1. 准备工作 在开始配置前,请确保已安装 Hermes Agent 框架并创建了 Taotoken API Key。登录 Taotoken 控制台,在「API 密钥」页面生成新密钥并妥善保存。同时,在「模型…...

2026年必看:精选靠谱电商公司,购物无忧新选择

随着电商行业的发展进入精细化、全域化运营阶段,品牌对第三方代运营公司的专业度和技术能力要求越来越高。在这样的背景下,我们为大家精选了几家在特定领域或区域市场具备显著特色的电商代运营企业,帮助大家更好地理解当前市场上的优质服务商…...

海棠山铁哥用《第一大道》对决《灵魂摆渡・浮生梦》,不躺平我们还有机会吗

“努力十年,不如资本铺路。” 当这句吐槽在凌晨 2:15 刷屏,当《灵魂摆渡浮生梦》靠资本加持冲上热搜, 而你的项目、你的方案、你的热爱又一次石沉大海—— 不躺平,我们还有机会吗?01 凌晨的叩问资本的世界我们的世界10…...

LED驱动电路热管理:CCR散热设计与PCB选型实践

1. LED驱动中的热管理挑战在LED驱动电路设计中,恒流调节器(CCR)的热管理是决定系统可靠性的关键因素。作为一名从事LED驱动设计多年的工程师,我见过太多因为热设计不当导致的系统失效案例。CCR器件在工作时会产生显著的热量,这些热量如果不能…...

为什么93%的数据团队还在用Tidyverse 1.x写报告?Tidyverse 2.0的`{reportr}`与`{lifecycle}`双引擎正悄然重构企业数据交付标准

更多请点击: https://intelliparadigm.com 第一章:Tidyverse 2.0自动化数据报告的企业级演进全景 Tidyverse 2.0 不再仅是函数语法的迭代,而是面向企业级数据工程与合规报告场景的架构级重构。其核心演进体现在三方面:统一的元数…...

2026年阿里云Hermes Agent/OpenClaw搭建攻略+百炼token Plan配置解析攻略教程

2026年阿里云Hermes Agent/OpenClaw搭建攻略百炼token Plan配置解析攻略教程。OpenClaw和Hermes Agent是什么?OpenClaw和Hermes Agent怎么部署?如何部署OpenClaw/Hermes Agent?2026年还在为部署OpenClaw和Hermes Agent到处找教程踩坑吗&#…...

【轴承故障诊断】加权多尺度字典学习模型(WMSDL)及其在轴承故障诊断上的应用(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

SVE指令集与DECW指令:现代SIMD编程核心技术解析

1. SVE指令集概述:现代SIMD处理的核心利器在当今处理器架构设计中,向量化计算已成为提升性能的关键手段。Arm的SVE(Scalable Vector Extension)指令集作为新一代SIMD扩展,彻底改变了传统固定长度向量指令的局限性。我第…...

【Docker 27工业集群部署终极指南】:20年运维专家亲授高可用、零宕机落地五步法

更多请点击: https://intelliparadigm.com 第一章:Docker 27工业集群部署的演进逻辑与核心价值 Docker 27并非官方版本号,而是工业界对基于Docker Engine v24.0、配合Docker Compose V2.25与Swarm Mode增强套件所构建的高可靠集群范式的代称…...

终极指南:如何使用免费开源工具深度调试和优化AMD Ryzen处理器性能

终极指南:如何使用免费开源工具深度调试和优化AMD Ryzen处理器性能 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址…...