C语言学习篇
文章平均质量分 59
C语言短小但精悍,需不断精进!
机器灵魂注入师
一名在不断精进的嵌入式软件开发工程师,很庆幸能从事自己喜欢的工作。
愿你要永远抱有一颗好奇之心。
愿你出走半生,归来仍是少年。
展开
-
【C语言内功心法】__DATE__和__TIME__帮你构建更完善的软件版本信息
和 是 语言中的两个内置宏,你可以理解为两个字符串值,这两个宏用于记录编译时的时间,也就是预编译的时候,编译器以字符串的形式赋予这两个宏当前编译程序系统的时间。所以这两个宏的重要特性就很简单粗暴了在嵌入式软件开发的过程中,尤其是比较复杂的软件系统,是需要不断迭代版本的。由于大系统迭代版本比较多,且涉及到多个模块开发人员的合作,在解bug、对齐各个模块软件状态等情况下,一个独特的版本信息是至关重要的。一般而言,开发人员会采用诸如 的方式去定义版本号,但是这种方式存在一些弊端:不能告知这个版本的具体生成时间原创 2022-12-04 17:44:39 · 625 阅读 · 0 评论 -
C语言中的字节对齐
概念对齐跟数据在内存中的位置有关。一般设置的对齐方式为1,2,4字节对齐方式。特殊的是,如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐,举个例子:比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是自然对齐的。字节对齐的写法和编译器有关,比如MDK、IAR、GCC的基本实现都不一样,不过可以使用宏重定义来统一起来。目的如果让一个变量的内存地...原创 2020-02-13 15:37:57 · 3310 阅读 · 0 评论 -
【C语言内功心法】inline -- 提高函数的运行效率
记得早先少年时大家诚诚恳恳说一句 是一句清早上火车站长街黑暗无行人卖豆浆的小店冒着热气从前的日色变得慢车,马,邮件都慢一生只够爱一个人从前的锁也好看钥匙精美有样子你锁了 人家就懂了木心 – 《从前慢 》一、inline 为何诞生?在使用 C 语言进行编程时,如果有一些函数被频繁调用,就会不断地有函数跳转的操作,随之也会频繁带来入栈、出栈等操作,不经影响效率,还会大量占用栈等空间。如何解决这个问题?我们可以选择用类似的代码段直接实现该函数功能,这样就不存在函数调用带来的各种问.原创 2022-03-17 23:35:42 · 3209 阅读 · 2 评论 -
常见 C 语言标准库函数的使用
求取字符串的长度:size_t strlen(const char* str)输入字符串 str,返回其长度(不包括\0),需要包含头文件 string.h。原创 2022-02-08 16:07:50 · 925 阅读 · 0 评论 -
函数自动初始化的机制 -- __attribute__((section(x)))的使用
一、简要介绍__attribute__ 是 GNU C 实现的编译属性设置机制,也就是通过给函数或者变量声明属性值,以便让编译器能够对要编译的程序进行优化处理。更多详细内容,请看这篇官网文档:《Unixwiz.net - Software Consulting Central -- Using GNU C __attribute__》而对于 section 这个关键字,我们可.原创 2021-11-27 11:43:15 · 5969 阅读 · 0 评论 -
【C语言】关于结构体的的注意事项
愿中国青年都摆脱冷气,只是向上走,不必听自暴自弃者流的话。能做事的做事,能发声的发声。有一分热,发一分光,就令萤火一般,也可以在黑暗里发一点光,不必等候炬火。此后如竟没有炬火:我便是唯一的光。倘若有了炬火,出了太阳,我们自然心悦诚服的消失,不但毫无不平,而且还要随喜赞美这炬火或太阳;因为他照了人类,连我都在内。——鲁迅《热风·随感录四十一》使用结构体需要注意的事项先假设有这样的结构体struct st{ char a; int b; float c;}test;如下是注意事项.原创 2021-10-30 23:28:47 · 1285 阅读 · 0 评论 -
【C语言关键字】typedef -- 爱给类型取别名的家伙
对讨厌的事说不出讨厌,对喜欢的事也总是偷偷摸摸。– 太宰治 《人间失格》说明typedef 来源于英文单词 “type define”,看着英文的意思是类型定义,其实作用应该是类型重命名。它的作用是给各种类型取别名。为什么要使用这个关键字来给类型取别名呢?原来的名字不香吗?其实使用这个关键字也有一些场景:一些复杂的类型,比如一些复杂的指针定义,使用 typedef 后就可以给复杂的类型定义取一个简单的名字,方便使用。方便工程移植,会给一些基本类型取个固定的别名,方便在不同架构或者.原创 2021-10-26 00:21:14 · 380 阅读 · 0 评论 -
【C语言内功心法】const -- 赋予变量只读属性
const,源自单词 constant,有“恒定的;不变的”的意思,用它来修饰变量,赋予变量只读属性。const 修饰的变量只能在定义的时候初始化,别的地方不允许改变它的值,但是请记住,它依然还是变量。注意事项在一些单片机系统中,使用 const 修饰的变量(比较常见的是数组),其值是保存在 FLASH 中的,读取的时候直接从FLASH 中读取,不会占用内存(RAM)的空间,大大节省了内存的使用,尤其是一些固定数据的数组.原创 2021-10-25 20:26:35 · 640 阅读 · 0 评论 -
【C语言关键字】sizeof -- 被误解为函数的关键字
我的不幸,恰恰在于我缺乏拒绝的能力。我害怕一旦拒绝别人,便会在彼此心里留下永远无法愈合的裂痕。– 太宰治 《人间失格》说明sizeof,英文应该是 “size of”,也就是 某某的大小 的意思。这个关键字的作用是计算变量的所占字节的大小,当然也可以计算变量类型的大小。注意事项括号不是非必须,比如计算变量的时候,可以使用 sizeof(变量名) 或者 sizeof 变量名,但是如果是计算变量类型的画话,就必须加括号,sizeof int 是不被允许的,因为容易让编译器误认为 sizeof .原创 2021-10-24 23:02:16 · 5526 阅读 · 0 评论 -
【C语言关键字】static
胆小鬼连幸福都会害怕,碰到棉花都会受伤,有时还被幸福所伤。– 太宰治 《人间失格》说明static,英文单词的意思是“静态的”。该关键字用来修饰变量和函数,作用是约束变量和函数的作用范围。该关键字修饰函数和变量的效果是不同的,下面逐一说明。修饰变量(分为全局变量和局部变量)修饰全局变量作用是约束其作用范围为被定义的文件,比如你定义其所在的 .c 文件。其实更准确的说是,其作用范围是自定义处开始到文件结尾。修饰局部变量因为局部变量自身就自带约束其作用范围只能在其定义的代码块(函数等).原创 2021-10-24 17:41:48 · 2957 阅读 · 0 评论 -
【C语言关键字】register -- 追求更快
古今之成大事业、大学问者,必经过三种之境界。“昨夜西风凋碧树,独上高楼,望尽天涯路。’此第一境也。“衣带渐宽终不悔,为伊消得人憔悴。”此第二境界。“众里寻她千百度,蓦然回首,那人却在,灯火阑珊处。”此第三境也。王国维 – 《人间词话》个人觉得在技术甚至是关于人生的理解:第一境界,立志,经历一番痛苦的迷茫与思索,找到目标和规划。第二境界,为了实现目标,努力去奋斗,甚至成癫成魔,沉迷其中,跟着了魔一样。第三境界,最后发现所追求的东西其实就是这不断成长的过程,是这充实而有趣的人生旅途。说明.原创 2021-10-24 01:11:08 · 3556 阅读 · 0 评论 -
C语言的 switch 语句的底层汇编具体实现(以ARM汇编为例讲解)
我于昨晚去世,走时心如止水。我于今早重生,来时心怀暖阳。敬你岁月无波澜,祝我余生不悲欢!- 选自散文集《我在人间凑数的日子》一、参考资料ARM M3/M4汇编指令TBB TBH实现复杂表格跳转C语言switch语句的汇编语言实现C语言汇编代码分析(switch case)本文是在看大佬们的文章后自己总结的,感谢大佬们的文章分享。二、具体分析我们知道 C 语言的 switch 关键字可以让一些情况下的分支判断变得更简洁,可以避免大量使用 if-else if-else不断判断的情况,让代码.原创 2021-10-18 21:01:02 · 4348 阅读 · 0 评论 -
以rtthread内核代码为例,内联函数关键字 inline 前为啥需要添加 static 的关键字?
“我努力奔跑,只为追上曾经被寄予厚望的自己。”金州勇士队队员 - 肖恩·利文斯顿本文以 RT-Thread 操作系统为例:我们可以在 RT-Thread 的内核代码的 kservice.c 中有关于 inline 的宏定义(省略中间无关的代码):#ifdef __CC_ARM /* ARM Compiler */ #define rt_inline static __inline#elif define.原创 2021-09-29 00:31:05 · 660 阅读 · 0 评论 -
搜索子字符串函数 strstr() 的标准库实现
权力的游戏最近经常使用到在某个字符串中搜索某个字符串的功能,本来还想着如何去实现,后来发现 C 语言的标准库函数就有提供,借鉴(抄袭 )不香吗?下面就来一波源码分析。首先这个函数的作用是搜索子字符串,如果使用标准库函数,那么就得包含头文件 <string.h>。看一下函数原型:char *strstr(const char *s1, const char *s2);也就是在 s1 中搜索s2 字符串出现的位置。返回指针,此指针指向 s1 字符串中的字符第一次出现在 s2 字符串中原创 2021-07-04 11:19:25 · 454 阅读 · 0 评论 -
【C语言内功心法】__weak -- 示弱也是一种强大
在使用STM32的hal库的时候,我们常常可以看到很多库自带的函数有很多是使用__weak修饰的,比如:这个修饰符的作用是什么呢?先看看单词的意思:我们可以知道这是弱的意思,具体弱在哪里?又是和什么比较?其实函数名称前面加上__weak 修饰符,我们一般称这个函数为“弱函数”。加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,...原创 2018-08-13 13:47:20 · 42726 阅读 · 18 评论 -
C语言入坑 ---- 不要轻易忽略volatile的用处
前言虽然看过很多C语言相关的书,volatile这个关键字都熟的不能再熟了,但是平时在使用C语言开发项目的时候,却常常忽略使用该关键字。终于,还是才在了坑上。在一个使用STM32的开发项目上,由于没有使用volatile,加之开启了最高的优化等级,出了个bug,折腾了我不少时间。类似这种uint8_t time10msFlag = 0;void Wait10ms(void){ time10msFlag = 0; while(!time10msFlag); //等待中断给 time10msF转载 2021-03-21 20:21:03 · 669 阅读 · 0 评论 -
C语言:关于函数名和数组名的理解
前言说到函数名和数组名,很多人对其代表的意思理解的还不是很清楚,甚至是错误的,本文试图将其所代表的意思描述清楚。数组名数组的定义通常是如下这种类型:int a[10];首先说明,数组名和指针虽然长得像,但是一点关系的没有(比如无法a++),很多用法都不同,可以当做两个东西。考虑到数组名作为右值的情况:数组名a作为右值时,代表的是数组首元素的地址(而不是数组的地址,其应为&a),也就是和&a[0]一样。(注:用在sizeof(a)时,不算右值,代表的是数组名)。值得一提的是原创 2021-03-03 23:03:51 · 1457 阅读 · 2 评论 -
指针的各种定义汇总
指针的定义指针的定义有些很容易,比如指针数组和数组指针,如何区别他们,其实可以使用运算符的优先级来判断,比如下面数组指针和指针数组,如果没有**(),由于 [] 的优先级更高,所以p**是数组而不是指针。遇到复杂一点的定义,要学会结合基本的类型进行拆分,得到其本质属性。一般指针的定义type *p = NULL; //type 可以是int、char等类型数组指针(指向数组的指针)int (*p)[5];//指向数组大小为5的数组的指针,是指针//int *p[5];//而这是存储5原创 2021-03-03 20:28:51 · 1083 阅读 · 1 评论 -
《C程序设计语言》 -- 重点笔记
《C程序设计语言》笔记第一章 入门介绍推荐每行只写一条语句,并且在运算符的两边都添加空格以使运算组合显得更加清楚。花括号的位置不那么重要,尽管人们都有各自喜欢的风格。我们从几种流行的风格中选取了一种,你可以选择一种适合自己的风格,并一直使用它。按惯例符号常量都用大写字母来书写,这样可以很容易地与小写字母书写的变量名相区别。使用符号常量代替具体值 ,因为它们会让程序的可读性更好。这对于...原创 2020-03-09 23:26:10 · 491 阅读 · 0 评论 -
内联函数 —— C 中关键字 inline 用法解析(转载)
一、什么是内联函数在C语言中,如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗。为了解决这个问题,特别的引入了inline修饰符,表示为内联函数。栈空间就是指放置程式的局部数据也就是函数内数据的内存空间,在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足所造成的程式出错的问题,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。下面我们来看...转载 2020-02-11 10:49:44 · 4325 阅读 · 0 评论 -
函数指针怎样传递
来看一段代码(代码没啥具体意义): void a(int* x){ return ++x;}void b(void (*p)(int* x)){ int y=5; return *p(y);}void main(){ int a=1; a(&x); b(a);} 其实令我疑惑的是倒数第二行...原创 2017-09-05 11:37:38 · 2773 阅读 · 0 评论 -
字面值(常量)到底是什么数据类型
当一个程序内出现出现整形字面值时(比如在宏定义中#define A 3,中的3),他是属于整形家族9种不同数据中的哪一种(int,unsigned int等)? 其实我们可以在这些字面值后面添加一个后缀来改变缺省的规则,比如123L(或其小写)表明其是长整型,123u(或123U)等等。 当然,如果没有添加后缀,即其处在缺省的状态,那么它是能容纳整个整形中的最短原创 2017-10-31 23:15:31 · 2354 阅读 · 0 评论 -
C语言之static关键字的使用
在C语言中,static关键字总是让许多人迷糊,因为对应不同的上下文,static关键字有不同的意思。其实我们只要记住下面两点即可。1、当它用于函数定义或用于代码块之外的变量定义(即全局变量)时,修改了标识符的链接属性,即从external变成了internal,但它们的存储类型不受影响。作用就是这种被static修饰的函数或全局变量只能在其所在的源文件中被访问,别的文件无法访问他们。2、原创 2017-11-02 23:11:18 · 544 阅读 · 0 评论 -
sizeof用于判断表达式的长度时的注意事项
我们来看这样一种情况:sizeof(a=b+1);//假设b初始值为1,a初始值为5很多人一定认为a的结果是6。 下面放上一段测试代码: #include "stdio.h" int main(void) { int a=5,b=1,c; printf("a=%d\n",a); c=sizeof(a=b+1); p原创 2017-11-03 21:47:43 · 1812 阅读 · 0 评论 -
怎样定义和声明全局变量和函数最好?
一个全局变量或函数可以 (在多个编译单元中) 有多处 “声明”, 但是 “定义” 却只能允许出现一次。 定义是分配空间并赋初值 (如果有) ,声明则是给需要调用该变量或者函数的地方声明该变量或函数,使编译不出错。 最好的安排是在某个相关的 .c 文件中定义, 然后在其对应的头文件 (.h)(用extern修饰) 中进行外部声明, 在需要使用的时候, 只要包含对...原创 2018-03-15 13:15:32 · 1859 阅读 · 1 评论 -
关于嵌入式开发的C语言基础总结
一、位操作C语言支持的6种位操作符如下:1.不改变其他位的值的状况下,对某几个位进行设值。方法:先对需要设置的位用&操作符(对应位&0)进行清零操作,然后用|操作符设值(对应位|你想要设定的值)。举例:改变 GPIOA-> BSRRL 的状态 GPIOA-> BSRRL &=0XFF0F; //将第 4-7 位清 0GPIOA-&...转载 2018-03-11 20:15:16 · 15274 阅读 · 0 评论 -
计算一个二进制数的1的位数bitcount
1. 一种比较好理解的方法是:int bitcount(d unsigned x){ int b; for (b = 0; x != 0; x >>= 1) if (x & 01) b++; return b;}该方法每次清除x的一位,并判断该位是否为1,若是,则b加1,否则不加,直到x变成0。2.另一种效率比...原创 2018-03-12 22:02:45 · 1678 阅读 · 0 评论 -
计算机进行小数运算出错的原因
我们知道,有一些小数无法准确用二进制来表示,结果造成了运算的偏差。至于原因,这是因为计算器的精度有限,有一些十进制小数用二进制表示会出现无限循环,比如0.1(十进制)=0.00011001100....(以1100循环)(二进制),由于计算机精度有限,会截取后面的一部分,由此造成误差。 改善的方法(有时也能完美解决)有:将小数扩大为整数,先用整数运算,得到的结果再除以相应的倍率。...原创 2018-04-01 15:57:50 · 3618 阅读 · 0 评论 -
详细解析:如何确定域在结构中的字节偏移的 C语言的代码的实现
如何确定域在结构中的字节偏移?ANSI C 在 <stddef.h> 中定义了 offsetof() 宏, 用 offsetof(struct s, f) 可以计算出域 f 在结构 s 中的偏移量。如果出于某种原因, 你需要自己实现这个功能, 可以使用下边这样的代码: #define offsetof(type,f) ( (size_t) ( (cha...原创 2018-05-12 15:29:40 · 716 阅读 · 0 评论 -
C语言中,打印数据类型的取值范围l的两种方法
C语言中,有很多种数据类型,比如int, char, unsigned char等等,因为不同的机器,其对应的范围可能不同,当然最简单的办法就是查对应机器的资料,当然下面提供另外两种不同的方法。number one:其实,C语言数据范围已经在头文件<limits.h>中定义了,打开这个文件,我们看一下其中的冰山一角: 图中,我们可以看出该头文件已经定义好了数据类型的范围...原创 2017-06-27 16:47:55 · 3060 阅读 · 0 评论 -
关于在Windows下用键盘输入EOF的一些疑问
作为C语言的初学者,《C程序设计语言》这本书想必是入门提高的神作,居家学习旅行必备。.在本书的1.5.3节有这样一个程序: #include <stdio.h>/* 统计输入的行数 */main(){ int c, nl; nl = 0; while ((c = getchar()) != EOF) if (c == '\n'...原创 2017-06-24 23:27:32 · 1994 阅读 · 0 评论 -
关于二进制的负数怎么表示与移位运算
对于正数的二进制,应该没有疑问,比如(以8位系统来分析,第一位为符号位)1的二进制表示为00000001。 而对于负数来说,就比较麻烦,比如-1。有人说是10000001,而有人说是11111111。其实这两种说法都没错,这是分类方法的差异。在一些把二进制分为原码、反码、补码的体系中(注:当然对于正数,三码为1),10000001是-1的原码,而11111111是1的...原创 2018-04-01 15:24:53 · 7788 阅读 · 5 评论 -
词法分析中的 贪心算法
我们经常遇到一些令人烦心的C语句,比如a---b;我们应该理解为(a--)-b呢?还是a-(--b)呢?下面介绍一个简单的方法,就是传说中的贪心算法(人有贪欲,编译器又何尝不是呢)。对于上一个问题,解决方案可以归纳为一个很简单的规则:每一个符号应该包含尽可能多的字符。也就是说编译器将程序分解成符号的方法是,从左到右一个一个字符读入,如果该字符可能组成一个符号,那么再读入下一个字符,然后...原创 2017-09-25 23:28:29 · 321 阅读 · 0 评论 -
关于低位数二进制数转换为高位二进制数的一些心得体会
最近被这样一道题弄哭,查了好多资料,才勉强弄懂,废话不多说,请看题。#include "stdio.h"void main(){ int8 a = -1; uint16 b; printf("%d",b);} A.0 B.1 C.255 D.65535 A.0 B.1 C.255 D.65535 要弄清楚这个问题,我们首先要明确的是...原创 2017-05-23 21:40:56 · 1948 阅读 · 0 评论 -
自己犯的C语言错误(为面试而生)
题目来源于各大网站和一些书籍,由于来源太多,这里就不一一列举了,毕竟目的只是用于记录学习。(不断更新中)1.int a,b; a=b=1; b=a++,b++,++a; 求b的结果是多少? 答案:1. 这道题主要考查C语言逗号的作用,一开始我选择了b=3;我以为是这样的b=(a++,b++,++a);然而事实确是这样的(b=a++),b++,++a;(逗号的优先级比赋...原创 2017-08-10 21:41:55 · 313 阅读 · 0 评论 -
什么是制表符终止位?
《C程序设计语言》这本书中的练习1-20中,提到了制表符终止位这个名词,初看真是一脸懵逼,各种百度之后,将自己的理解描述一下,希望和我有一样困惑的人从中有收获。 起初,我以为制表符就是代表其占据一定的空格数(不同系统不同软件都可能不一样),但是真正作用是将每行按整数倍(需要系统或软件定义)的间隔进行分布。举个例子比较好:比如“制表符代表八个空格”,不是说代替八个空格。而其...原创 2018-03-08 15:41:26 · 2415 阅读 · 7 评论 -
把常量字符串赋予不同数组和不同指针之间的区别
有下面这样一个C语言函数:int text(void){char str1[]="good good study";char str2[]="good good study"; char* str3="good good study";char* str4="good good study"; if(str1==str2)printf("same");...原创 2017-09-10 23:48:28 · 301 阅读 · 0 评论 -
关于c语言中#ifndef预处理后接什么的疑惑
学单片机的小伙伴,都会看到.h(头文件)下面有这一样一个固定格式:其中#ifndef、#define、#endif等宏定义的作用是是为了防止重复声明,避免重复编译,提高编译器效率。其实很多人疑问的是这里的LED_H这个位置应该填什么,是不是有固定要求?其实LED_H这个位置填什么无所谓,只是定义一个宏而已,也就是说编译器只根据这个宏是否被定义来决定是否编译,你换别的名字也是可以...原创 2017-09-01 11:08:11 · 948 阅读 · 0 评论 -
C之代码注释
C语言中,因为某些原因(比如调试),我们经常需要把一段代码注释掉,许多初学者一般使用/* */来注释你想暂时不需要,可能以后需要的代码,即/* 你想注释掉的代码*/这种做法存在一些可怕的风险,因为在C语言中,注释不允许嵌套,第一个/* 会与第一个*/结合,也就是说如果你注释的代码里面本身就存在用/* */注释,那么你注释这段代码时将会出错,举个例子:/* int i; /原创 2017-10-25 15:49:04 · 1537 阅读 · 3 评论