注:他们的操作数必须是整数。
先铺垫一些基础知识:
计算机中一个整数的二进制有3种表示形式:原码、反码、补码,由于是整型,因此大小为四个字节,可以用32个bit位来表示,其中,最左边一位是符号位,0表示正数,1表示负数
例如:
3的原码:00000000000000000000000000000011
-5的原码:10000000000000000000000000000101
正整数:原码反码补码三者相同
例如
3的反码:00000000000000000000000000000011
3的补码:00000000000000000000000000000011
负整数:需要通过计算相互求得:原码除符号位不变,其他各位取反得到反码,反码加1得到补码
例如:
-5的反码:11111111111111111111111111111010
-5的补码:11111111111111111111111111111011
内存中:二进制数是以补码的形式存在,因此在移位时也自然是针对补码进行,因此对负整数操作时一定要注意!!
<< 左移操作符移位规则:左边丢弃,右边补0
由上述代码可知:左移有乘2的效果,每左移一位,原结果就乘一次2
>> 右移操作符移位规则:
算数移位:右边丢弃,左边补原符号位(vs编译器中采用的就是算数移位)
逻辑移位:右边丢弃,左边补0(大部分编译器不是采用逻辑移位,因此这种情况少见)
警告⚠ : 对于移位运算符,不要移动负数位,这个是标准未定义的,也可说就是错的。 例如: int num = 10 ; num >>- 1 ; //error 位操作符注:他们的操作数必须是整数。
& //按位与 | //按位或 ^ //按位异或 对它们的补码进行按位操作 *按位异或的一个实际应用:不创新变量的前提下,交换两变量内容 先来看一下基础: 3的2进制表示为:011 011^011=0——>3^3=0 5的2进制表示为:101 101^000=101——>5^0=5 结论一;一个数和自己本身异或,结果为0,一个属与0异或,结果为本身。 3^3^5=5; 3^5^3=5; 结论二:异或运算满足交换律 由此可实现题目要求的功能,见如下代码:下面这一种方法虽然也可以实现,但是当a,b两个数都很大的时候,两数相加会出现栈溢出,因此上面的一种方法更好
- 练习一(左移和按位与的综合应用):编写代码实现:求一个整数存储在内存中的二进制(即该数的补码)中1的个数
方法:将1依次左移,直至移到第32位(移31次),每移动一次,和原数进行一次按位与运算,如果结果不为0(为真),则此位上为1,否则为0,再利用for循环计数即可,具体代码如下:
赋值操作符计算顺序是从右往左,把右边的值赋给左边
== 复合赋值符:+= -= *= /= %= >>= <<= &= |= ^=int x = 10;
x = x+10;
x += 10 ; //复合赋值,与 x = x + 10 ;等价。 其他运算符一样的道理。这样写更加简洁。 单目操作符 ! 逻辑反操作int main() //C语言中0表示假,非0表示真
{
int flag = 3;
//flag为真,进入if
if (flag)
{}
//flag为假,进入if
if(!flag)
{}
return 0;
}
- 负值
+ 正值
& 取地址
&a表示的是a的地址,如果要定义一个变量来存放地址时,应该定义为指针变量,指针变量里放的都是对应变量的地址
%d打印的是整数,地址要用%p打印
sizeof 操作数的类型长度(以字节为单位)计算类型所创建的变量占据空间的大小,也可直接计算变量所占空间的大小,单位是字节
也可以应用于数组用于计算数组中元素个数
应用于变量时可以省略括号(侧面说明它不是函数),但应用于类型时不能省略
sizeof()与数组
数组在函数传参时传的是首元素的地址,此时sizeof(arr)计算的是地址的长度,在*86环境下,地址长度是4,因此若要使用sizeof来实现计算元素个数,最好在主函数中计算出来,然后将其作为参数一并传参
~ 对一个数的二进制补码按位取反 -- 前置、后置-- ++ 前置、后置++前置:先++(--)后使用
后置;先使用,后++(--)
//++和--运算符 //前置++和-- #include* 间接访问操作符(解引用操作符)int main() { int a = 10; int x = ++a; //先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。 int y = --a; //先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10; return 0; } //后置++和-- #include int main() { int a = 10; int x = a++; //先对a先使用,再增加,这样x的值是10;之后a变成11; int y = a--; //先对a先使用,再自减,这样y的值是11;之后a变成10; return 0; }
*和&搭配在有指针的情况下使用
int main()
{
int a = 10;
int* p = &a;
*p = 10;//*p=a
printf("%dn", a);
return 0;
}
(类型) 强制类型转换
例如:
srand((unsigned int)time(NULL))中,time()函数的返回值类型是time_t,但srand()函数要求参数的类型是unsigned int,因此在前面加上(unsigned int),将time_t类型强制转换为unsigned int类型
关系操作符 > >= < <= != (用于测试“不相等” ) == ( 用于测试“相等”) 比较简单,但要注意以下陷阱: 1. 在编程的过程中 == 和 = 不小心写错,导致的错误 2.测试字符串内容是否相同时,不能用==,即"abc" == "abcdef"这种写法是错误的,这比的是两字符串首元素地址是否相等,应该用专门的函数strcmp()使用时记得引用头文件string.h 逻辑操作符 &&逻辑与两数相与,同时为真,结果才真,只要有一个为假,结果为假
|| 逻辑或两数相或,同时为假,结果才假,只要有一个为真,结果为真
C语言中,0为假,非0为真
注意区分逻辑与和按位与的特点!!!
练习一:360的一道面试题
结论:
1.对于&&来说,只要左边为0,便可以判断结果为0,就不再进行之后的运算
2.对于||来说,只要左边为非0,便可以判断结果为1,就不再进行之后的运算 条件操作符 exp1 ? exp2 : exp3其实是一种对if......else......语句的简化形式
//如何用条件操作符来表达下列语句? if (a > 5) b = 3; else b = -3; // 可以表示为 a>5?b=3:b=-3逗号表达式 exp1, exp2, exp3, …expN 1.逗号表达式,就是用逗号隔开的多个表达式。 2.逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
if (a =b + 1, c=a / 2, d > 0) //只有最后的d>0起判断作用
有时候可以利用逗号表达式来简化代码
a = get_val();
count_val(a);
while (a > 0)
{
//业务处理
a = get_val();
count_val(a);
}
//如果使用逗号表达式,改写:
while (a = get_val(), count_val(a), a>0)
{
//业务处理
}
下标引用、函数调用和结构成员
[ ] 下标引用操作符
操作数:一个数组名
+
一个索引值
( ) 函数调用操作符
操作数:至少有1个操作数(函数名),参数也是其操作数
访问一个结构的成员 1、. 结构体 . 成员名 2、-> 结构体指针 -> 成员名 表达式求值 表达式求值的顺序一部分是由操作符的优先级和结合性决定。同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。 隐式类型转换 C 的整型算术运算总是至少以缺省(默认)整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型 提升 。(字符在内存中本质是128-127的整数,ASCII码值取值是0-127,是整型,因此字符型通常也被归类到整型家族)下面以一个计算实例来详细看一下整型提升:
还有哪些例子可以说明整形提升的存在?
1、
2、
算术转换 如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换 。
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。 操作符的属性 复杂表达式的求值有三个影响的因素。 1. 操作符的优先级 2. 操作符的结合性 3. 是否控制求值顺序。 两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。 操作符优先级
*如果我们没有很好的理解操作符的属性,就可能写出一些问题表达式,导致程序出现bug,以下为例:
1、
2、
3、
int main()
{
int i = 10;
i = i-- - --i * (i = -3) * i++ + ++i;
//i=i-- - --i *i*i++ + ++i,()内的值先算,此时所有的i=-3
//但之后我们只能保证后置--和++先于前置--和++先于*先于+和减,但在同一优先级却不能决定顺序
printf("i = %dn", i);//因此计算路径有很多
return 0;
}
以下是该段代码在不同编译器上的运行结果,因此属于问题代码
4、
5、
Linux环境下运行结果是;
10
4
vs2013环境下运行结果是:
12
4
这段代码中的第一个 + 在执行的时候,第三个 ++ 是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个 + 和第三个前置 ++ 的先后顺序。总结:我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的,因此在写程序的时候要尽量避免写太过复杂的表达式。



