同样的,本篇之所以会讲解这个头文件是因为我们的BIF(2.1.1)会使用到这个头文件。
至于2.1.1什么时候更新,这个因为最近电脑的故障问题,又要送去维修,所以这个如果现在能发布也算比较有幸了。
就很多人会说封装函数库很困难,实际上是因为大家没有比较深入的去学习C语言,而且封装函数库只是集成标准库,并没有涉及底层逻辑和具体应用之类的。
头文件介绍
<stdint.h>全程是Standard Integer Types Header ,即“标准整数类型头文件”
主要作用:提供一系列标准化的整数类型定义,这些类型具有明确的大小和符号特性,从而确保代码在不同的平台和编译器之间都有一定的可移植性。
准确地反映了它的功能和用途。
Standard
表示这些类型是标准化的,符合C语言标准(如我们上次说的C99)
Integer Types
表示这些类型是整数类型,包括有符号整数和无符号整数
Header
表示这是一个头文件,用于在C语言程序中包含这些类型定义。
这时候你可能要问了,既然这个头文件支持C99,那我们那个代码为什么不用这个头文件呢。
这个是只能应用在支持C99标准的头文件上,我后面去看了一下调查了一下我们的Keil,它对C99的支持存在一定的限制,但是我们可以通过配置来部分启用C99特性。具体的操作方法我放在后面了,以确保我们的Keil5能支持这个头文件。
具体内容介绍
<stdint.h>的实质很简单,他就是利用宏定义使得这个定义可以应用于不同的开发平台上。
接下来我会把里面所有的整数类型给大家介绍一下,如果想要快速了解的就直接通过目录来粗看一眼就可以了。
1.精确宽度整数类型
这个类型具有明确的位宽,确保在任何支持C99的平台上都有相同的大小。
位宽(Bit Width):指一个数据类型或数据存储单元所占用的二进制位的数量。不同的整数类型都有不同的位宽。这个在我们后续写这个具体的整数类型的时候我会附加在上面。
有符号整数类型
-
int8_t
:8 位有符号整数 -128 ~ 127 -
int16_t
:16 位有符号整数 -32768 ~ 32767 -
int32_t
:32 位有符号整数 -2147483648 ~ 2147483647 -
int64_t
:64 位有符号整数 -9223372036854775808 ~ 9223372036854775807
无符号整数类型
-
uint8_t
:8 位无符号整数 0 ~ 255 -
uint16_t
:16 位无符号整数 0 ~ 65535 -
uint32_t
:32 位无符号整数 0 ~ 4294967295 -
uint64_t
:64 位无符号整数 0 ~ 18446744073709551615
2.最小宽度整数类型
这些类型至少具有指定的宽度,但可能更宽,具体取决于平台。
它与精确宽度整数类型的区别在下面讲述。
有符号整数类型
-
int_least8_t
:至少 8 位的有符号整数 -128 ~ 127 -
int_least16_t
:至少 16 位的有符号整数 -32768 ~ 32767 -
int_least32_t
:至少 32 位的有符号整数 -2147483648 ~ 2147483647 -
int_least64_t
:至少 64 位的有符号整数 -9223372036854775808 ~ 9223372036854775807
无符号整数类型
-
uint_least8_t
:至少 8 位的无符号整数 0 ~ 255 -
uint_least16_t
:至少 16 位的无符号整数 0 ~ 65535 -
uint_least32_t
:至少 32 位的无符号整数 0 ~ 4294967295 -
uint_least64_t
:至少 64 位的无符号整数 0 ~ 18446744073709551615
精确与最小宽度整数类型的区别
精确宽度:提供具有 固定位宽 的整数类型,确保在所有支持C99的平台这些类型的大小完全一致。这主要适用于 精确控制数据大小 的场景。如我们在硬件编程,网络协议等,数据大小必须严格符合规范。
最小宽度:提供至少具有指定最小位宽的整数类型,但实际大小可能大于指定的最小位宽,这些因平台而异。适用于需要保证数据类型至少有某种最小宽度,但对具体大小没有严格要求的场景。如通用编程,性能优化等。
3.最大宽度整数类型
这些类型用于表示最大可能的有符号和无符号整数
intmax_t: 最大可能的有符号整数 -9223372036854775808 ~ 9223372036854775807
uintmax_t:最大可能的无符号整数 0 ~ 18446744073709551615
4.最快整数类型
在当前平台上执行速度最快,但可能比指定的最小位宽更宽
有符号整数类型
-
int_fast8_t
:最快的至少 8 位的有符号整数 -128 ~ 127 -
int_fast16_t
:最快的至少 16 位的有符号整数 -32768 ~ 32767 -
int_fast32_t
:最快的至少 32 位的有符号整数 -2147483648 ~ 2147483647 -
int_fast64_t
:最快的至少 64 位的有符号整数 -9223372036854775808 ~ 9223372036854775807
无符号整数类型
-
uint_fast8_t
:最快的至少 8 位的无符号整数 0 ~ 255 -
uint_fast16_t
:最快的至少 16 位的无符号整数 0 ~ 65535 -
uint_fast32_t
:最快的至少 32 位的无符号整数 0 ~ 4294967295 -
uint_fast64_t
:最快的至少 64 位的无符号整数 0 ~ 18446744073709551615
5.指针宽度整数类型
这些类型与指针的宽度相同,用于确保指针和整数之间的安全转换
有符号整数类型
-
intptr_t
:与指针宽度相同的有符号整数
无符号整数类型
-
uintptr_t
:与指针宽度相同的无符号整数
6.整数类型的极限值
(宏定义),用于表示各种整数类型的极限值,这些宏确保代码在不同平台有相同的边界值
有符号整数的最小值
-
INT8_MIN
:int8_t
的最小值 -128 -
INT16_MIN
:int16_t
的最小值 -32768 -
INT32_MIN
:int32_t
的最小值 -2147483648 -
INT64_MIN
:int64_t
的最小值 -9223372036854775808 -
INT_LEAST8_MIN
:int_least8_t
的最小值 -128 -
INT_LEAST16_MIN
:int_least16_t
的最小值 -32768 -
INT_LEAST32_MIN
:int_least32_t
的最小值 -2147483648 -
INT_LEAST64_MIN
:int_least64_t
的最小值 -9223372036854775808 -
INT_FAST8_MIN
:int_fast8_t
的最小值 -128 -
INT_FAST16_MIN
:int_fast16_t
的最小值 -32768 -
INT_FAST32_MIN
:int_fast32_t
的最小值 -2147483648 INT_FAST64_MIN
:int_fast64_t
的最小值 -9223372036854775808-
INTPTR_MIN
:intptr_t
的最小值 -2147483648 (32位)/ -9223372036854775808(64位) -
INTMAX_MIN
:intmax_t
的最小值 -9223372036854775808
有符号整数的最大值
-
INT8_MAX
:int8_t
的最大值 127 -
INT16_MAX
:int16_t
的最大值 32767 -
INT32_MAX
:int32_t
的最大值 2147483647 -
INT64_MAX
:int64_t
的最大值 9223372036854775807 -
INT_LEAST8_MAX
:int_least8_t
的最大值 127 -
INT_LEAST16_MAX
:int_least16_t
的最大值 32767 -
INT_LEAST32_MAX
:int_least32_t
的最大值 2147483647 -
INT_LEAST64_MAX
:int_least64_t
的最大值 9223372036854775807 -
INT_FAST8_MAX
:int_fast8_t
的最大值 127 -
INT_FAST16_MAX
:int_fast16_t
的最大值 32767 -
INT_FAST32_MAX
:int_fast32_t
的最大值 2147483647 -
INT_FAST64_MAX
:int_fast64_t
的最大值 9223372036854775807 -
INTPTR_MAX
:intptr_t
的最大值 2147483647(32位)/ 9223372036854775807(64位) -
INTMAX_MAX
:intmax_t
的最大值 9223372036854775807
无符号整数的最大值
-
UINT8_MAX
:uint8_t
的最大值 255 -
UINT16_MAX
:uint16_t
的最大值 65535 -
UINT32_MAX
:uint32_t
的最大值 4294967295 -
UINT64_MAX
:uint64_t
的最大值 18446744073709551615 -
UINT_LEAST8_MAX
:uint_least8_t
的最大值 255 -
UINT_LEAST16_MAX
:uint_least16_t
的最大值 65535 -
UINT_LEAST32_MAX
:uint_least32_t
的最大值 4294967295 -
UINT_LEAST64_MAX
:uint_least64_t
的最大值 18446744073709551615 -
UINT_FAST8_MAX
:uint_fast8_t
的最大值 255 -
UINT_FAST16_MAX
:uint_fast16_t
的最大值 65535 -
UINT_FAST32_MAX
:uint_fast32_t
的最大值 4294967295 -
UINT_FAST64_MAX
:uint_fast64_t
的最大值 18446744073709551615 -
UINTPTR_MAX
:uintptr_t
的最大值 32:4294967295 / 64:18446744073709551615 -
UINTMAX_MAX
:uintmax_t
的最大值 18446744073709551615
7.整数常量宏
用于表示各种整数类型的常量,确保在不同平台上具有相同的大小和符号
作用就是确保整数常量具有正确的类型(转换常量类型)实现的
有符号整数常量
-
INT8_C
:用于表示int8_t
的常量 -
INT16_C
:用于表示int16_t
的常量 -
INT32_C
:用于表示int32_t
的常量 -
INT64_C
:用于表示int64_t
的常量
无符号整数常量
-
UINT8_C
:用于表示uint8_t
的常量 -
UINT16_C
:用于表示uint16_t
的常量 -
UINT32_C
:用于表示uint32_t
的常量 -
UINT64_C
:用于表示uint64_t
的常量
最大宽度整数常量
-
INTMAX_C
:用于表示intmax_t
的常量 -
UINTMAX_C
:用于表示uintmax_t
的常量
8.其他定义
SIZE_MAX
size_t 的最大值。size_t 是一种无符号整数类型,通常用于表示对象的大小。
如我们在经常使用的size函数就是用来计算某一变量的长度的。
uint8_t char_number = size(arr);//arr是一个字符数组
SIZE_MAX 定义了 size_t 类型能够表示的最大值
PIPDIFF_MIN 和 PTRDIFF_MAX
分别表示 ptrdiff_t 的最小值和最大值。
ptrdiff_t 是一种有符号整数类型,用于表示两个指针之间的差值。这两个宏定义了 ptrdiff_t 类型的取值范围。
SIG_ATOMIC_MIN 和 SIG_ATOMIC_MAX
分别表示 sig_atomic_t
的最小值和最大值。sig_atomic_t
是一种整数类型,用于在信号处理函数中安全地进行操作。这两个宏定义了 sig_atomic_t
类型的取值范围。
大家上面可能不太清楚哪个叫那个啊
uint 无符号、int 有符号、max 最大值、min 最小值、pip 指针类型、fast 最快整数、least 最小宽度、max 最大宽度(_左侧)
再叙·前缘
现在的你已经了解了这个函数库的所有定义内容,我们再来重新认识一下这个<stdint.h>
<stdint.h>的实现是通过宏定义吗
是的,<stdint.h>中的许多内容都是通过宏定义来实现的,这些宏定义提供了一种标准化的方式来访问和使用这些值,确保代码的可移植性和一致性。
事实上,绝大多数的函数都是采用宏定义的方法来增加代码的可移植性。
那么你可能会想到,我们在keil编程中对单片机进行编程的时候,如引脚定义,如ADC采样,如配置时钟、高低电平等等,实际上都是通过宏定义、直接调用该文件的地址,对地址进行处理实现的。(如我们在上一篇文章说过的——寄存器地址映射)
#define GPIOA_BASE_ADDR 0x40020000
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE_ADDR)
我们发现这个地址文件是一个16进制的数字,我们通过上一章讲过的位操作实现间接控制。
同样的HAL库也是采用同样的策略
#define __HAL_RCC_GPIOA_CLK_ENABLE() (RCC->AHB1ENR |= (RCC_AHB1ENR_GPIOAEN))
void HAL_GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
也是通过我们手动配置结构体,进而通过内部寄存器存储的引脚配置信息进行“读取”和“识别”(结构体信息),然后进行操作(硬件寄存器)。
HAL库安全在它通过更高层次的封装和抽象,将复杂的底层细节隐藏起来,我们的BIF函数库也是如此。
这个时候聪明的朋友就会想到,我们的地址都是针对于我们当前类型文件的,如果更换了平台,使用场景不就使用不了了吗?
我们一起来看一下<stdint.h>处理头文件的内部实现细节。
#define INT8_MIN (-128)
#define INT8_MAX 127
#define INT16_MIN (-32768)
#define INT16_MAX 32767
#define INT32_MIN (-2147483648)
#define INT32_MAX 2147483647
#define INT64_MIN (-9223372036854775808LL)
#define INT64_MAX 9223372036854775807LL
#define UINT8_MAX 255
#define UINT16_MAX 65535
#define UINT32_MAX 4294967295U
#define UINT64_MAX 18446744073709551615ULL
这里为大家列出的是整数类型的极限值宏定义方法。
我们在前面已经铺垫过了,我们这里的宏定义实际上都是象征着:符号属性、位宽(取决于平台)以及取值范围。
这个头文件在我们进行ADC采样的时候尤为有用(maybe)
最近一个月真的很忙啊,还碰上了校赛,有种南村群童欺我老无力的无助感,有的时候真的很感慨。不过,马上就是五一啦,我们会在五一回来后更新一部分BIF函数库,最近真的没有这些精力了。
在临近结束之前,我们再来聊聊keil中的C99标准配置环节吧。
keil如何配置C99
我们的认知中,keil都是以C语言为标准进行编程的,而C语言的标准是国际通用的C99标准,那么为什么keil会出现不支持C99的情况呢?
实际上,keil的年龄十分大了,keil的一些编译器默认遵顼的是旧版C标准(如C89、C90),而不是C99.
那么这些标准有什么区别呢?C90标准要求变量必须在代码块的开头声明,C99允许在代码块的任意位置声明变量。(这个任意位置真的省去了我们很多的麻烦,在我还在ACM的时候深有体会)
keil C51 主要用于 8051系列的微控制器,基于旧版本C标准,所以不支持C99 ,而我们的keil MDK_ARM 用于ARM架构的编译器(如 armcc) 就支持C99,但需要正确配置。
但是keil只是基于微控制器的编译器,并非专业的C语言开发软件,仍有一些C99特性无法支持,如我们上文所出现的。
如果你的keil里没有C99Mode这个选项,则在Masic Controls 里面输入 “--C99”即可。
你可能会疑惑这个 " --gnu "是干什么的
--gnu 是一个编译器选项,用于启用对GNU C扩展的支持。GNU C是GNU编译器(GCC)中对标准C语言的扩展,它提供了一些额外的功能和特性,虽然不完全符合标准C ,但在某些情况下可以大大提高你的代码灵活性和效率。
这其中有计算机最爱的嵌套函数(汉诺塔问题等)、变量声明在代码块中间、复合字面量(直接构造结构体或者联合体)、指定初始器等等。
启用“--gnu"就会避免各种编译错误(有的时候这也是一种弊端,出问题很难排查)。当然,--gnu 启用后,你的代码将不再完全符合标准CC ,需要在其他标准C编译器中编译代码,最好禁用--gnu 或者调整你的代码。
我就知道你们会问如果我想同时输入C99 和 GNU 应该怎么写
--gnu --c99
后事有曰
最近颇有感悟,我发现这个比赛如果不是为了热爱,那便失去了意义。但是在现实生活中,我们都在追求极致的奖项,为升学做准备。我们难道就应该说是学习阻拦了我们发展爱好的脚步吗?当然不能,只能说,在生活和梦想的权衡之下,我们最终沉默在了生活之中。
我最终去了电源组,电源组拿好奖难,我可能是个M,就喜欢没苦硬吃,但是我还是很享受这个探索、挖掘、发现一个个不起眼的东西时候的兴奋、惊讶的感觉。
(哇塞,我们模电老师也是这么说的)
以前不懂装懂,过度的包装让自己看起来十分臃肿,现在坦然地接受自己,从0开始,重新筑基,反倒有了轻松、踏实的感觉。
所以,在学习每一章内容,我都会寻根问底,了解它的底层逻辑。
比起别人眼中那个亮丽的自己,其实那个寻求真理,不顾一切向前奔去的你更加动人美丽。
除了研究电路、代码,我还是一个社会行为分析大师pwp,没想到吧!
大家如果对这方面感兴趣,就去关注我的海尔默理论吧,能否搜到就看缘分了,哈哈哈。
最近要准备小说《门的界》,里面是海尔默理论最出彩的一个部分——时间论。
其中我假设了世界个另一个维持状态的原因——时间因子,感兴趣的可以去起点上一看。
那么今天就到这里了,祝大家学有所成,马到成功。
学研之路遥遥无期,但探求真理之路永远昂扬向上,充满光芒。