算术运算符
+ - * / %
除了%操作符,其余几个操作符都是既适用于浮点类型有适用于整数类型。
当/操作符的两个操作数都是整数时,它执行整除运算,在其他情况下则执行浮点数除法,整除运算的任意操作符为负值,运算结果又编译器定义。整数类型进行 "/" 运算商也是整数,截断小数部分
%为取模操作符,它接受两个整型操作数,把左操作数除以右操作数,它的返回值是余数而不是商
应尽量避免让负数参与/ 和 %运算,结果肯能是不确定的
#include <stdio.h> void div(unsigned int i, unsigned int j) { if (i == ((i/j)*j)) { printf ("%u is divisible by %u\n", i, j); } } void div_mod(unsigned int i, unsigned j) { if (i == (((i/j)*j) + i%j)) { printf ("%u is divisible by %u\n", i, j); } } int main() { div(100, 10); div(100, 11); div_mod(100, 10); div_mod(100, 11); }运行结果:
100 is divisible by 10
100 is divisible by 10
100 is divisible by 11
逻辑操作符
&& || !
使用&&或||连接的表达式从左到右求值,已知结果则立即停止。
注意!与~操作符的区别:!是逻辑取反,~是按位取反
#include <stdio.h> int main() { int a; int i; for (a=0, i=0; a<=1&&!i++; a++) { a++; } printf("a = %d x = %d\n", a, i); }运行结果:
a = 2 x = 1
位操作符
& | ^ ~ << >>
&可用于屏蔽(mask off)位,|可用于设置(turn on)位
value = value & ~(1<<bit_number)或者 value &= ~(1<<bit_number)
value = value | 1<<bit_number或者 value |= 1<<bit_number
~操作符可用于定义机器字长全F
<< 左移n位,等效于 *2^n(不溢出情况下)
>> 右移n位,等效于/ 2^n
左移右移两个操作数必须都是整型类型
右移位操作存在一个左移位操作不曾面临的问题:从左边移入新位时,可以选择两种方案:
一种是逻辑移位,左边移入的位用0填充;
另一种是算术移位,左边移入的位有原先该值的符号位决定,符号位为1则移入的位均为1,符号位为0则移入的位均为0,这样能够保证原数的正负形式不变。
算术左移和逻辑左移是相同的,它们只在右移时不同,而且只有当操作数是负值时才不一样。
注:标准说明无符号类型值的执行的所有移位操作都是逻辑移位,但对于有符号值,到底是采用逻辑移位还是算术移位取决于编译器。因此,一个程序如果使用了有符号数的右移位操作,它就是不可移植的。
&在特定情况下可以代替%进行取余运算
除数为2^n时取余可用&代替,且效率更高
以下为判断整除的例子
除数任意
BOOL_T IsDivisible_A(IN UINT uiX, IN UINT uiY) { BOOL_T bDiv = BOOL_FALSE; if (0 == (uiX%uiY)) { bDiv = BOOL_TRUE; } return bDiv; } BOOL_T IsDivisible_B(IN UINT uiX, IN UINT uiY) { BOOL_T bDiv = BOOL_FALSE; if (uiX == ((uiX/uiY)*uiY)) { bDiv = BOOL_TRUE; } return bDiv; }除数为2^n
BOOL_T IsDivisible_8_A(IN UINT uiX) { BOOL_T bDiv = BOOL_FALSE; if (0 == (uiX&0x7U)) { bDiv = BOOL_TRUE; } return bDiv; } BOOL_T IsDivisible_8_B(IN UINT uiX) { BOOL_T bDiv = BOOL_FALSE; if (uiX == ((uiX>> 3UL)<<3UL)) { bDiv = BOOL_TRUE; } return bDiv; }对数据进行截取、移位、组合,就可以进行字节序转换
#include <stdio.h> #define Conversion_4Byte(x ,y) y = ((x & 0xFF000000U) >> 24)| ((x & 0x00FF0000U) >> 8) | ((x & 0xFF00U) << 8) | ((x & 0xFFU) << 24) int main() { unsigned int x = 0xAABBCCDD; unsigned int y = 0; Conversion_4Byte(x, y); printf("%#X \n%#X\n", x, y); }运行结果:
0XAABBCCDD
0XDDCCBBAA
位运算符常见用法
#define BIT_SET(f, b) ((f) |= (b)) #define BIT_RESET(f, b) ((f) &= ~(b)) #define BIT_TEST(f, b) (((f)&(b)) != 0) #define BIT_MATCH(f, b) (((f)&(b)) == (b)) typedef enum tagEvent { EVENT_X = 0, EVENT_Y, EVENT_Z } #define EVENT_MASK(enEvt) ((UINT)(1UL << (ULONG)(UINT)(enEvt))) #define EVENT_ON(uiCur, enEvt) (BIT_SET(uiCur, EVENT_MASK(enEvt))) #define EVENT_OFF(uiCur, enEvt) (BIT_RESET(uiCur, EVENT_MASK(enEvt))) #define EVENT_TEST(uiCur, enEvt) (BIT_TEST(uiCur, EVENT_MASK(enEvt)))
#define BIT_SET(f, b) ((f) |= (b)) #define BIT_RESET(f, b) ((f) &= ~(b)) #define BIT_TEST(f, b) (((f)&(b)) != 0) #define BIT_MATCH(f, b) (((f)&(b)) == (b)) #define STATUS_X 0x01U #define STATUS_Y 0x02U #define STATUS_Z 0x04U #define STATUS_ALL (STATUS_X | STATUS_Y | STATUS_Z) #define STATUS_ISVALID(uiStatus) (BIT_TEST(uiStatus, STATUS_ALL) && !BIT_TEST(uiStatus, ~STATUS_ALL))
转换
类型 后缀
int ??
unsigned int u/U
long l/L
unsigned long ul/UL
long long ll
unsigned long long ull/ULL
为常量写适当的后缀,可增加代码可读性,也能预防由于转换引起的微妙问题
#include <stdio.h> int main() { printf("the default size %d\n", sizeof(0)); printf("the postfix U size %d\n", sizeof(0U)); printf("the postfix L size %d\n", sizeof(0L)); printf("the postfix UL size %d\n", sizeof(0UL)); printf("the postfix LL size %d\n", sizeof(0LL)); printf("the postfix ULL size %d\n", sizeof(0ULL)); }32bitCPU运行结果:
the default size 4
the postfix U size 4
the postfix L size 4
the postfix UL size 4
the postfix LL size 8
the postfix ULL size 8
#include <stdio.h> #define USHORT unsigned int #define UINT unsigned int #define ULONG unsigned long #define UINT64 unsigned long long int main() { USHORT usA = (USHORT)-1; UINT uiB = (UINT)-1; ULONG ulC = (ULONG)-1; UINT64 uiD = (UINT64)-1; printf("%hx\n%x\n%lx\n%llx\n", usA, uiB, ulC, uiD); }
运行结果:
ffff
ffffffff
ffffffff
ffffffffffffffff
ULONG g_ulA = (ULONG)-1L
ULONG g_ulA = ~0UL
整型提升(intergral promotion)
某些运算符会对操作数进行整型提升
#include <stdio.h> int main() { unsigned short usData = 0; if (~usData > (unsigned short)0) { printf ("TRUE\n"); } else { printf("FALSE\n"); } }运行结果:
FALSE
"~"会对操作数做“整型提升”,USHORT 提升为INT变为有符号类型,取反后为四字节全F(-1<0)
不要让UCHAR , USHORT,直接进行“~”, << >>也有类似问题
算术类型转换
当运算符的操作数类型不一致时,运算前会将操作数转换为同一类型
原则:
1、长度不同,往更大长度的类型转换
2、长度相同,往无符号类型转换
long double > double > float > unsigned long > long > unsigned int > int
扩展
从一个较小的数据类型转换到一个较大的类型
要将一个无符号数转换为一个更大的数据类型,只要简单地在表示的开头添加0。这种运算被称为零扩展(zero extension)
要将一个二进制补码数字转换为一个更大的数据类型,规则是执行一个符号扩展(sign extension),在表示中添加最高有效位的值。
#include <stdio.h> int main() { int val = -12345; short sx = val; unsigned short usx = sx; int x = sx; unsigned int ux = sx; printf("%10d %#10hx\n", sx, sx); printf("%10u %#10hx\n", usx, usx); printf("%10d %#10x\n", x, x); printf("%10u %#10x\n", usx, usx); }运行结果:
-12345 0xcfc7
53191 0xcfc7
-12345 0xffffcfc7
53191 0xcfc7