算数操作符
+ - * / %
1.除法操作符
当两个数都是整数时,执行整数的除法,但有一个数时浮点数时执行浮点数的除法运算。它的应用之一是可以判断一个整数的位数
2.取模操作符
取模操作时两个数必须为整数。它的应用之一是可以判断一个多位数每一位的数字
如求水仙花数中就应用到了求位数和求每一位的大小
移位操作符
该操作符作用于数的补码,功能不再赘述,对于整数而言其补码就时原码,操作后换算易懂。
对于负数而言,其补码等于源码符号为不变其余为按位取反后+1.当操作完成后对于补码-1,然后符号为不变剩余位按位取反得到原码。
>> 右移操作符 一般情况该操作符操作时,可有除二的效果,但是对于-1而言不适用
int main()
{
int a = -1;
printf("%d", a>>1);
system("pause");
return 0;
}
对于该操作输出结果仍位-1
<<左移操作符 有乘二的效果。
1.算数右移:右侧丢一位,左侧补足符号位
2.逻辑右移:右侧丢弃,左侧补0(两者相异于编译器,但前者更为科学,因此如果一个程序采用了有符号数的右移位操作,它就是不可移植的)
对于移位操作符,不可移动负数位 如n<<-1, 这个标准时未定义的。
位操作符
& 按位(二进制位补码)与(皆1则1,有0则0) | 按位或 (皆0为0,有1则1) ^ 按位异或(相同为0相异为1)
其中当一个数与1按位与(&)的时候,可以得到该数的最低位
按位异或的值具有传递性和对称性,一个数和自身异或等于零,反之同零异或等于它本身(出来混总要还得性质,无论多少个数相异或,只要出现与前几位重复的数,值就会一步一步返回若全部重复会归零)
例:若求1,2,3,4,1,2,3,4,5这几个数中求出只出现一次的数字
最后5轮空,5和0异或结果为5.
它同样可以应用到不借助第三变量的两个数换值
赋值
赋值表达式的结合性(即求值顺序,每一种操作符的结合性及其他性质在文末表中指出)从右向左:
a=x=y+3相当于a=(x=y+3)也等同于x=y+3;a=x;
虽然大部分情况下两种书写方式赋予的值是一样的,但是后者更易读易懂。然而出现赋值不同的情况是
如果X是个字符变量,那么他们之间会存在隐式转换, 解释详见《C和指针》page 70底部。
单目操作符
! 逻辑取反操作符,其产生的结果位整形0,或者1 可用于if while语句等中的执行
~ 对整形类型的操作数的补码进行求补操作
sizeof操作符,&取地址操作符。
sizeof是判断它操作数的类型长度,字节是它的单位。以下均是正确的表达式
sizeof(int) //int不能去掉括号
sizeof a;//不建议这样写
sicccczeof(a);
szie(a=b-2);
其中sizeof()括号中可以是表达式,但第四个sizeof表达式中b-2并没有向a赋值,即不参与运算。
&产生它的操作数的地址,它可以取得变量的地址,从而赋值给指针变量。
※在sizeof求数组长度时
关于数组名,其表示的是首元素的地址
但在一下两种情况表示的是整个数组
sizeof(数组名) 在这里表示的是这里的数组的大小,单位是字节,数组名表示整个数组
&数组名,取出的是整个数组的地址,数组名表示整个数组
&数组名+1是跳过整个的数组的地址,而arr+1是第二个元素的地址
※ 当函数中arr作为首元素地址传入指针,sizeof(arr)计算的是指针的类型长度,所以无论arr是整型还是字符型其在同一个设备上的大小是一样的
~操作符对整型操作数进行求补操作,即操作数1位变成0,0变成1
注意:
int main()
{
printf("%d",~0);
reuturn0;
}
该值输出的是-1,因为要以整型格式输出,存储在内存中的二进制数是补码,在求补操作以后,数字变成了负数,要以整型形式输出需要将补码转换为源码。(其中针对于求补的操作数也是在内存中以源码的形式进行求补的)
应用:将二进制数00000000000001010倒数第三位改成1.
可以用按位或 000000000000000100得到。得到00000000000001110,但想恢复时可以通过将000000000000000100进行求补操作然后按位与得到000000000000001010.
++和--
该操作符分为前置和后者
++a时使用前自增,a++是使用后自增 --同理。
此处增值操作符执行的是对变量值的一份拷贝,这份拷贝应用到了周围表达式如c=a++,这种情况下在进行复制应用到表达式以后增加a的值,++a表示在增加a的值以后复制应用,--同理,所以这是一个值,不能对这个值进行赋值 如 a++=10,这是错误的。
*操作符是间接访问操作符
如
int main()
{
int a=0;
int *p=&a;//此处*是int*型,用来定义指针变量
*p = 5;//此处指间接访问操作符访问p所指向的值
(类型)强制类型转换
将两个不同类型赋值后提示
强制类型转换可以解决问题,但是会导致精度丢失。
关系操作符
关于操作符的易错点,在使用关系操作符,尤其是在应用与if,while语句的判断时,==和=一定要分清,前者是判断后者是赋值,因为c不具备布尔类型所以赋值的情况下任何非零皆为真
逻辑操作符 (控制求值顺序)
&& ||
&& 若左侧值为假,则不执行右侧表达式
输出结果
因为a++时在使用后增值,对于&&的运算得到的值时0,从而后面的不运算
|| 若左值为真,则不执行右侧表达式 ,他们具有短路性质。
但位运算 | & 操作符两边的操作数都需要进行求值,且逻辑操作的值只有0和1,不同于位运算,所以在执行if while等条件判断时需要仔细
条件操作符;(控制求值顺序)
逗号操作符 (控制求值顺序)
表达式自左向右逐个求值,但整个逗号表达式式的值就是最后的那个表达式的值。一版情况下不建议使用,难分辨易混乱。
下标引用操作符和函数调用
下标应用操作符 []
它有两个操作数,数组名和索引值且它和*间接访问操作符是等价的,但优先级不同,如
arr[数]==*(arr+数);
函数调用操作符至少有一个操作数,那就是函数名
结构成员
-> 该操作符用于结构成员的访问,用法如下
#include<stdio.h>
#include<Windows.h>
struct poppin //定义结构体类型,名为poppin
{
int popper; //定义结构体成员
char blf[10];
char sex[6];
};
void test(struct poppin*ps)
{
strcpy(ps->blf,"KOD");//此处ps等价于(*p).
}
int main()
{
struct poppin s; //定义结构体变量
test(&s);
printf("%s", s.blf);//输出结构体中的成员blf
system("pause");
return 0;
}
输出结果
隐式类型转换
将字符型或者短整型进行整型算数求值的时候存在隐式转转换,换句话说当要进行运算时,编译器自动将进行整型提升
int main()
{
char a=1;
char b=127;
char c= a+b;//这个过程中的隐式转换导致了最终结果的不同
printf("%d",c);
}
最终的输出结果位 -127。因为a,b,c都是字符型,他们值由一个字节,在进行整型的加法运算时,会将二进制位从八位调整到32位,最后补码相加得到的二进制数的有效数字大于八位,要以整型输出,补齐符号位(符号位为1则全补1),得到源码为-127.
操作符的属性
操作符具有三个属性,结合性,优先级,是否控制求值顺序。
表见《C和指针》page81-82.
优先级决定的是两个相邻操作符,若优先级相同,则按照他们的结合性。除此之外,编译器可以自由决定任何顺序对表达式的求值,只要不为非 , && || ?: 这几个操作符的限制。
如 s=a*b+c*d+e*f 不同的编译器求出的值可能不同。
再者 如 c+--c, 我无法判断左侧的C是在c--执行之前还是执行之后赋值的,所以不同编译器有不同的结果。
这类表达式都是有歧义的,个人认为一个合格的程序员应该 是严谨和追求高效性,不仅通俗易懂而且具有很高的可读性和一致性才是关键,而不是追求极力简洁和酷炫。
本章总结个人认为是片面和考虑欠妥的,但贴出的实例都予以测试,任何错误之处请指出.