操作符

算术操作符: + - * / %

移位操作符: >> <<

位操作符: &(按位与) |(按位或) ^(按位异或)

赋值操作符: = += -= *= /= %= &= ^= |= >>= <<=

单目操作符:

操作符 意义
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
前置、后置–
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换

关系操作符: > >= < <= != ==

逻辑操作符: && ||

条件操作符: exp1 ? exp2 : exp3

逗号表达式: exp1,exp2,exp3,…expN

下标引用、函数调用和结构成员: [] () . ->

除号/运算解析**

当除号两端都是整数时,执行的是整数除法,得到的值是整数

但是当除号两端有小数时,则可以算出小数

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
//当除号两端都是整数时,执行的是整数除法,得到的值是整数
int a = 9 / 2;
float b = 9 / 2;
//但是当除号两端有小数时,则可以算出小数
float c = 9 / 2.0;
printf("%d\n", a); //4
printf("%f\n", b); //4.000000
printf("%f\n", c); //4.500000
return 0;
}

移位操作符的使用

移位操作符 - 移动的是二进制位

1
2
3
4
5
6
7
8
9
10
11
12
13
//移位操作符
int main()
{
//左移操作符 - 移动的是二进制位
//而int是四字节 一个字节占8个比特位
//所以 00000000 00000000 00000000 00000010
int a = 2;
//a向左移动一位
//00000000 00000000 00000000 00000100
int b = a << 1;
printf("%d\n", b);//所以 打印输出4
return 0;
}

赋值操作符的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//赋值操作符
int main()
{
int a = 2;
a += 5;
int b = 9;
b -= 5;
int c = 3;
c %= 3;
printf("%d\n", a);//7
printf("%d\n", b);//4
printf("%d\n", c);//0
return 0;
}

单目操作符的简单使用

单目操作符 - 只有一个操作数

比如 :a+b 这里的 + 是双目操作符,它有两个操作数 ab

!a中的 !就是单目操作符,只有一个操作数 a

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//单目操作符
int main()
{
int a = 10;
a = -a;
printf("%d\n", a);//-10
int arr[10] = { 0 };
//计算的是数组的总大小 10*4
printf("%d\n", sizeof(arr));//40
printf("%d\n", sizeof(arr[0]));//4
int shu = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", shu);//10
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//++ --同理
int main()
{
int a = 10;
int b = ++a;//前置++ 先++ 后使用
printf("%d\n", a);//11
printf("%d\n", b);//11

int c = 10;
int d = c++;//后置++ 先使用 后++
printf("%d\n", c);//11
printf("%d\n", d);//10
return 0;
}

操作符 ~ 的使用

单目操作符 ~ 的意义 - 对一个数的二进制按位取反

即,把二进制位中的数字,1变为0,0变为1

扩展:

整数在内存中存储的是补码

一个整数的二进制表示有三种:原码、反码、补码

比如:-1,它的原码是 10000000 00000000 00000000 00000001

(第一位的1表示他的符号位 0为正 1为负 是不变的)

它的反码为 11111111 11111111 11111111 11111110

它的补码为 11111111 11111111 11111111 11111111

原码变为反码 除符号位 其余数字皆取反 反之亦然

反码变为补码 最后一位需要+1 反之 -1

当然这是对于负数而言的,正整数的原码、反码、补码是相同的

1
2
3
4
5
6
int main()
{
int a = 0;
printf("%d\n", ~a); //-1
return 0;
}

解析:以上代码,a = 0 整数在内存中存储的是补码

而0的补码,是 00000000 00000000 00000000 00000000

而使用操作符 ~ 后,按位取反 变为 11111111 11111111 11111111 11111111

打印输出时%d 解析时使用的是原码,所以需要把内存中存储的补码变为原码

补码:11111111 11111111 11111111 11111111

改成反码: 11111111 11111111 11111111 11111110

再改成原码:10000000 00000000 00000000 00000001

所以输出的是 -1

强制类型转换

类型转换是隐式的,由编译器自动执行,也可以是显式的,通过使用强制类型转换运算符来指定

1
2
3
4
5
6
7
8
9
10
11
//强制类型转换
int main()
{
//直接进行类型转换
int a = 3.14;
printf("%d\n", a);//3
//强制类型转换
int b = (int)3.14;
printf("%d\n", b);//3
return 0;
}

条件操作符的使用

1
2
3
4
5
6
7
8
9
10
11
12
//条件操作符(三目操作符)
int main()
{
int a = 0;
int b = 2;
int c = 0;
//exp1 ? exp2 : exp3
//exp1为判断条件 如果条件为真 计算exp2 如果为否 计算exp3
c = a > b ? a : b;
printf("%d\n", c);//2
return 0;
}

逗号表达式的使用

逗号表达式是从左向右依次计算的
整个逗号表达式的结果是最后一个表达式的结果

1
2
3
4
5
6
7
8
9
10
11
int main()
{
//(2, 4 + 5, 6);
int a = 0;
int b = 2;
int c = 3;
//a=2+2=4 c=4-4=0 b=0+2=2
int d = (a = b + 2, c = a - 4, b = c + 2);
printf("%d\n", d);//2
return 0;
}

关键字

关键字是C语言提供的,不能自己创建关键字

变量名不能是关键字

常见的关键字:

auto break case char const continue default do double

else enum extern float for goto if int long register

return short signed sizeof static struct switch typedef

union unsiged viod volatile while

简单介绍几个关键字

auto - 每个局部变量都是有auto修饰的,不过auto一般省略

extern - 是用来申明外部符号的

register - 寄存器关键字(建议将数据存储到寄存器中)

1
2
3
//大量/频繁被使用的数据,放到寄存器中,提升效率
//但是现在的编译器会把它认为可以放到寄存器中的数据,自己放到寄存器中
register int num = 100;

signed - 有符号的

unsigned - 无符号的

static - 静态的

union - 联合体(共用体)

void - 无(空)

扩展:计算机中数据可以存储在哪里?

寄存器

高速缓存

内存

硬盘

网盘

从下往上 造价越高,速度越快,空间越小

注:defineinclud 并不是关键字,它是预处理指令

关键字 typedef

typedef - 类型重定义

1
2
3
4
5
6
7
8
9
10
11
//将unsigned int重命名为u_int 
typedef unsigned int u_int;
int main()
{
unsigned int num = 100;
//即,你使用u_int 时,就是在使用unsigned int
u_int num2 = 200;
printf("%d\n", num);//100
printf("%d\n", num2);//200
return 0;
}

关键字 static

static - 静态的 - 用于修饰变量和函数

  1. 修饰局部变量 - 静态局部变量
  2. 修饰全局变量 - 静态全局变量
  3. 修饰函数 - 静态函数

修饰局部变量

先看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void test()
{
int a = 1;
a++;
printf("%d ", a);// ?
}
int main()
{
int i = 0;
while (i<10)
{
test();
i++;
}

return 0;
}

这时候打印输出的是什么呢?

答案:2 2 2 2 2 2 2 2 2 2

为什么呢?因为局部变量的生命周期是进入作用域时生命周期开始,出作用域生命周期结束

当你循环一次后,调用整个函数,它创建 a 变量,当结束后,它销毁 a 变量

每次重新调用函数都会重新创建a变量,结束销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void test()
{
//给变量前加上 static
static int a = 1;
a++;
printf("%d ", a); //?
}
int main()
{
int i = 0;
while (i<10)
{
test();
i++;
}

return 0;
}

这时候打印输出什么?

答案:2 3 4 5 6 7 8 9 10 11

static修饰局部变量,改变了局部变量的生命周期(本质上是改变了变量的存储类型)

注意:static修饰的局部变量作用域并没有改变

扩展:

栈区 - 存储局部变量,函数的参数

堆区 - 动态内存分配

静态区 - 全局变量,static修饰的静态变量

修饰全局变量

新建一个add.c的源文件,定义一个全局变量

1
int g_val = 2022;

然后在另一个源文件中使用

1
2
3
4
5
6
7
//extern声明外部符号的
extern int g_val;
int main()
{
printf("%d\n", g_val);//2022
return 0;
}

打印输出为2022

那我们给全局变量使用static进行修饰

1
static int g_val = 2022;

这时ctrl+F5运行,会发现报错

是因为static修饰全局变量,使得这个全局变量只能在自己所在的这个源文件(.c)中使用,其他源文件无法使用

全局变量,在其他源文件内部可以被使用,是因为全局变量具有外部链接属性

但是被static修饰后,就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了

修饰函数

add.c的源文件,定义一个函数

1
2
3
4
int Add(int x, int y)
{
return x + y;
}

然后在另一个源文件中调用

1
2
3
4
5
6
7
8
9
10
//声明函数
extern int Add(int x, int y);
int main()
{
int a = 10;
int b = 20;
int sum = Add(a, b);
printf("sum = %d\n",sum);//sum = 30
return 0;
}

那我们给这个Add函数用static进行修饰

这时ctrl+F5运行,会发现报错

所以,static修饰函数类似static修饰全局变量

static修饰函数,使得函数只能在自己所在的源文件中使用,不能在其他源文件中使用

本质上:static是将函数的外部链接属性变成了内部链接属性(和static修饰全局变量一样!)