C语言中最难啃的硬骨头非这三个莫属

2023-05-16

C语言中最难啃的硬骨头非这三个莫属

75d82f0462a2559b586af213fcec0658.png

\\\插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的--)。最近比较闲,带做毕设,带学生参加省级以上比///绿色图标【‟で】liutianwang123

正文开始:\\\///b0dcbfef11ca81e6b6e1ffd2cb15bee3.png

afb7dd4e22d40b88c3d3a16d55192321.png

C语言在嵌入式学习中是必备的知识,审核大部分操作都要围绕C语言进行,而其中有三块“难啃的硬骨头”几乎是公认级别的。

0x01指针

指针公认最难理解的概念,也是让很多初学者选择放弃的直接原因。

指针之所以难理解,因为指针本身就是一个变量,是一个非常特殊的变量,专门存放地址的变量,这个地址需要给申请空间才能装东西,而且因为是个变量可以中间赋值,这么一倒腾很多人就开始犯晕了,绕不开弯了。C语言之所以被很多高手所喜欢,就是指针的魅力,中间可以灵活的切换,执行效率超高,这点也是让小白晕菜的地方。

指针是学习绕不过去的知识点,而且学完C语言,下一步紧接着切换到数据结构和算法,指针是切换的重点,指针搞不定下一步进行起来就很难,会让很多人放弃继续学习的勇气。

指针直接对接内存结构,常见的C语言里面的指针乱指,数组越界根本原因就是内存问题。在指针这个点有无穷无尽的发挥空间。很多编程的技巧都在此集结。

指针还涉及如何申请释放内存,如果释放不及时就会出现内存泄露的情况,指针是高效好用,但不彻底搞明白对于有些人来说简直就是噩梦。

在概念方面问题可以参见此前推文《对于C语言指针最详尽的讲解》,那么在指针方面可以参见一下大神的经验:

复杂类型说明

要了解指针,多多少少会出现一些比较复杂的类型。所以先介绍一下如何完全理解一个复杂类型。

要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样。

所以笔者总结了一下其原则:从变量名处起,根据运算符优先级结合,一步一步分析。

下面让我们先从简单的类型开始慢慢分析吧。

int p;

这是一个普通的整型变量

int p;

首先从P处开始,先与结合,所以说明P是一个指针。然后再与int结合,说明指针所指向的内容的类型为int型,所以P是一个返回整型数据的指针

int p[3];

首先从P处开始,先与[]结合,说明P是一个数组。然后与int结合,说明数组里的元素是整型的,所以P是一个由整型数据组成的数组。

int *p[3];

首先从P处开始,先与[]结合,因为其优先级比高,所以P是一个数组。然后再与结合,说明数组里的元素是指针类型。之后再与int结合,说明指针所指向的内容的类型是整型的,所以P是一个由返回整型数据的指针所组成的数组。

int (*p)[3];

首先从P处开始,先与结合,说明P是一个指针。然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组。之后再与int结合,说明数组里的元素是整型的。所以P是一个指向由整型数据组成3个整数的指针。

int **p;

首先从P开始,先与结合,说明P是一个指针。然后再与结合,说明指针所指向的元素是指针。之后再与int结合,说明该指针所指向的元素是整型数据。由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针。

int p(int);

从P处起,先与()结合,说明P是一个函数。然后进入()里分析,说明该函数有一个整型变量的参数,之后再与外面的int结合,说明函数的返回值是一个整型数据。

int (*p)(int);

从P处开始,先与指针结合,说明P是一个指针。然后与()结合,说明指针指向的是一个函数。之后再与()里的int结合,说明函数有一个int型的参数,再与最外层的int结合,说明函数的返回类型是整型,所以P是一个指向有一个整型参数且返回类型为整型的函数的指针。

int (p(int))[3];

可以先跳过,不看这个类型,过于复杂。从P开始,先与()结合,说明P是一个函数。然后进入()里面,与int结合,说明函数有一个整型变量参数。然后再与外面的结合,说明函数返回的是一个指针。之后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组。接着再与结合,说明数组里的元素是指针,最后再与int结合,说明指针指向的内容是整型数据。所以P是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数。

说到这里也就差不多了。理解了这几个类型,其它的类型对我们来说也是小菜了。不过一般不会用太复杂的类型,那样会大大减小程序的可读性,请慎用。这上面的几种类型已经足够我们用了。

细说指针

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。

要搞清一个指针需要搞清指针的四方面的内容:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。让我们分别说明。

先声明几个指针放着做例子:

(1)int*ptr;

(2)char*ptr;

(3)int**ptr;

(4)int(*ptr)[3];

(5)int*(*ptr)[4];

指针的类型

从语法的角度看,小伙伴们只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。

让我们看看上述例子中各个指针的类型:

(1)intptr;//指针的类型是int

(2)charptr;//指针的类型是char

(3)intptr;//指针的类型是int

(4)int(ptr)[3];//指针的类型是int()[3]

(5)int*(ptr)[4];//指针的类型是int(*)[4]

怎么样?找出指针的类型的方法是不是很简单?

指针所指向的类型

当通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。

从语法上看,小伙伴们只需把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。

上述例子中各个指针所指向的类型:

(1)intptr; //指针所指向的类型是int

(2)char*ptr; //指针所指向的的类型是char*

(3)int*ptr; //指针所指向的的类型是int*

(4)int(*ptr)[3]; //指针所指向的的类型是int(*)[3]

(5)int*(*ptr)[4]; //指针所指向的的类型是int*(*)[4]

在指针的算术运算中,指针所指向的类型有很大的作用。

指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当小伙伴们对C越来越熟悉时,就会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。

笔者看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。

指针的值

即指针所指向的内存区或地址。

指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。

在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。

以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。

指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。

以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?

指针本身所占据的内存区

指针本身占了多大的内存?只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据4个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。

0x02函数

面向过程对象模块的基本单位,以及对应各种组合,函数指针,指针函数

一个函数就是一个业务逻辑块,是面向过程,单元模块的最小单元,而且在函数的执行过程中,形参,实参如何交换数据,如何将数据传递出去,如何设计一个合理的函数,不单单是解决一个功能,还要看是不是能够复用,避免重复造轮子。

函数指针和指针函数,表面是两个字面意思的互换实际上含义截然不同,指针函数比较好理解,就是返回指针的一个函数,函数指针这个主要用在回调函数,很多人觉得函数都没还搞明白,回调函数更晕菜了。其实可以通俗的理解指向函数的指针,本身是一个指针变量,只不过在初始化的时候指向了函数,这又回到了指针层面。没搞明白指针再次深入的向前走特别难。

19a2711a633873aeb9bc0a908d874b4e.png

C语言的开发者们为后来的开发者做了一些省力气的事情,他们编写了大量代码,将常见的基本功能都完成了,可以让别人直接拿来使用。但是那么多代码,如何从中找到自己需要的呢?将所有代码都拿来显然是不太现实。

但是这些代码,早已被早期的开发者们分门别类地放在了不同的文件中,并且每一段代码都有唯一的名字。所以其实学习C语言并没有那么难,尤其是可以在动手锻炼做项目中进行。使用代码时,只要在对应的名字后面加上( )就可以。这样的一段代码就是函数,函数能够独立地完成某个功能,一次编写完成后可以多次使用。

很多初学者可能都会把C语言中的函数和数学中的函数概念搞混淆。其实真相并没有那么复杂,C语言中的函数是有规律可循迹的,只要搞清楚了概念你会发现还挺有意思的。函数的英文名称是 Function,对应翻译过来的中文还有“功能”的意思。C语言中的函数也跟功能有着密切的关系。

我们来看一小段C语言代码:

#include

int main()

{

puts("Hello World");

return 0;

}

把目光放在第4行代码上,这行代码会在显示器上输出“Hello World”。前面我们已经讲过,puts后面要带(),字符串也要放在()中。

在C语言中,有的语句使用时不能带括号,有的语句必须带括号。带括号的就是函数(Function)。

C语言提供了很多功能,我们只需要一句简单的代码就能够使用。但是这些功能的底层都比较复杂,通常是软件和硬件的结合,还要要考虑很多细节和边界,如果将这些功能都交给程序员去完成,那将极大增加程序员的学习成本,降低编程效率。

有了函数之后,C语言的编程效率就好像有了神器一样,开发者们只需要随时调用就可以了,像进程函数、操作函数、时间日期函数等都可以帮助我们直接实现C语言本身的功能。

C语言函数是可以重复使用的。

函数的一个明显特征就是使用时必须带括号(),必要的话,括号中还可以包含待处理的数据。例如puts("果果小师弟")就使用了一段具有输出功能的代码,这段代码的名字是 puts,"尚观科技"是要交给这段代码处理的数据。使用函数在编程中有专业的称呼,叫做函数调用(Function Call)。

如果函数需要处理多个数据,那么它们之间使用逗号,分隔,例如:

pow(10, 2);

该函数用来求10的2次方。

好了,看到这里你有没有觉得其实C语言函数还是比较有意思的,而且并没有那么复杂困难。以后再遇到菜鸟小白的时候,你一口一个C语言的函数,说不定就能当场引来无数膜拜的目光。

0x03结构体、递归

很多在大学学习C语言的,很多课程都没学完,结构体都没学到,因为从章节的安排来看好像,结构体学习放在教材的后半部分了,弄得很多学生觉得结构体不重要,如果只是应付学校的考试,或者就是为了混个毕业证,的确学的意义不大。

如果想从事编程这个行业,对这个概念还不了解,基本上无法构造数据模型,没有一个业务体是完全使用原生数据类型来完成的,很多高手在设计数据模型的时候,一般先把头文件中的结构体数据整理出来。然后设计好功能函数的参数,以及名字,然后才真正开始写c源码。

如果从节省空间考虑结构体里面的数据放的顺序不一样在内存中占用的空间也不一样,结构体与结构体之间赋值,结构体存在指针那么赋值要特别注意,需要进行深度的赋值。

递归一般用于从头到位统计或者罗列一些数据,在使用的时候很多初学者都觉得别扭,怎么还能自己调用自己?而且在使用的时候,一定设置好跳出的条件,不然无休止的进行下去,真就成无线死循环了。

对于结构体方面的知识,可以参见此前推送的文章《C语言结构体(struct)最全的讲解(万字干货)》。具体也可以参见大佬的经验:

相信大家对于结构体都不陌生。在此,分享出本人对C语言结构体的研究和学习的总结。如果你发现这个总结中有你以前所未掌握的,那本文也算是有点价值了。当然,水平有限,若发现不足之处恳请指出。代码文件test.c我放在下面。在此,我会围绕以下2个问题来分析和应用C语言结构体:

.C语言中的结构体有何作用

.结构体成员变量内存对齐有何讲究(重点)

对于一些概念的说明,我就不把C语言教材上的定义搬上来。我们坐下来慢慢聊吧。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言中最难啃的硬骨头非这三个莫属 的相关文章

  • C++3--构造函数、冒号语法

    一 构造函数 1 为什么要有这个概念 xff1a 例如下面的代码 xff0c 对于Table类 xff0c 可以通过t Set公有的方法给对象设置内容 xff0c 但是如果每次创建对象都调用该方法设置信息 xff0c 就会有点麻烦 xff0
  • c/c++语言面试题目整理

    1 static有什么用途 xff1f 在C语言中 xff0c static主要定义全局静态变量 xff0c 定义局部静态变量 xff0c 定义静态函数 限制变量的作用域 xff0c 设置变量的存储域 static 关键字主要有两种作用 x
  • c语言宏定义一个MAX函数

    1 span class token macro property span class token directive hash span span class token directive keyword define span sp
  • C++ -- 异常:try、throw、catch

    异常 申请内存的时候 xff0c 内存不够用 空间配置器申请失败 xff0c 不做处理 拷贝时内存错误 除数分母不能为0 等情况 会抛出异常 try throw catch 1 使用示例 1 xff09 除数为零 xff0c 情况 未处理情
  • C++ -- 笔试题

    1 下列对派生类的描述中错误的说法是 D A 派生类至少有一个基类 B 派生类可作为另一个派生类的基类 C 派生类除了包含它直接定义的成员外 xff0c 还包含其基类的成员 D 派生类所继承的基类成员的访问权限保持不变 2 当派生类中有和基
  • Linux 用户切换、修改用户名、修改密码

    一 用户切换 34 34 xff1a 普通用户提示符 34 34 xff1a root用户提示符 1 普通用户到root 方式一 xff1a 命令 xff1a su 然后输入root密码 此种方式只是切换了root身份 xff0c 但She
  • C++中的.和::和:和->的区别

    在学习C 43 43 的过程中我们经常会用到 和 和 xff1a 和 gt xff0c 在此整理一下这些常用符号的区别 1 A B则A为对象或者结构体 xff1b 2 A gt B则A为指针 xff0c gt 是成员提取 xff0c A g
  • AirSim学习日志 5-LQR实现无人机轨迹跟踪

    1 LQR控制器算法原理推导 1 1 状态反馈控制 连续线性系统的状态空间表示为 x
  • 英语常用短语

    1 xff0e 经济的快速发展 the rapid development of economy 2 xff0e 人民生活水平的显著提高 稳步增长 the remarkable improvement steady growth of pe
  • PX4进入系统控制台以及运行程序

    这里提供进入控制台两种办法 1 运行 Tools mavlink shell py dev ttyACM0 是我进入Px4系统控制台的命令 xff0c 进入之后应该是这样 Pixhawk src Firmware Tools mavlink
  • 哈哈,终于知道CSDN怎么改头像了

    话说之前一直郁闷 xff0c 说改头像的功能还没修好 xff0c 一直说服务器错误 今天偶尔发现 xff0c 右上角有个设置 xff0c 原来在这里可以改 我还发了几封邮件给CSDN的admin xff0c 居然只知道道歉 xff0c 不告
  • Hello Sky! pixhawk第一个例程学习解读

    学习px4的第一个程序 xff0c 这个例子作为官方给出解释的例程 xff0c 对于新手上手来说 xff0c 是很好的范例 接下来我对照程序源码 xff08 已经经过自己的修改和添加注释 xff09 进行讲解 1 PX4 INFO是标准的L
  • ShadowSSDT Hook

    ShadowSSDT表的获取 这里的ShadowSSDT表的获取是通过函数KeAddSystemServiceTable来获取的 使用这个函数的原因 xff1a 1 这个函数是已经导出的 xff0c 可以在代码中直接使用 2 这个函数里面使
  • 2013&2014

    2013总结 2013 毕业了 xff0c 算是正式工作半年 xff0c 2013年7月开始 xff0c 算是我的生活 xff0c 工作之外的时间都是自己的 一 收获 1 压力测试 差不多算是一个月的时间 xff0c 疯狂的一个月 xff0
  • BCTF总结

    缘由 上周 xff0c 我们小组Sigma参加了 百度杯 BCTF比赛 xff0c 经历了难忘的双休 xff0c 这次的BCTF跟以前参加的国内类似的安全比赛有些不同 xff0c 时间只有48小时 xff0c 题目不多 xff0c 但难度大
  • BCTF_海报探秘(300)

    这个题目来自上周的BCTF比赛 xff0c 题目是海报探秘 xff08 300 xff09 xff0c 一张png图片中隐藏了KEY xff0c 解出KEY xff0c 具体报告 xff0c 请下载 xff1a http download
  • 博客转移

    最近好久不来CSDN了 xff0c 自己搭建了一个博客 欢迎各位去新博客留言 http www l0g1n cn 以前学习汇编的博客 http www asmedu net blog user usermain jsp neighborId
  • 《Windows程序设计》之BLOKOUT1

    LRESULT CALLBACK WndProc HWND hwnd UINT message WPARAM wParam LPARAM lParam static BOOL fBlocking fValidBox static POINT
  • 天猫抢红包小工具源码

    时间很仓促 xff0c 写的很乱 xff0c 也就能用明天一天了 主要还是学习这个过程 include lt Windows h gt include lt stdio h gt include lt iostream gt include

随机推荐

  • vs2010开发qt程序debug正常,release出错

    在debug模式下 xff0c 配置的动态链接库是qtmaind lib QtGuid4 lib QtCored4 lib 这些链接库 xff0c 在release模式下是不适用的 xff0c 进入到qt的目录下 xff0c 发现了有一些不
  • cout与wcout

    一直以来只知道有cout用来输出 xff0c 今天用cout输出wchar时出现问题了 xff0c 输出结果是一段地址 xff0c 才发现了wcout的存在 使用wcout输出中文时 xff0c 又出现问题 xff0c 中文输出不了 xff
  • (极力推荐)BW:How to load and generate a custom hierarchy in SAP Business Warehouse (SAP BW) without using flat files

    这篇文章 xff0c 真是救了哥 xff0c 所以非常感谢这位作者 你看 xff0c 人家也是 2011 年 2 月 3 号才写的 xff0c 量身打造 xff0c 多好的人 xff0c 刚好赶上我了 PS xff1a 前段时间设计的文本抽
  • 主引导记录(MBR)信息分析与获取

    前段时间在安装黑苹果时 xff0c 发现一个问题 xff0c 电脑在启动时 xff0c 会找激活分区 xff0c 如果没有找到 xff0c 那就启动不起来 那能否写个小程序读取一下MBR信息 xff0c 把激活分区换成其它 xff0c 搞点
  • Duilib登录窗口

    先上效果图 xff08 自己感觉还不错 xff09 xff1a 功能不完善 xff0c 一是为了熟悉xml的写法 xff0c 手写 xff0c 不建议使用编辑器 xff0c 二了为了理顺程序的流程 xff0c 加入了部分注释 xml文件 l
  • React框架制作移动端H5可拖拽的悬浮图标

    一 前言 在跟需求方对接研发的需求 xff0c 人家说要做一个悬浮在页面的图标 xff0c 点击完截图然后进入一个新页面 xff0c 填完一些数据 xff0c 提交给接口 那么问题来了 xff0c 怎么制作一个悬浮框 xff0c 还是可拖拽
  • GIT回退到指定版本的两种方法(reset/revert)

    实现多人合作程序开发的过程中 xff0c 我们有时会出现错误提交的情况 xff0c 此时我们希望能撤销提交操作 xff0c 让程序回到提交前的样子 xff0c 本文总结了两种解决方法 xff1a reset revert 命令特点reset
  • W: GPG 错误:http://ppa.launchpad.net lucid Release: 由于没有公钥,无法验证下列签名: NO_PUBKEY FAF69C646FF368B7的问题

    在安装更新时 xff0c 即在运行 xff0c 命令行sudo apt get update 或者运行更新管理器的时候 xff0c 出现W GPG 错误 xff1a http ppa launchpad NET lucid Release
  • Ubuntu操作系统下Pixhawk原生固件PX4的编译方法

    在Ubuntu上搭建环境相对简单许多 xff0c 按照PX4中文维基官网的教程即可 工具链安装 权限设置 把用户添加到用户组 dialout xff08 如果这步没做 xff0c 会导致很多用户权限问题 xff09 span class h
  • Tegra TK1中文输入法出错解决

    输入 xff1a ibus daemon drx
  • ubuntu16.0.4查看内存实时动态

    1 命令行输入 xff1a top 2 安装命令如下 xff1a sudo apt get install htop 安装完后 xff0c 直接输入命令 xff1a htop
  • xsense免注册资料网址

    http www avicom cn
  • loam中imu消除重力加速度的数学推导

    最近在看loam的源码发现里面有一段关于imu消除重力加速度的源码 xff0c 刚开始看不明白后来终于搞清楚了 xff0c 欢迎大家批评指正 要理解这个问题首先得明白欧拉角到旋转矩阵的变换 先上图 此图描述的是先绕X xff0c 再绕Y x
  • rc=20 > Connect to SAP gateway failed

    这种错误 xff0c 我是在一台用户的电脑上碰到的 xff0c 解决方案很简单 xff1a 把Computer Name换成英文 汗了许久
  • 循环结构

    循环结构 while循环 while是最基本的循环 xff0c 它的结构为 xff1a 只要布尔表达式为true xff0c 循环就会一直执行下去 我们大多数情况是会让循环停止下来的 xff0c 我们需要一个让表达式失效的方式来结束循环 少
  • 社会闲散人士或者普通大学生想走嵌入式方向该怎么学比较有效

    社会闲散人士或者普通大学生想走嵌入式方向该怎么学比较有效 先说观点 1这几年嵌入式开发的薪水已经非常高了 我们实验室做嵌入式的学长 xff0c 年薪平均30w以上 2嵌入式开发的竞争力很小 相比算法等 3嵌入式开发薪水的高低与学校的好坏挂钩
  • 单片机最小系统着实让人着迷

    单片机最小系统着实让人着迷 插播一条 xff1a 我自己在今年年初录制了一套还比较系统的入门单片机教程 xff0c 想要的同学找我拿就行了免費的 xff0c 私信我就可以哦 点我头像黑色字体加我地球呺也能领取哦 最近比较闲 xff0c 带做
  • 听我一句劝,单片机不要去学STM32真的

    听我一句劝 xff0c 单片机不要去学STM32真的 插播一条 xff1a 我自己在今年年初录制了一套还比较系统的入门单片机教程 xff0c 想要的同学找我拿就行了免費的 xff0c 私信我就可以哦 点我头像黑色字体加我地球呺也能领取哦 最
  • uboot启动内核是什么,认识 uboot 和 内核 之间不可不说的关系

    uboot启动内核是什么 xff0c 认识 amp nbsp uboot和内核之间不可不说的关系 uboot镜像为 amp nbsp uboot bin xff0c Linux镜像为 amp nbsp zImage 嵌入式设备中的分区表是自
  • C语言中最难啃的硬骨头非这三个莫属

    C语言中最难啃的硬骨头非这三个莫属 插播一条 xff1a 我自己在今年年初录制了一套还比较系统的入门单片机教程 xff0c 想要的同学找我拿就行了免費的 xff08 禾 厶 亻 言 手 戈 xff09 最近比较闲 xff0c 带做毕设 xf