目录
一.十大操作符详解
(一)算术操作符
对2个操作数进行算术运算。注:操作符也被为运算符
(1)加法:+
有2个操作数;9+9;
(2)减法:-
(3)乘法:*
(4)除法:/,两个整数除,得到整数(商);带小数点除,值为小数
(5)取模(取余):%,计算整除后的余数;
1.%只能用于整数取余,且两侧操作数必须是整数,返回值是余数(整数);
2.整数类型和浮点数相互打印输出,会变为0;
3.整数类型赋给浮点数类型,末尾加.0;小数类型赋给整数类型,去小数;
4.除%的4个操作符,两侧操作数都为整数执行整数算术,至少有一个操作数为浮点数,则进行浮点数除法;
注:操作数:操作符左右两侧的参数
扩展:%f %lf,默认打印小数点后6位
(二)移位操作符
扩展:2进制位知识补充
数值的表现形式有多种:
- 数值12
10进制表示:12
8进制表示:14
16进制:c
2进制:1100
以上本质数值都是12。
整数的2进制表现形式分为3种:
(1)原码
(2)反码
(3)补码
#include<stdio.h>
int main()
{
//按照一个数的正负,直接写出它的二进制表示形式得到的就是原码
//
//正数
//正数的原码、反码、补码是相同的
//负数的原码、反码、补码要经过计算的
//反码是原码的符号位不变,其他位按位取反,就是反码
//补码是反码+1
//整型占4个字节(32bit)
//00000000000000000000000000001010 - 原码
//00000000000000000000000000001010 - 反码
//00000000000000000000000000001010 - 补码
int a = 10;
int b = -10;
//有符号数最高位是0表示正数,最高位是1表示负数;
//10000000000000000000000000001010 - 原码
//11111111111111111111111111110101 - 反码
//11111111111111111111111111110110 - 补码
//11111111111111111111111111110110 - 补码
//10000000000000000000000000001001
//10000000000000000000000000001010 - 原码
return 0;
}
注:内存中存储的是补码,因此移位移的是二进制中的补码,打印出来的是原码;
补码-原码:
方法一:符号位不变,取反+1(一模一样),得到原码;
方法二:-1,符号位不变,取反,先得反码,再得原码;
1.<<,左移操作符
2.>>,右移操作符
注:移位操作符的操作数只能是整数。移位移的是整数的二进制位(补码);
int num=1;
num<<1; 左移一位
num<<1.5;//error
(1)左移操作符<<
- 正数左移
#include<stdio.h>
int main()
{
int a = 10;
int b = a << 1;
//00000000000000000000000000001010 -a的补码
//00000000000000000000000000010100 -表达式a<<1的值,即a左移一位的值
//移位规则:补码,左边抛弃,右边补0
printf("%d\n", b);
printf("%d\n", a);
//a的值不变,知识表达式a<<1的值变成了20
return 0;
}
- 负数左移:
#include<stdio.h>
int main()
{
int a = -10;
//10000000000000000000000000001010
//11111111111111111111111111110101
//11111111111111111111111111110110 - 补码
//
int b = a << 1;
//
//11111111111111111111111111101100 - b的补码
//10000000000000000000000000010011
//10000000000000000000000000010100 - b的原码
//
printf("b=%d\n", b);//-20
printf("a=%d\n", a);
return 0;
}
总结:左移数有乘2的现象;
(2)右移操作符>>
#include<stdio.h>
int main()
{
int a = -1;
//10000000000000000000000000000001
//11111111111111111111111111111110
//11111111111111111111111111111111 a的补码
//
int b = a >> 1;
//算术右移:11111111111111111111111111111111 -1
//逻辑右移:
//01111111111111111111111111111111 b的补码
//00000000000000000000000000000000
//00000000000000000000000000000001 b的原码 1
printf("%d\n", b);
printf("%d\n", a);
return 0;
}
int a=1;
a>>-1; //error
(三)位操作符
1.& //按位与
计算规则:对应的二进制位有0,则为0,两个同时为1,才为1
2.| //按位或
计算规则:对应的二进制位有1则为1,两个同时为0则为0
3.^ //按位异或
计算规则:对应的二进制位相同为0,相异为1
注:他们的操作数必须是整数,也是针对二进制位进行计算的(补码)。
实例操作:
//五.位操作符的作用演示
//& - 按2进制位与
//对应的二进制位有0,则为0,两个同时为1,才为1
//
//^ - 按2进制位异或
//对应的二进制位:相同为0,相异为1
//
//按2进制位或
//对应的二进制位有1则为1,两个同时为0则为0
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -5;
//10000000000000000000000000000101
//11111111111111111111111111111010
//11111111111111111111111111111011 -补码
int c = a & b;
//00000000000000000000000000000011-a的补码
//11111111111111111111111111111011-b的补码
//00000000000000000000000000000011-c的补码
int d = a | b;
//00000000000000000000000000000011
//11111111111111111111111111111011
//11111111111111111111111111111011
int e = a ^ b;
//00000000000000000000000000000011
//11111111111111111111111111111011
//11111111111111111111111111111000
//11111111111111111111111111110111
//10000000000000000000000000001000
//-8
printf("%d\n", c); //c=3
printf("%d\n", d); //d=-5
printf("%d\n", e); //e=-8
return 0;
}
扩展:一道变态的面试题:
#include<stdio.h>
int main() //方法1
{
int a = 3;
int b = 5;
//int tmp = 0;//临时变量方法
//printf("%d %d\n", a, b);
//tmp = a;
//a = b;
//b = tmp;
//printf("%d %d\n", a, b);
//a = a + b; //算术搞定
//b = a - b;
//a = a - b; //数太大溢出,一个不溢出,2个加起来溢出
//printf("%d %d\n", a, b);
a = a ^ b; //位操作符搞定
b = a ^ b;
a = a ^ b;
printf("%d %d\n", a, b);//没有产生进位
return 0;
}
(四)赋值操作符
赋值操作符
=
复合赋值操作符(对自己进行操作)
1.+=
// a+=1等价于 a=a+1;
2.-=
3.*=
4./=
5.%=
....
实现自加1的操作;
a=a+1;
a++;
++a;
a+=1;
以上4个都等价;
#include<stdio.h>
int main()
{
int a = 10;
a = a + 5;
a += 5;
int b = 12;
b = b >> 1;
b >>= 1;
return 0;
}
(五)单目操作符
单目操作符
操作数只有一个;
1.! 逻辑反操作
2.- 负值
3.+ 正值
4.& 取地址
5.sizeof 操作数的类型长度(以字节为单位)
6.~ 对一个数的二进制按位取反
7.-- 前置、后置--
8.++ 前置、后置++
9.* 间接访问操作符(解引用操作符)
10.(类型) 强制类型转换
1.! 逻辑反操作
//1.! 逻辑反操作
#include<stdio.h>
int main()
{
//C语言中0表示假,非0表示真
int flag = 0;
//if (flag)//flag如果为真,做.....
//{
// printf("hehe\n");
//}
if (!flag)
{
printf("hehe\n");
}
printf("%d\n", flag);
printf("%d\n", !flag);
return 0;
}
补充:布尔类型
补充一下:布尔类型
//C99中引入的
//布尔类型就是用来表示真假的类型
//
#include<stdio.h>
#include <stdbool.h>
//实例
//bool is_leap_year(int y)
//{
// if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
// return true;
// else
// return false;
//}
//
int main()
{
_Bool flag = true; //类型值只有 true false,本质上和0 1差不多
if (flag)
{
printf("hehe\n");
}
return 0;
}
2.- +操作符
1.-
把正数变负数;
2.+
基本用不上;
作用就是正负数转化;
#include<stdio.h>
int main()
{
int a = -10;
printf("%d\n", a); //-10
printf("%d\n", -a); //10
return 0;
}
扩展:有符号、无符号数
无符号数表示正数,补码最高位不作为符号位
#include<stdio.h>
int main()
{
unsigned int num = -10;
printf("%u\n", num);//无符号数用%u输出
//补码最高位不是符号位,输出直接按数值输出
//4294967286
return 0;
}
注:
signed int a=10;
int a=10;
两者等价
3.取地址符&和间接访问符*一起使用(一般)
&
取出一个对象的在内存中的地址
*
通过解引用来找到对应的变量
#include<stdio.h>
int main()
{
int a = 10;
printf("%p\n", &a);
int* pa = &a;
char ch = 'w';
char* pc = &ch;
char arr[10] = { 0 };
char* p2 = arr;
char* p3 = &arr[0];
char* p = "abcdef";//字符串赋给指针变量给的是a的地址
printf("%p\n", p);
printf("%c\n", *p);
*pa = 20;//解引用操作,通过pa找到a
printf("%d\n", a);
return 0;
}
4.sizeof
//5.sizeof
// /函数调用的时候,要写()
//但是sizeof后边的括号可以省略(计算类型时不可以),说明sizeof不是函数
//根据类型报
#include<stdio.h>
int main()
{
//int a = 10;
//printf("%d\n", sizeof(a));
//printf("%d\n", sizeof a);//ok
//printf("%d\n", sizeof(int));
//int arr[10] = {0};
//printf("%d\n", sizeof arr);//ok
//printf("%d\n", sizeof(arr));//ok
//printf("%d\n", sizeof(int[10]));//ok,int arr[10]的类型是 int[10]
int a = 10;
short s = 5;
printf("%d\n", sizeof(s = a + 3)); //2,sizeof的表达式不参与计算,只考虑s,是2个字节
printf("%d\n", s); //5
return 0;
}
5.~ 对一个数的二进制按位取反
//~ 按位取反,把补码二进制位(包括符号位)都取反,只针对整数
//00000000000000000000000000000000
//11111111111111111111111111111111 - 按位取反,补码是全1
//原码就是-1
#include<stdio.h>
int main()
{
int a = 0;
printf("%d\n", ~a);//-1
return 0;
}
//& | ^ >> << ~
// 对二进制位的操作
#include<stdio.h>
int main()
{
int a = 9;
//00000000000000000000000000001001
//00000000000000000000000000010000 1<<4
//00000000000000000000000000011001
//
//把a的二进制中第5位改成1
a |= (1<<4);
printf("%d\n", a);//25
//把a的二进制中的第5位改回来,变成0
//00000000000000000000000000011001
//11111111111111111111111111101111
//00000000000000000000000000001001
a &= (~(1 << 4));
printf("%d\n", a);//9
return 0;
}
6.
(六)关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
(七)逻辑操作符
&& 逻辑 与
|| 逻辑 或
(八)条件操作符
exp1 ? exp2 : exp3
(九)逗号表达式
exp1, exp2, exp3, …expN
//代码1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//逗号表达式
c是多少?
//代码2
if (a =b + 1, c=a / 2, d > 0)
//代码3
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. [ ] 下标引用操作符
2. ( ) 函数调用操作符
3.访问一个结构的成员
. 结构体.成员名
-> 结构体指针->成员名
1. [ ] 下标引用操作符
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
[ ]的两个操作数是arr和9。
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //实用()作为函数调用操作符。
test2("hello bit.");//实用()作为函数调用操作符。
return 0;
}
#include <stdio.h>
struct Stu
{
char name[10];
int age;
char sex[5];
double score;
};
void set_age1(struct Stu stu)
{
stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
pStu->age = 18;//结构成员访问
}
int main()
{
struct Stu stu;
struct Stu* pStu = &stu;//结构成员访问
stu.age = 20;//结构成员访问
set_age1(stu);
pStu->age = 20;//结构成员访问
set_age2(pStu);
return 0;
}
二.表达式求值