[交流][微知识]整整整形(Integer)
本帖最后由 Gorgon_Meducer 于 2014-4-22 17:07 编辑说在前面的话
也许真的很久没有出来冒泡泡了,说白了就是懒。一方面,我总觉得如果要写点什么,就必须对得起围观,引得起占楼;
另一方面,其实还是懒。说起来,被人追捧的感觉就像晚饭后又喝了两杯,微醉又不至于飘飘然,很是舒服和满足;但实际
上我还是清楚的,所谓傻孩子一出必是精品实际上是一种苦涩——其实我之前还是蛮喜欢有点什么技术,也不管成熟不成熟,
都拿出来分享,结果讨论的还是太少,发帖也就求个人气,看来对不上大家胃口的东西真心不讨好——但真正技术的东西,
又有多少是可乐口味的呢?于是,傻孩子功利起来,要发就发热点,要发就发大家都喜欢看的,后来,就是大家看到的了
——很久不露面,发一个就是所谓的精品。
真心觉得现在认真研究技术的,至少混论坛的少了。每次都要憋个大家伙放出来,实在是懒了……
最近流行微博,我突然想到,也许不必每次一发都是什么系统的东西,也许小的知识点也可以弄个微知识,虽然不一定限
制什么140字写作,但是从小了说起,点滴说起还是很不错的,也不会有多少压力——毕竟一口气都是可以写完的。
—— 傻孩子 2012-10-24
[微知识]整整整型(Integer)
1. 标准整型
整形变量大家都了解,从学习C语言开始,随手一句int a,b,c;相当潇洒。大道理不说,在C语言中常见的整形变量有以下几种:
char 字符型 占用1个字节
short int 短整数 占用2个字节
int 整数 取决于编译器和CPU的ALU宽度
long 长整数 取决于编译器和CPU的ALU宽度
long long ...
列举上面的表格,聪明的你一眼就看出来了,使用C语言标准整型是存在不确定性的——究竟占用几个字节是编译器和CPU
说了算的。例如下面的代码,在不同的环境下结果就可能是不同的:
unsigned int hwDemo = 0x1234;
unsigned int hwExample = hwDemo << 16 >> 16;
这个代码看起来的确有点二,但如果我们将这个运算理解为某些复杂运算的某种边界条件下(比如移位都是由变量控制的)的
等效情形时,就不那么二了。很容易看出,如果编译器认定int是4个字节,那么hwExample的结果是0x1234;如果编译器认定int
是2个字节,那么hwExample就是0。
这些都是显而易见的内容。如果你经常在不同的项目和平台中穿梭,那么一定对这个问题很有认识,也容易对解决这个问题的
方法达成共识:不用C标准的数据类型,使用自定义的类型。在这个大背景下,各种各样的自定义变量类型集合如雨后春笋般涌现
出来——可以说几乎每一个人都有自成体系的冲动——我自己就用过不少,例如什么BYTE, HWORD,WORD,DWORD;什么UINT8,
UINT16,UINT32,UINT64之类,等等。结果,每次移植代码都是灾难的开始。为了解决这种各自为政的现象,C语言国际标准组织
在ANSI-C里面增加了一个标准整型集合,也就是大家熟悉的stdint.h。这个集合提供了编译器无关的整形类型,如果你的编译器不幸
因为各种原因无法使用#include<stdint.h>来获得这种支持,你也可以自己完成一些常用的定义来保持代码的兼容性:
//! 以下定义以ARM平台为背景
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsignd short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
...
似乎皆大欢喜,然而,故事才刚刚开始...
2、拥有自然原子性的整型(ALU-Dependent-Atom-Integer)
所谓拥有自然原子性的整型是那些字符宽度和处理器流水线内数字运算单元(ALU)宽度一致的整形。简单来说,8位机就是uint8_t,
16位机就是uint16_t,32位机就是uint32_t。拥有自然原子性的整型,顾名思义,对其进行的普通运算操作,在正常运算法则下是具有
原子性的,在要求不严格的场合下是可以认为是“线程安全”的,说白了就是中断和其它线程是不会破坏其数据完整性的。
举例来说,在8-bit的AVR环境下存在如下的代码:
volatile int16_t g_iADCResult = 0;
ISR(ADC_vect)
{
//! ADC 采样完成中断
g_iADCResult = ADC - 512;
}
int main(void)
{
...
while(1){
...
if (g_iADCResult > 128) {
//! 高于一个门限了,
...
}
}
return 0;
}有过开发经验的人一眼就能看出这个代码的问题所在——是的,在8位环境下CPU对16位的访问要分两次操作,如果不对这一过程做原子
保护,就有可能出现数据完整性的问题——表现在这个代码里面就是误判的存在。
拥有自然原子性的变量,就是因为ALU宽度与变量宽度一致而天然享有原子性。那么这有什么用呢?这不得不提到boolean型变量了。布尔型
变量从诞生之日起就肩负着一个重大的职责:充当标志。大家从来没有怀疑过布尔型变量的数据完整性问题,或者说大家本能的人为boolean型变
量就应该是具有原子性的。为什么提出这个问题呢?因为很多人因为节省空间的原因,往往对boolean型变量进行各种各样的改造,比如用位域啦,
比如固定用uint8_t啦。这就有可能导致在更高位数的平台下,比如32位,boolean的数据完整性得不到保证,同时访问效率也无法保证。
说了这么多,结论也很明显了,对于C语言来说,应该用具有自然原子性的整型来定义boolean型变量。以ARM为例子,boolean的
定义如下:
//! \brief 推荐用下面的方法来获得boolean的支持,但如果条件不允许,也可以自己定义
//#include<stdbool.h>
//! 以ARM平台为例
typedef unsigned int bool;
#ifndef faslse
# define false (0)
#endif
#ifdef true
# undef true
#endif
#define true (!false)
拥有自然原子性的整型还有一个特性就是访问效率高。比如,用来做循环变量啊,参数值传递阿之类的。如此之好,看来可以自己定义一个这样
的整型了,例如:
//! 以ARM平台为例
typedef unsigned int essf_atom_uint_t;
typedef signed int essf_atom_int_t;
问题随之而来,如果单纯这样定义,那么这个essf_atom_uint_t和essf_atom_int_t就和原本的int以及unsigned int没有区别了——字节的长度也是
没有区别的。换一个思路,我们需要的是可能的访问效率,所谓可能就是“有自然最好,没有的情况下应该保证功能正常”。从这个角度来说,我们
可以定义一系列“具有可优化潜质的整型”,比如:
//! 以ARM环境为例
typedef essf_atom_int_t esssf_int8_t;
typedef essf_atom_uint_t essf_uint8_t;
typedef essf_atom_int_t essf_int16_t;
typedef essf_atom_uint_t essf_uint16_t;
typedef essf_atom_int_t essf_int32_t;
typedef essf_atom_uint_t essf_uint32_t;
作为对比,下面提供一个AVR平台的定义
typedef essf_atom_int_t essf_int8_t;
typedef essf_atom_uint_t essf_uint8_t;
typedef int16_t essf_int16_t;
typedef uint16_t essf_uint16_t;
typedef int32_t essf_int32_t;
typedef uint32_t essf_uint32_t;聪明的你一看就懂了吧?需要注意的是,这种essf_uintx_t和essf_intx_t的变量由于实际长度是不保证的,因此因该避免将这类变量应用在需要移位
操作或者指针操作的场合。
正如大家所见到的,我又开始定义自己的变量类型了,这是走向非标准化的深渊。其实,为了同样的目的,IEEE在C99标准中引入了新的
变量类型,例如
uint_fast8_t
uint_fast16_t
uint_fast32_t
...
这就和我前面的essf_intxx_t一个效果。既然C99在stdint.h提供了这类好东西,我们自然要归顺啦。从此我写的代码就只使用stdint了
全文完
板凳。。。 我是板凳,LS的,{:titter:} 确实 发现大家都很少看论坛了 。至少 技术贴 变少了。 坐等继续{:lol:} 楼主继续,搬板凳听课,谢谢讲课 楼主威武 王工, 支持了, {:lol:} 前排占楼 学习 楼主真的好久没见冒泡了支持 搬个凳子学习。对于32位平台来说,若使用8位数据操作,是否还拥有原子性呢? AIHHLI 发表于 2012-10-24 12:40 static/image/common/back.gif
搬个凳子学习。对于32位平台来说,若使用8位数据操作,是否还拥有原子性呢? ...
取决于编译器,通常可以安全的假设——不具有。 前来听课,支持楼主。哈哈~~ 对于 cortex 来说 内核同时支持16位和32位 意味着都具有原子性? Flyback 发表于 2012-10-24 21:26 static/image/common/back.gif
对于 cortex 来说 内核同时支持16位和32位 意味着都具有原子性?
你说的是指令宽度吧?ALU是数据宽度。 Gorgon_Meducer 发表于 2012-10-25 09:55 static/image/common/back.gif
你说的是指令宽度吧?ALU是数据宽度。
我以为cortex的指令集既有16位数据操作指令又有32位操作指令,那ALU可能同时支持两种数据宽度
上面提到: 32位平台,uint8_t表示的boolean的数据完整性得不到保证没想通,望详解
本帖最后由 Gorgon_Meducer 于 2012-10-25 11:52 编辑
Flyback 发表于 2012-10-25 10:23 static/image/common/back.gif
我以为cortex的指令集既有16位数据操作指令又有32位操作指令,那ALU可能同时支持两种数据宽度
上面提到 ...
指令集的宽度和ALU宽度没有必然联系。
在32位环境下,因为效率的原因,数据的寻址都是以4字节进行对齐的,你用8bit,如果8bit的变量恰好落在4字节的倍数上,
并且后续的3个字节都没有分配内容,则有可能编译器会将这个uint8_t变量在后台升格为uint32_t来使用,当然这对你来说
是透明的,因为代码逻辑并没有变成uint32_t。如果不幸uint8_t落在了非整倍数的位置,或者后续3个字节有别的变量占用,
则对系统来说,它会首先取该变量所在的地址对齐到WORD以后连续的4个字节,然后再将内容提取出来。这个过程中,uint8_t
的原子性是无法保证的,当然,我没有说是绝对无法保证。但这种问题,只要有情况,并且风险较大,就应该避免这样使用,
一切都是为了兼容性——兼容性考量中,风险意味着在未来某个时候你不注意的情况下给你带来巨大的损失。另外,可以肯定
的是如果你和我较真这个原子性问题——觉得我说的32位平台下uint8_t无法保证原子性的说法太绝对,那是我太绝对了。
我能肯定的是在32位环境下用uint8_t效率肯定是有很大机会受到限制的,这也是为什么在32位平台下应该用uint32_t定义
boolean的原因之一。 谢谢解惑,之前没考虑过数据对齐
上面说的两点是我现在遇到的实际问题
我在systick中断中的程序,用来产生任务的时间片,都是使用的 u16,在相应的任务中会清零下面三个计数器
void IT_SysTickHook(void)
{
SYSTICK_RUN++;
SYSTICK_ADC++;
SYSTICK_MODBUS++;
}
看了上面的解释,uint8_t看来是不能保证数据安全,现在用的stm32f103,如何确定16位数据是否能保证安全呢
编程能力有限,我在中断程序中访问了不少任务变量,都是u16,全部使用32位的话,ram开销很大 Flyback 发表于 2012-10-25 12:05 static/image/common/back.gif
谢谢解惑,之前没考虑过数据对齐
上面说的两点是我现在遇到的实际问题
建立临界区啊。如果你跑操作系统,就用操作系统的资源来建立临界区。如果是裸奔,就屏蔽目标等级的中断就可以了——偷懒的话,屏蔽全局中断就可以了。 总感觉,关心这个,意义不大!
1.使用变量, 本来就是 按变量的取值范围去选择变量类型; 就算是8位的是原子整形, 超出范围还是得用16位的!
2.对于32位机,本来指令中就带8位和16位操作,反正存到内部存储器的时候,还是占用32位空间,对速度本来就没多少影响;
3.就算是有影响,对于32位这样速度的单片机来说, 这样的丁点影响有必要关注吗??? 关注这个不就是转牛角尖吗?
4.关注这个是为了榨干CPU的资源, 我觉得这种想法在设计中就是一致极端的想法, 每个设计都应该留有足够的余量, 如CPU只使用80%的性能, flash只使用80%的空间, 这本来就是系统安全性和设备程序升级换代的需要!
按这样, 关注这个就更加多余了!
所以, 对于整形的类型应该关系的是以下两点:
1. 数据对齐问题
2.程序中应该强类型, 但只考虑 符号和位数, 本来C语言设计这个整形的东西就是一个陷阱:
最好的建议是 按不同的器件, 定义出 u8, u16, u32, s8, s16, s32等类型, 屏蔽像 int, long这种不确定的整形;
还有就是 char,short,int, long这些类型, 一直是存在二次思维的问题, 也就是, 你看到short,你的想想是否有符号, 说多少位数, 才能确定这样的类型是否合适使用! Gorgon_Meducer 发表于 2012-10-25 12:51 static/image/common/back.gif
建立临界区啊。如果你跑操作系统,就用操作系统的资源来建立临界区。如果是裸奔,就屏蔽目标等级的中断就 ...
thanks ,这就改代码 楼主帅气! onepower 发表于 2012-10-25 14:05 static/image/common/back.gif
总感觉,关心这个,意义不大!
1.使用变量, 本来就是 按变量的取值范围去选择变量类型; 就算是8位的是原子整形 ...
很遗憾,为了活命,我苦逼得长期在被迫要求榨干处理器的每一点资源……
在蚊子腿上挂肉的事情是我过去三年中干的最多的事情——当然是被迫的…… Gorgon_Meducer 发表于 2012-10-25 15:03 static/image/common/back.gif
很遗憾,为了活命,我苦逼得长期在被迫要求榨干处理器的每一点资源……
在蚊子腿上挂肉的事情是我过去三 ...
蚊子腿上挂肉, 好比喻, 之前我也做多了, 现在坚决不这样做, 这样反看开了, 心情也轻松了! 我们是自己再压迫自己啊! onepower 发表于 2012-10-25 15:28 static/image/common/back.gif
蚊子腿上挂肉, 好比喻, 之前我也做多了, 现在坚决不这样做, 这样反看开了, 心情也轻松了! 我们是自己再压 ...
貌似你是有的选……羡慕…… 顶!!!! Gorgon_Meducer
保证原子性 是非常重要的!!!
不理解的这重要性的人,只是还没有达到 傻孩子这样高度而已! 本帖最后由 ele_eye 于 2012-10-25 16:34 编辑
一般在需要方便移植都会定义一个 def_Int 表示默认的数据宽度,
例如:
typedef uint8_t def_int //------8位机
typedef uint16_t def_int //------16位机
typedef uint32_t def_int //------32位机
在没有必须的场合都使用 def_int来定义变量,这样只需要更改 def_int定义 就可以方便移植
ele_eye 发表于 2012-10-25 16:21 static/image/common/back.gif
一般在需要方便移植都会定义一个 def_Int 表示默认的数据宽度,
例如:
typedef uint8_t def_int ...
uint8_t uint16_t 这些已经能保证移植性了……
楼下有文化
本帖最后由 Sullivan 于 2012-10-26 17:41 编辑先插后看 楼上是人才
先顶后看 好东西留下看看
Sullivan 发表于 2012-10-25 19:08 static/image/common/back.gif
先插后看
找别地插去! 支持 ,不错 好贴呀,顶了 强悍。
不过最后的没看懂啊。不知道理解对不对。
我认为是:
在32位机上,8,16,32位的都可以是bool型,按楼主说的好像是优化作用?
而8位机上的,只能用8位的bool RUANJI 发表于 2012-10-26 10:55 static/image/common/back.gif
强悍。
不过最后的没看懂啊。不知道理解对不对。
如果你不在乎效率和所谓的自然原子性,任何整型都可以定义为bool。
就是从效率和自然原子性角度考虑,32位系统推荐用uint32_t作为bool的基础类型。 我单片机程序(8位MCU为主)的都是用bit来做!我觉得单片机程序都应该是有位操作的指令!
union {
struct {
UINT8b0:1;
UINT8b1:1;
UINT8b2:1;
UINT8b3:1;
UINT8b4:1;
UINT8b5:1;
UINT8b6:1;
UINT8b7:1;
}oneBit;
UINT8 allBits;
}; 从来都是看帖子不回帖的我,今天忏悔来了,因为没有,积分,发帖子都不让用链接呀。 暂时MARK一个16/32位平台program pack(1)在这里。 习惯于编译器对 unsigned char ,unsigned int, char, int... 等的高亮显示,程序看起来更规范。 聪明的你一看就懂了吧?需要注意的是,这种vsf_uintx_t和vsf_intx_t
-------------
老师 对不起 我太笨了 没看懂
vsf_uintx_t和intx-t再定义到标准的char int之类么 我会告诉你28335的编译器里面char型是占用2个字节吗? kihell 发表于 2013-1-20 20:32 static/image/common/back.gif
聪明的你一看就懂了吧?需要注意的是,这种vsf_uintx_t和vsf_intx_t
-------------
老师 对不起 我太笨了...
不用了吧? i55x 发表于 2013-1-20 21:38 static/image/common/back.gif
我会告诉你28335的编译器里面char型是占用2个字节吗?
所以uint8_t这种定义就更重要了。 Gorgon_Meducer 发表于 2013-1-20 22:44 static/image/common/back.gif
所以uint8_t这种定义就更重要了。
http://www.ti.com/lit/ug/spru514e/spru514e.pdf
TId的C2000编译器里面就没有uint8_t这个类型 schwarz 发表于 2013-1-21 15:42 static/image/common/back.gif
http://www.ti.com/lit/ug/spru514e/spru514e.pdf
TId的C2000编译器里面就没有uint8_t这个类型 ...
那咋办?(哇库~哇库~) 整整整形……学习了。 自然原子性 平时不太留意 拜读了
页:
[1]