算术运算符
+ - * / % ++ --
1.除了%操作符之外,其他的几个操作符可以作用于整数和浮点数
2.%操作符的两个操作数必须为整数。返回的是整除之后的余数。
3.对于/操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
关系运算符
> < >= <= != ==
C语言的基本数据类型有char、int、double,我们暂时认为只有char和int适用于上述关系运算符,double和字符串(字符数组)的关系运算以后再讨论。
注:“=”是赋值,“==”才是判断两个数是否相等,不能混用
逻辑运算符
&& 逻辑与
|| 逻辑或
! 逻辑非
- 区别 逻辑与&&和 按位与&
1&&2 -----> 1
1&2 -------> 0
1.&&具有短路功能,而&不具有短路功能。
2.当&运算符两侧的表达式的结果均为真时,整个运算结果才为真。
当&&操作符第一个表达式为 false时,结果为 false,并且不再计算第二个表达式。- 区别 逻辑或|| 和 按位或 |
1 || 2 -----> 1
1 | 2 ------> 3
|| 和 | 都是表示“或”,区别是||只要满足第一个条件,后面的条件就不再判断,而|要对所有的条件进行判断。
&& 和 || 的代码
#include<stdio.h>
int main(){
int i = 0,a = 0, b = 2,c = 3, d = 4;
i = a++ && ++b && d++;
printf("a = %d\n b = %d\n c = %d\n d = %d\n",a,b,c,d);
return 0;
}
结果:1 2 3 4
#include<stdio.h>
int main(){
int i = 0,a = 0, b = 2,c = 3, d = 4;
i = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
结果:1 3 3 4
#include<stdio.h>
int main(){
int i = 0,a = 1, b = 2,c = 3, d = 4;
i = a++ && ++b && d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
结果:2 2 3 5
#include<stdio.h>
int main(){
int i = 0,a = 1, b = 2,c = 3, d = 4;
i = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
return 0;
}
结果:2 2 3 4
赋值运算符
=
+= -= *= /= %=
&= |= ^=
>>= <<=
赋值运算符支持的是C语言的基本数据类型,包括char、int和double,字符串(字符数组)不能使用赋值运算符。
逗号运算符
expl,exp2,exp3,…expn
逗号表达式,就是用逗号隔开的多个表达式。逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
#include <stdio.h>
int main(){
int a = (3, 4, 5);
printf("a=%d\n", a); //a = 5
return 0;
}
条件运算符
exp1 ? exp2 : exp3
2>1 ? 10 : 20
因为2>1成立,因此它的值就是表达式2的值,也就是10。
sizeof运算符
sizeof是C语言的关键字,它用来计算变量(或数据类型)在当前系统中占用内存的字节数。
sizeof不是函数,产生这样的疑问是因为sizeof的书写确实有点像函数,sizeof有两种写法
①用于数据类型
sizeof(数据类型);
数据类型必须用括号括住。
printf("字符型变量占用的内存是=%d\n",sizeof(char));
// 输出:字符型变量占用的内存是=1
printf("整型变量占用的内存是=%d\n",sizeof(int));
// 输出:整型变量占用的内存是=4
②用于变量
变量名可以不用括号括住,带括号的用法更普遍,大多数程序员采用这种形式。
sizeof(变量名);
sizeof 变量名;
sizeof的应用
1.求数组的大小
#include <stdio.h>
int main()
{
int arr[10]= { 0 };
printf ("%d\n" , sizeof(arr)); //40,单位是字节
printf ("%d\n", sizeof(int[10])); //40, int[10]是arr数组的类型
return 0;
}
2.sizeof计算指针
#include <stdio.h>
void test1(int arr[])
{
printf("%d\n",sizeof(arr)); // 4/8 -> 指针的大小
}
void test2(char ch[])
{
printf("%d\n",sizeof(ch)); // 4/8 -> 指针的大小
}
int main()
{
int arr[10] = {0};
char ch[10] = {0};
printf("%d\n", sizeof(arr)); //40
printf("%d\n", sizeof(ch)); //10
test1(arr);
test2(ch);
return 0;
}
用sizeof求数组大小时要注意,当数组被当作参数传递过去时,传过去的是数组的首元素的地址,因为是地址,所以用指针来接收,所以函数里面用sizeof求的都是指针的大小,不是数组,32位平台下都是4字节,64位平台下都是8字节
3.特殊例子:
#include <stdio.h>
int main()
{
short s =5;
int a = 10;
printf ( "%d \n", sizeof(s = a + 2)); //结果为:2
printf ( "%d \n", s); //结果为:5
return 0;
}
解释:因为sizeof括号中的表达式是不参与运算的! 源文件在变成可执行文件过程中,经过预处理,编译,汇编,链接,sizeof在编译中就判断了s的大小,给出值为2,并且表达式不参与运算,而printf函数是在运行的时候再执行的,所以直接打印出2,和下面s变量的值5
strlen与sizeof的区别
strlen -函数:求字符串长度的,找’\0’之前出现的字符个数
sizeof -操作符:算变量/类型所占内存大小,单位是字节
sizeof的返回类型
#include <stdio.h>
int i; //i是全局变量,不初始化,默认是0
int main ()
{
i--; //-1
if (i > sizeof(i))
{
printf ( "> \n" );
}
else
{
printf ("<\n");
}
return 0;
}
这个例子的输出结果是>,sizeof这个操作符,算出的结果的类型是unsigned int,因为大小是没有负数的,当进行 i > sizeof(i) 比较时,会把有符号的 i 算术转换为无符号,因为-1在内存中全1存储,变为无符号后原码补码一样,将会是一个很大的数,肯定比4字节大,所以输出>
移位运算符
<< 左移位操作符
>> 右移位操作符
注:他们的操作数都必须是整数
右移操作符移位规则:
- 算术右移:右边丢弃,左边补原符号位
- 逻辑右移:右边丢弃,左边补0
左移操作符移位规则:
- 算术左移和逻辑左移一样都是右边补0
算术左移和算术右移主要用来进行有符号数的倍增、减半;
逻辑左移和逻辑右移主要用来进行无符号数的倍增、减半;
#include<stdio.h>
int main(){
int a = -1;
//整数的二进制表示有:原码、反码、补码
//存储到内存的是补码
//10000000,00000000,00000000,00000001 原码
//11111111,11111111,11111111,11111110 反码
//11111111,11111111,11111111,11111111 补码
int b = a>>1;
printf("%d\n",b); //b = -1
//因此,计算机采用的是 算术右移
return 0;
}
位运算符
& 按位与
| 按位或
^ 按位异或
注:他们的操作数都必须是整数
不能创建临时变量(第三个变量),实现两个数的交换。
但实际开发会选择创建第三个变量,执行的效率和开销
都大于以下两种方法
//法一
#include<stdio.h>
int main(){
int a = 10;
int b = 20;
a = a^b;
b = a^b;
a = a^b;
printf("a = %d b = %d\n",a,b);
return 0;
}
-----------------------------------------------
//法二
#include<stdio.h>
int main(){
int a = 10;
int b = 20;
a = a+b;
b = a-b;
a = a-b;
printf("a = %d b = %d\n",a,b);
return 0;
}
求一个整数存储在内存中的二进制中1的个数
#include <stdio.h>
int main()
{
int num = 0;
int count = 0;
printf("请输入一个整数:");
scanf("%d",&num);
//方法一(当值为负数时不满足)
// while (num)
// {
// if (num % 2 == 1)
// count++;
// num = num / 2;
// }
//方法二
// int i = 0;
// for(i = 0; i < 32 ; i++){
// if(1 == ((num>>i) & 1))
// count++;
// }
//方法三
while(num){
count++;
num = num&(num-1);
}
printf("二进制中1的个数 = %d\n", count);
return 0;
}
比较两个数二进制中不同位的个数
#include <stdio.h>
int count_bit_one(int n){
int count = 0;
while(n){
n = n&(n-1);
count++;
}
return count;
}
int get_diff_bit(int m,int n){
int tmp = m^n; //异或之后不同的位都变为二进制1
int count = 0;
return count_bit_one(tmp); //统计二进制1的个数
}
int main(){
int m = 0;
int n = 0;
printf("请输入两个整数:");
scanf("%d%d", &m,&n);
int count = get_diff_bit(m,n);
printf("count = %d\n", count);
return 0;
}
输入:1999 2299
结果:count = 7
下标引用、函数调用和结构体
下标引用[]
函数调用()
结构体 . ->
单目运算符
运算符 | 作用 |
---|---|
+ | 正值 |
- | 负值 |
! | 逻辑反操作 |
~ | 对一个数的二进制位按位取反 |
++ | ++前置、后置++ |
- - | - -前置、后置- - |
& | 取地址 |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
sizeof | 操作数的类型长度(以字节为单位) |
!逻辑取反 和 ~ 按位取反 的区别:
! :代表逻辑取反,即:把非0的数值变为0,0变为1
~ :表示按位取反,即在数值的二进制表示方式上,将0变为1,将1变为0
~ 按位取反 和 反码 的区别:
按位取反不考虑符号位,所有位都取相反
反码需要保留符号位,除符号位以外所有位取反
#include<stdio.h>
int main(){
int a = 0;
int b = 5;
int c = 0;
// ~ 按(二进制)位取反
//00000000,00000000,00000000,00000000 原/反/补
//11111111,11111111,11111111,11111111 补码
//11111111,11111111,11111111,11111110 反码
//10000000,00000000,00000000,00000001 原码
printf("%d\n",~a); // -1
printf("%d\n",!b); // 0
printf("%d\n",!c); // 1
return 0;
}
运算符的属性
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺
表格优先级从上到下,依次递减
类别 | 运算符 | 结合性 |
---|---|---|
后缀运算符 | [ ] ( ) . -> | 从左到右 |
单目运算符 | + - ! ~ ++ - - & * (类型) sizeof | 从右到左 |
算术运算符 | * / % | 从左到右 |
算术运算符 | + - | 从左到右 |
移位运算符 | << >> | 从左到右 |
关系运算符 | > < >= <= | 从左到右 |
关系运算符 | == != | 从左到右 |
位运算符 | & | 从左到右 |
位运算符 | ^ | 从左到右 |
位运算符 | | | 从左到右 |
逻辑运算符 | && | 从左到右 |
逻辑运算符 | || | 从左到右 |
条件运算符 | ? : | 从右到左 |
赋值运算符 | = += -= *= /= %= <<= >>= &= ^= |= | 从右到左 |
逗号运算符 | . | 从左到右 |
1. 同一优先级的运算符,运算次序由结合方向所决定。
2. 简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
3.只有 单目运算符、赋值运算符、条件运算符的结合性是 从右到左
#include<stdio.h>
int main(){
int a,b,c;
a = 5;
c = ++a;
b = ++c,c++,++a,a++;
b += a++ + c;
printf("a = %d b = %d c = %d\n",a,b,c);
//a = 9 b = 23 c = 8
//赋值运算符高于逗号运算符
return 0;
}
算术转换
如果某个操作数的类型较低,那么要先转换为另一个操作数的类型后再执行运算
隐式类型转换
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的
//计算机中是以补码形式存放数据
#include<stdio.h>
int main(){
char a = 3;
//00000000 00000000 00000000 00000011 原码/反码/补码
//00000011 a占1字节
char b = 127;
//00000000 00000000 00000000 01111111 原码/反码/补码
//01111111 b占1字节
//a和b如何相加
//00000000 00000000 00000000 00000011
//00000000 00000000 00000000 01111111
//00000000 00000000 00000000 10000010
char c = a+b;
//10000010 c(最高位为1),则c为负值,符号位为1
//11111111 11111111 11111111 10000010 补码
//11111111 11111111 11111111 10000001 反码
//10000000 00000000 00000000 01111110 原码----> -126
printf("%d\n",c); c ----> -126
return 0;
}
#include<stdio.h>
int main(){
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if(a == 0xb6){
printf("a");
}
if(b == 0xb600){
printf("b");
}
if(c == 0xb6000000){
printf("c");
}
return 0;
}
//打印 c
#include<stdio.h>
int main(){
char c = 1;
printf("%u\n",sizeof(c)); //1
printf("%u\n",sizeof(+c)); //4
//c只要参与表达式运算,就会发生整型提升,所以sizeof(+c)是4个字节
//表达式-c也会发生整型提升,所以sizeof(-c)是4个字节
printf("%u\n",sizeof(!c)); //4
return 0;