C语言【指针篇】(三)
C语言【指针篇】(三)
- 前言
- 正文
- 1. 数组名的理解
- 2. 使用指针访问数组
- 3. 一维数组传参的本质
- 4. 冒泡排序
- 5. 二级指针
- 6. 指针数组
- 7. 指针数组模拟二维数组
- 总结
前言
本文主要基于前面对指针的掌握,进一步学习:数组名的理解、使用指针访问数组、一维数组传参的本质、冒泡排序、二级指针、指针数组以及指针数组模拟二维数组。
正文
1. 数组名的理解
我们可能遇到过这样的代码
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
在这里&arr[0]表示取到第一个元素的首地址,而arr本身就是地址,地址与首元素的地址一样,如下代码可做验证:
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("arr = %p\n" , arr);return 0;
}

运行结果果然一致
结论:数组名和数组首元素的地址打印出的结果一模一样,即数组名就是数组首元素 的地址。
但是,有人又有这样的疑问:既然一样,它们所占字节是否也是相等的?如下代码
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("%zd\n", sizeof(arr));printf("%zd\n", sizeof(arr[0]));return 0;
}

显然不同,若arr是数组首元素的地址,输出应该是4或8(取决于系统中指针的大小)。
其实数组名是数组首元素的地址这一说法是正确的
但存在两个例外:
sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
&数组名,这里的数组名表示整个数组,取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的)。
我们可以测试如下代码
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf( "arr = %p\n", arr);printf("&arr = %p\n", &arr);return 0;
}
结果是一致的,那arr和&arr有啥区别呢?再看下面代码:
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] = %p\n", &arr[0]);printf("&arr[0]+1 = %p\n", &arr[0] + 1);printf("arr = %p\n", arr);printf("arr+1 = %p\n", arr + 1);printf("&arr = %p\n", &arr);printf("&arr+1 = %p\n", &arr + 1);return 0;
}

可以发现&arr[0]和&arr[0]+1相差 4 个字节,arr和arr+1相差 4 个字节,这是因为&arr[0]和arr都是首元素的地址,+1就是跳过一个元素。而&arr和&arr+1相差 40 个字节,这是因为&arr是数组的地址,+1操作是跳过整个数组。至此,数组名的意义就清楚了,数组名是数组首元素的地址,但有上述 2 个例外。
2. 使用指针访问数组
下面我们看如何使用指针快速访问数组,下面给出常见写法
注意:语句上面的注释代表等价
#include<stdio.h>int main()
{int arr[10] = { 0 };//使用指针操作数组int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;//输入for (int i = 0; i < sz; i++){/*scanf("%d", p);p++;*/scanf("%d", p + i);}//输出p = arr;for (int i = 0; i < sz; i++){/*printf("%d", *p);p++;*//*printf("%d ", *(p + i));*//*printf("%d ", arr[i]);*//*printf("%d ", p[i]);*/printf("%d ", i[arr]);//可以但不推荐,毕竟太奇怪了}return 0;
}
最后自己敲出来理解一下。
3. 一维数组传参的本质
数组可以传递给函数,本小节讨论数组传参的本质。先看一个问题,之前都是在函数外部计算数组的元素个数,能否把数组传给函数后,在函数内部求数组的元素个数呢?
#include <stdio.h>
void test(int arr[])//arr[]相当于指针变量,x64是8字节
{int sz2 = sizeof(arr)/sizeof(arr[0]);printf("sz2 = %d\n", sz2);
}
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int sz1 = sizeof(arr)/sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}
输出结果:


发现在函数内部没有正确获得数组的元素个数。
这是因为数组传参的本质是:数组名是数组首元素的地址,数组传参时传递的是数组名,即本质上传递的是数组首元素的地址。
所以函数形参部分理论上应该使用指针变量来接收首元素的地址。在函数内部写sizeof(arr)计算的是一个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分本质是指针,所以在函数内部无法求出数组元素个数。
void test(int arr[])//参数写成数组形式,本质上还是指针
{printf("%d\n", sizeof(arr));
}
void test(int* arr)//参数写成指针形式
{printf("%d\n", sizeof(arr));//计算一个指针变量的大小
}
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};test(arr);return 0;
}
两个的结果是一致的x64都是8;
总结:一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。
4. 冒泡排序
排序在数据结构中会重点讲,这里简单说一下冒泡排序
冒泡排序的核心思想是:两两相邻的元素进行比较。
方法1:
//方法1
void bubble_sort(int arr[], int sz)//参数接收数组元素个数
{int i = 0;for(i=0; i<sz-1; i++){int j = 0;for(j=0; j<sz-i-1; j++){if(arr[j] > arr[j+1]){int tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}
}
int main()
{int arr[] = {3,1,7,5,8,9,0,2,4,6};int sz = sizeof(arr)/sizeof(arr[0]);bubble_sort(arr,sz);int i=0;for(i=0;i<sz;i++){printf("%d",arr[i]);}return 0;
}
方法2 - 优化:
//方法2-优化
void bubble_sort(int arr[],int sz)
{int i=0;for(i=0;i<sz-1;i++){int flag=1;//假设这一趟已经有序了int j=0;for(j=0;j<sz-i-1;j++){if(arr[j]>arr[j+1]){int tmp = arr[j];arr[j]= arr[j+1];arr[j+1]=tmp;flag = 0;}}if(flag==1)//这一趟没交换就说明已经有序,后续无序排序了break;}
}
int main()
{int arr[]={3,1,7,5,8,9,0,2,4,6};int sz=sizeof(arr)/sizeof(arr[0]);bubble_sort(arr, sz);int i=0;for(i=0;i<sz;i++){printf("%d",arr[i]);}return 0;
}
5. 二级指针
指针变量也是变量,有地址,指针变量的地址存放在二级指针中。

#include <stdio.h>
int main()
{int a=10;int *pa = &a; int**ppa=&pa;return 0;
}
对于二级指针的运算有:
*ppa:通过对ppa中的地址进行解引用,找到的是pa,*ppa其实访问的就是pa。
int b = 20;
*ppa = &b;//等价于 pa = &b;
**ppa:先通过*ppa找到pa,然后对pa进行解引用操作*pa,找到的是a。
**ppa = 30;
//等价于*pa = 30;
//等价于a = 30;
int main()
{int a = 10;int* pa = &a;int** ppa = &pa; printf("%d\n", a);printf("%d\n", *pa);printf("%d\n", **ppa);return 0;
}

6. 指针数组
指针数组是存放指针的数组,类比整型数组存放整型、字符数组存放字符。指针数组的每个元素都是用来存放地址(指针)的,每个元素又可以指向一块区域。

简单示例:
int main()
{int a = 10, b = 20, c = 30,d = 40;int* parr[4] = { &a,&b,&c,&d };for (int i = 0; i < 4; i++){printf("%d ", *(parr[i]));}return 0;
}

7. 指针数组模拟二维数组
代码演示:
#include <stdio.h>
int main()
{int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 2,3,4,5,6 };int arr3[5] = { 3,4,5,6,7 };int* parr[3] = { arr1,arr2,arr3 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", parr[i][j]);}printf("\n");}return 0;
}
parr[i]用于访问parr数组的元素,parr[i]找到的数组元素指向了整型一维数组,parr[i][j]就是整型一维数组中的元素。

上述代码模拟出二维数组的效果,但实际上并非完全是二维数组,因为每一行并非是连续的。

总结
本文大部分内容都是围绕数组和指针两者结合使用,并且涉及排序这个数据结构的重要内容,喜欢或觉得有用的话还希望点赞关注收藏三连支持一下,谢谢。
相关文章:
C语言【指针篇】(三)
C语言【指针篇】(三) 前言正文1. 数组名的理解2. 使用指针访问数组3. 一维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组 总结 前言 本文主要基于前面对指针的掌握,进一步学习:数组名的理解、使用指针…...
DevSecOps普及:安全与开发运维的深度融合
一、引言 随着软件开发模式的演进,DevOps已成为现代软件工程的主流实践。然而,在传统的DevOps流程中,安全往往被视为开发和运维之外的额外环节,导致安全漏洞在产品交付后才被发现,增加了修复成本和风险。为了解决这一…...
【JAVA-数据结构】Map和Set
上一篇我们聊到了排序相关内容,这一篇我们对Map和Set进行一系列说明,大家自取。 1.搜索树 1.1 概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节…...
从 0 到 1,用 Python 构建超实用 Web 实时聊天应用
从 0 到 1,用 Python 构建超实用 Web 实时聊天应用 本文深入剖析如何运用 Python 的 Flask 框架与 SocketIO 扩展,搭建一个功能完备的 Web 实时聊天应用。从环境搭建、前后端代码实现,到最终运行展示,逐步拆解关键步骤࿰…...
轻松搭建:使用Anaconda创建虚拟环境并在PyCharm中配置
一、使用Anaconda创建虚拟环境 1. 安装Anaconda 2..conda常用的命令 3. 创建虚拟环境-以搭建MachineVision为例 4. 激活虚拟环境 5. 安装依赖包 二、PyCharm配置环境 在进行Python项目开发时,合理的环境管理是必不可少的,特别是当你在多个项目中…...
【新人系列】Python 入门专栏合集
✍ 个人博客:https://blog.csdn.net/Newin2020?typeblog 📝 专栏地址:https://blog.csdn.net/newin2020/category_12801353.html 📣 专栏定位:为 0 基础刚入门 Python 的小伙伴提供详细的讲解,也欢迎大佬们…...
linux ununtu安装mysql 怎么在my.cnf文件里临时配置 无密码登录
在 Ubuntu 中,若需通过修改 my.cnf 临时禁用 MySQL 的密码验证(例如忘记 root 密码需要重置),可以通过添加 skip-grant-tables 选项实现。以下是具体步骤: 步骤 1:编辑 MySQL 配置文件 1. 打开 MySQL 配置…...
git,bash - 从一个远端git库只下载一个文件的方法
文章目录 git,bash - 从一个远端git库只下载一个文件的方法概述笔记写一个bash脚本来自动下载get_github_raw_file_from_url.shreanme_file.shfind_key_value.sh执行命令 END git,bash - 从一个远端git库只下载一个文件的方法 概述 github上有很多大佬上传了电子书库…...
python生成的exe文件防止反编译(pyinstaller加密)
python生成的exe文件可以轻松的被破解,为了防止反编译,知乎友友们给出了很多不同的见解,其中主流的回答是pyinstaller加密和niutka打包python,本篇介绍的方法是第一种,pyinstaller打包的时候进行加密,防破解…...
Android移动应用开发实践-1-下载安装和简单使用Android Studio 3.5.2版本(频频出错)
一、下载安装 1.Android Studio3.5.2下载地址:Android Studio3.5.2下载地址 其他版本下载地址:其他版本下载地址 2.安装教程(可以多找几个看看) 安装 | 手把手教你Android studio 3.5.2安装(安装教程)_a…...
Android Audio实战——音频相关基础概念(附)
Android Audio 开发其实就是媒体源数字化的过程,通过将声波波形信号通过 ADC 转换成计算机支持的二进制的过程叫做音频采样 (Audio Sampling)。采样 (Sampling) 的核心是把连续的模拟信号转换成离散的数字信号。 一、声音的属性 1、响度 (Loudness) 响度是指人类可以感知到的…...
5分钟使用Docker部署Paint Board快速打造专属在线画板应用
文章目录 前言1.关于Paint Board2.本地部署paint-board3.使用Paint Board4.cpolar内网穿透工具安装5.创建远程连接公网地址6.固定Paint Board公网地址 💡 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住…...
vue实现根据点击或滑动展示对应高亮
页面需求: 点击左侧版本号,右侧展示对应版本内容并置于顶部右侧某一内容滚动到顶部时,左侧需要展示高亮 实现效果: 实现代码: <template><div><div class"historyBox pd-20 bg-white">…...
java练习(41)
ps:题目来自力扣 最接近的三数之和 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 import java.util.Arrays;class Solut…...
【二分查找 图论】P8794 [蓝桥杯 2022 国 A] 环境治理|普及
本文涉及的基础知识点 本博文代码打包下载 C二分查找 C图论 [蓝桥杯 2022 国 A] 环境治理 题目描述 LQ 国拥有 n n n 个城市,从 0 0 0 到 n − 1 n - 1 n−1 编号,这 n n n 个城市两两之间都有且仅有一条双向道路连接,这意味着任意两…...
Docker镜像面试题及参考答案
目录 Docker 镜像与容器的关系是什么?如何理解 “镜像为静态定义,容器为运行时实体”? 解释 Docker 镜像的联合文件系统(UnionFS)分层机制,为何这种设计能优化存储效率? Docker 镜像的 LABEL 标签有什么作用?如何通过标签管理多版本镜像? 镜像的 latest 标签有哪些…...
浅显易懂HashMap的数据结构
HashMap 就像一个大仓库,里面有很多小柜子(数组),每个小柜子可以挂一串链条(链表),链条太长的时候会变成更高级的架子(红黑树)。下面用超简单的例子解释: 壹…...
Fisher信息矩阵与Hessian矩阵:区别与联系全解析
Fisher信息矩阵与Hessian矩阵:区别与联系全解析 在统计学和机器学习中,Fisher信息矩阵(FIM)和Hessian矩阵是两个经常出现的概念,它们都与“二阶信息”有关,常用来描述函数的曲率或参数的敏感性。你可能听说…...
【HTML— 快速入门】HTML 基础
准备工作 vscode下载 百度网盘 Subline Text 下载 Sublime Text下载 百度网盘 vscode 下载 Sublime Text 是一款轻量好用的文本编辑器,我们在写前端代码时,使用 Sublime Text 打开比使用记事本打开,得到的代码体验更好,比 vscode…...
Docker 与 Serverless(无服务器架构)
Serverless(无服务器架构) 是一种新的云计算架构,它通过让开发者专注于业务逻辑而无需管理服务器基础设施,来简化应用的开发和部署。Serverless 模型通常由云服务提供商管理基础设施的所有方面,而开发者只需提供代码和…...
如何快速掌握LangGraph:构建智能工作流的5大秘诀
如何快速掌握LangGraph:构建智能工作流的5大秘诀 【免费下载链接】langgraph Build resilient language agents as graphs. 项目地址: https://gitcode.com/GitHub_Trending/la/langgraph LangGraph是一个强大的图网络框架,专门用于构建可编排、可…...
给AI代理加记录仪,值不值?从OpenClaw漏洞看日志监控的代价与边界
先说结论 日志监控能有效追溯AI代理的异常行为,但引入额外开销和复杂度,需要权衡安全与性能。 自建方案(如ELK)控制力强但成本高,云服务省心但可能受限于厂商锁定和功能边界。 实施前应明确监控粒度、告警策略和存储…...
React Context 状态管理陷阱与优化
React Context 状态管理陷阱与优化 React Context 是 React 提供的一种状态管理方案,能够避免 props 层层传递的繁琐,尤其适合全局状态共享。在实际开发中,开发者常常会陷入性能陷阱或设计误区,导致应用出现不必要的渲染或逻辑混…...
为什么你的vLLM缓存命中率低于行业均值?27家头部AI公司缓存配置审计报告首次公开
第一章:大模型工程化缓存策略与性能优化 2026奇点智能技术大会(https://ml-summit.org) 大模型推理服务在高并发场景下面临显著的延迟与资源开销挑战,缓存机制成为工程化落地的关键杠杆。合理设计缓存层级、键空间结构及失效策略,可将重复查…...
2026届最火的十大AI写作平台解析与推荐
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 在学术研究里,人工智能辅助撰写开题报告已然成了重要工具的一种,用户…...
Cisco 18系列AP通过u-boot实现tftp镜像启动的详细步骤解析
1. 理解Cisco 18系列AP的u-boot启动机制 当你拿到一台Cisco 18系列AP设备时,可能会遇到需要从网络加载镜像进行启动的情况。这就像我们电脑坏了需要从U盘重装系统一样,只不过这里用的是tftp协议通过网络来传输系统镜像。u-boot就是这个过程中的关键角色&…...
深入解析Wi-Fi AMPDU帧聚合技术:提升无线网络效率的关键
1. 为什么你的Wi-Fi总是不够快? 每次看视频卡顿、下载文件龟速时,你可能都以为是宽带的问题。但真相是:80%的无线网络卡顿都源于Wi-Fi协议本身的效率低下。想象一下快递员送包裹的场景——如果每次只送一个小件,大部分时间都花在来…...
从硬件原理到软件中断:深入解析耳机插拔与按键检测的实现逻辑
1. 耳机接口的硬件基础:从三段式到四段式 第一次拆解耳机接口时,我被那些细小的金属环搞晕了头。后来发现,这些看似简单的结构藏着精妙的电路设计。最常见的3.5mm耳机接口分为三段式和四段式两种,就像USB-A和Type-C的区别…...
数据摄取构建模块简介(预览版)(一)刺
一、语言特性:Java 26 与模式匹配进化 1.1 Java 26 语言级别支持 IDEA 2026.1 EAP 最引人注目的变化之一,就是新增 Java 26 语言级别支持。这意味着开发者可以提前体验和测试即将在 JDK 26 中正式发布的语言特性。 其中最重要的变化是对 JEP 530 的全面支…...
Room数据库迁移踩坑实录:从手动到自动的完整避坑指南
Room数据库迁移实战:从手动到自动的完整避坑指南 去年在重构一个百万级用户的金融类App时,我们团队在数据库迁移上栽了个大跟头。某个深夜的紧急更新后,部分用户的交易记录突然"消失",最终排查发现是漏掉了一个Migratio…...
