目录
[一. 键盘输入](#一. 键盘输入)
[1.1 grtchar()](#1.1 grtchar())
[1.2 scanf()](#1.2 scanf())
[二. 全局变量/局部变量(函数的分类)](#二. 全局变量/局部变量(函数的分类))
[三.C语言内存模型(堆栈内存and so on )](#三.C语言内存模型(堆栈内存and so on ))
[3.1 栈区(stack)(未完待续.....)](#3.1 栈区(stack)(未完待续.....))
[3.2 堆区](#3.2 堆区)
[3.3 全局静态区](#3.3 全局静态区)
[1. 未初始化](#1. 未初始化)
[2. 已初始化](#2. 已初始化)
[3.4 常量区](#3.4 常量区)
[3.5 代码区](#3.5 代码区)
[4.1 自动转换](#4.1 自动转换)
[4.2 强制转换](#4.2 强制转换)
[4.3 数据类型的精度](#4.3 数据类型的精度)
[五. 运算符](#五. 运算符)
[1. 算数运算符](#1. 算数运算符)
[2. 比较运算符](#2. 比较运算符)
[3. 赋值运算符](#3. 赋值运算符)
[4. 自增运算符](#4. 自增运算符)
[5. 逻辑运算符](#5. 逻辑运算符)
[6. 条件运算符(三目运算符)](#6. 条件运算符(三目运算符))
[7. 逗号运算符](#7. 逗号运算符)
[8. 运算符优先级(未完待续.....)](#8. 运算符优先级(未完待续.....))
[9. 位运算符](#9. 位运算符)
[1. 补码的核心定义](#1. 补码的核心定义)
[2. 按位取反 (~) 的本质](#2. 按位取反 (~) 的本质)
[3. 按位取反步骤(公式为~ a = -(a + 1))](#3. 按位取反步骤(公式为~ a = -(a + 1)))
一. 键盘输入
1.1 grtchar()
getchar() 函数用于从标准输入中读取一个字符,返回值为读取的字符。
getchar() 函数会等待用户输入一个字符并按下回车键,然后返回该字符的 ASCII 值。
cpp
// 这是一个简单的 getchar()示例,getchar只能读取数据类型为字符的变量
// getchar() 函数用于从标准输入中读取一个字符,返回值为读取的字符。
// getchar() 函数会等待用户输入一个字符并按下回车键,然后返回该字符的 ASCII 值。
printf("请输入一个字符:\n");
printf("请输入一个字符:\n");
char ch = getchar(); // 使用 getchar 获取一个字符输入,并将其存储到变量 ch 中
printf("您输入的字符是:%c\n", ch); // 输出用户输入的字符
1.2 scanf()
使用 scanf 函数从标准输入中读取数据需要包含 stdio.h 头文件
scanf() 函数用于从标准输入中读取数据,格式为 scanf("格式控制字符串", 参数列表),类似于 printf() 的格式化输出。
谨防 "空格陷阱" 。
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 这是一个简单的 printf() 示例
printf("请输入一个字符:\n");
printf("请输入一个字符:\n");
char ch = getchar(); // 使用 getchar 获取一个字符输入,并将其存储到变量 ch 中
printf("您输入的字符是:%c\n", ch); // 输出用户输入的字符
// 使用 scanf 函数从标准输入中读取数据需要包含 stdio.h 头文件
// scanf() 函数用于从标准输入中读取数据,格式为 scanf("格式控制字符串", 参数列表),类似于 printf() 的格式化输出。
// 常见的格式化输入符号包括:%d(整数),%f(浮点数),%c(字符),%s(字符串),%x(十六进制整数),%o(八进制整数),%p(指针地址)。
printf("请输入一个整数:\n");
int num = 0; // 定义一个整数变量 num 并初始化为 0
// 使用 scanf() 函数从标准输入中读取整数并存储到 num 中
scanf("%d", &num); // 使用 scanf 函数从标准输入中读取一个整数并存储到 num 中
printf("您输入的整数是:%d\n", num); // 输出用户输入的整数
printf("请输入一个字符类型变量:\n");
char c1; // 定义一个字符变量 c1
scanf("%c", &c1); // 使用 scanf 函数从标准输入中读取一个字符并存储到 c1 中
printf("您输入的字符是:%c\n", c1); // 输出用户输入的字符
// 使用 scanf() 函数从标准输入中读取多个变量的值
printf("请输入两个变量的值(一个整数和一个浮点数):\n");
int a = 0; // 定义一个整数变量 a 并初始化为 0
float b = 0.0; // 定义一个浮点数变量 b 并初始化为 0.0
scanf("%d %f", &a, &b); // 使用 scanf 函数从标准输入中读取一个整数和一个浮点数,并存储到变量 a 和 b 中
printf("您输入的整数是:%d,浮点数是:%f\n", a, b); // 输出用户输入的整数和浮点数
return 0;
}
总结:
空格在
scanf
中的行为:匹配输入中的任意数量空白字符(包括零个或多个)。格式字符串中的普通字符:必须严格匹配输入流中的字符。
最佳实践:
谨慎使用格式字符串中的空格。
结合
fgets
和sscanf
处理输入,避免缓冲区残留问题。始终检查
scanf
的返回值,确保输入符合预期。
二. 全局变量/局部变量(函数的分类)
1.全局变量
(1)即作用于代码运行全过程的变量
(2)具体演示,在下方代码
cpp
#include <stdio.h>
// 全局变量..........所有函数都可以访问
// 全局变量如果未赋值,分配的默认值根据数据类型的不同而不同
// 全局变量耦合度高,容易引起错误,使用时要小心
/*
int类型的全局变量默认值为0
float类型的全局变量默认值为0.000000
char类型的全局变量默认值为'\0'
double类型的全局变量默认值为0.000
char类型的全局变量默认值为'\0'
short类型的全局变量默认值为0
long类型的全局变量默认值为0
*/
int globalVar = 10; // 全局变量,作用域为整个文件,与函数平级
void function()
{
// 局部变量
int localVar = 5; // 局部变量,作用域仅限于此函数内
printf(" 函数内 局部变量 localVar 的值: %d\n", localVar);
printf(" 函数内 全局变量 globalVar 的值: %d\n", globalVar);
}
int main()
{
printf("全局变量 globalVar 的值: %d\n", globalVar); // 可以访问全局变量
function(); // 调用上方的函数
// printf("局部变量 localVar 的值: %d\n", localVar); // 这行代码会报错,因为 localVar 作用域仅限于 function 函数
return 0;
}
2.局部变量
(1)即作用于部分代码块的变量
(2)具体演示,在下方代码
cpp
#include <stdio.h>
// 局部变量示例:演示局部变量的初始化和使用
// 局部变量未初始化时,其默认值是未定义的,因为普通局部变量存储在栈空间中,包含之前的随机数据。
int main()
{
// 未初始化的局部变量-----------注意其默认值是未定义的
int localVar; // 局部变量声明但未初始化
printf("未初始化的局部变量 localVar 的值: %d\n", localVar);
// 初始化局部变量-----------默认值为0
localVar = 0;
localVar = 0; // 初始化局部变量,将其值设置为0
printf("初始化后的局部变量 localVar 的值: %d\n", localVar);
// 在同一个函数中,局部变量可以重复使用
// int localVar = 5; // 如果这样写会报错,因为 localVar 已经在上面声明过,并且已经使用 int 在栈中分配了空间
// printf("在同一个函数中,局部变量可以重复使用,但需要注意不能重新声明同名变量。");
// 在同一个函数中,可以使用同名的变量,但需要使用不同的作用域
localVar = 5; // 修改局部变量的值为5
printf("修改后的局部变量 localVar 的值: %d\n", localVar);
return 0;
}
三.C语言内存模型(堆栈内存and so on )
内存分为五个模块,我们常用的int在内存开辟空间,其实就是,在内存呢的栈区开辟了空间byte为4.

3.1 栈区(stack)(未完待续.....)
先进后出(filo模式),即电梯,弹夹
表现形式为倒扣水杯,口朝下(上方闭口为栈底,开口处为栈顶,先进为高地址,反之为底)
未初始化的变量,会覆盖掉之前的栈帧(那么意思就是栈帧是不会立刻消失,在内存中仍有痕迹)


3.2 堆区
3.3 全局静态区
未完待续......
1. 未初始化
未完待续......
2. 已初始化
未完待续......
3.4 常量区
未完待续......
3.5 代码区
未完待续......
四.基本数据类型的转换
自动类型转换:当同意表达式中有不同类型的变量时,编译器会自动将低精度类型转换为高精度类型,然后进行运算。
强制类型转换:使用强制类型转换运算符将一种数据类型转换为另一种数据类型。
数据类型的转换不会影响数据本身的值,只是改变了数据在内存中的存储方式。
4.1 自动转换
cpp
int a = 5;
float b = 2.5;
double c = 3.5;
// 自动类型转换
double result = a + b + c; // int -> float -> double,
printf("自动类型转换结果: %f\n", result); // 输出结果为 11.000000,%f表示浮点数格式输出,默认保留6位小数
4.2 强制转换
cpp
/*
强制类型转换
int -> float -> double
*/
int d = 5;
float e = 2.5;
double f = 3.5;
// 强制类型转换
double result2 = (double)d + (double)e + (double)f; // int -> float -> double
printf("强制类型转换结果: %f\n", result2); // 输出结果为 11.000000,%f表示浮点数格式输出,默认保留6位小数
// 再转为int类型
int result3 = (int)result2; // double -> int
printf("强制类型转换结果: %d\n", result3); // 输出结果为 11
4.3 数据类型的精度
高精度类型:double、float、long double
低精度类型:int、char、short、long
高转低精度类型:基本不会失去精度,数据不会丢失。
低转高精度类型:会失去精度,可能会导致数据丢失。
五. 运算符
去
1. 算数运算符
用于基本数学运算。
运算符 描述 示例 注意 +
加法 a + b
-
减法 a - b
*
乘法 a * b
/
除法 a / b
整数除法会丢弃小数部分 %
取模(余数) a % b
只能用于整数,结果符号与被除数相同 ++
自增 a++
或++a
a = ( a + 1 ) --
自减 a--
或--a
同上
cpp
#include <stdio.h>
// 实型是指小数的类型,整型是指整数的类型,字符型是指字符的类型
float f = 10.0; // 浮点型变量,表示小数类型
// 整形是指整数的类型,实型是指小数的类型,字符型是指字符的类型
int i = 10; // 整型变量,表示整数类型
int main(int argc, char const *argv[])
{
// 算数运算符
// 正数负数
int a = 10;
int b = -10;
printf("a = %d, b = %d\n", a, b);
// 基本算数运算符
int c = 20;
int d = 10;
float f1 = 5.5;
float f2 = 10.5;
printf("c + d = %d\n", c + d); // 加法
printf("c - d = %d\n", c - d); // 减法
printf("c * d = %d\n", c * d); // 乘法
printf("c / d = %d\n", c / d); // 除法,除数不能为0
printf("c %% d = %d\n", c % d); // 取余,此处需要用%%来转义%,因为%是格式化输出的标志,取小数点后1位的值,不能对0取余,除数不能为0
// 1. 先乘除后加减
printf("c + d * 2 = %d\n", c + d * 2);
// 2. 先乘除后加减,括号内的优先级最高
printf("c + d * 2 = %d\n", (c + d) * 2); // 先加减后乘除,括号内的优先级最高
// 3. 两个整数相除,结果是整数,取整
printf("c / d = %d\n", c / d); // 结果是整数,取整
// 4. 两个浮点数相除,结果是浮点数,保留小数点后6位
printf("f1 / f2 = %f\n", f1 / f2); // 结果是浮点数,保留小数点后6位
// 5. 整数和浮点数相除,结果是浮点数,保留小数点后6位
printf("c / f1 = %f\n", c / f1); // 结果是浮点数,保留小数点后6位
// 6. 取余运算符%只能用于整数之间的运算,不能用于浮点数之间的运算
// printf("f1 %% f2 = %f\n", f1 % f2); // 错误,取余运算符%只能用于整数之间的运算,不能用于浮点数之间的运算
// 7. 对负数取余,正整数的余数是正数,负整数的余数是负整数
printf("b %% d = %d\n", b % d); // 结果是负整数,取余运算符%只能用于整数之间的运算,不能用于浮点数之间的运算
return 0;
}
2. 比较运算符
比较运算符:==、!=、>、<、>=、<=
==:相等运算符,判断两个值是否相等(与赋值运算符=不同)
!=:不相等运算符,判断两个值是否不相等
>: 大于运算符,判断左边的值是否大于右边的值
<: 小于运算符,判断左边的值是否小于右边的值
>=:大于等于运算符,判断左边的值是否大于或等于右边的值
<=:小于等于运算符,判断左边的值是否小于或等于右边的值
注意:比较运算符的结果是布尔值(0或1),0表示假,1表示真。
用于比较两个值,返回
1
(真)或0
(假)。
运算符 描述 示例 注意 ==
等于 a == b
不要与 =
混淆!=
不等于 a != b
>
大于 a > b
<
小于 a < b
>=
大于等于 a >= b
<=
小于等于 a <= b
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
/*
比较运算符:==、!=、>、<、>=、<=
==:相等运算符,判断两个值是否相等(与赋值运算符=不同)
!=:不相等运算符,判断两个值是否不相等
>: 大于运算符,判断左边的值是否大于右边的值
<: 小于运算符,判断左边的值是否小于右边的值
>=:大于等于运算符,判断左边的值是否大于或等于右边的值
<=:小于等于运算符,判断左边的值是否小于或等于右边的值
注意:比较运算符的结果是布尔值(0或1),0表示假,1表示真。
*/
int a = 10, b = 20;
printf("a == b: %d\n", a == b); // 相等(==)运算符 返回值0,表示假
printf("a != b: %d\n", a != b); // 不相等(!=)运算符 返回值1,表示真
printf("a > b: %d\n", a > b); // 大于(>)运算符 返回值0,表示假
printf("a < b: %d\n", a < b); // 小于(<)运算符 返回值1,表示真
printf("a >= b: %d\n", a >= b); // 大于等于(>=)运算符 返回值0,表示假
printf("a <= b: %d\n", a <= b); // 小于等于(<=)运算符 返回值1,表示真.
/*
比较运算符自身的优先级低于算术运算符和位运算符,但高于逻辑运算符。
例如:在表达式 a + b > c && d < e 中,首先计算 a + b 和 d < e,然后再进行逻辑与运算。
*/
int c = 30, d = 40, e = 50;
printf("a + b > c && d < e: %d\n", (a + b > c) && (d < e)); // 返回值1,表示真
/*
算数运算符的优先级高于比较运算符,但低于逻辑运算符。
例如:在表达式 a + b > c && d < e 中,首先计算 a + b 和 d < e,然后再进行逻辑与运算。
*/
printf("a + b > c && d < e: %d\n", (a + b > c) && (d < e)); // 返回值1,表示真
return 0;
}
3. 赋值运算符
赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
=: 赋值运算符,将右边的值赋给左边的变量
+=: 加法赋值运算符,将右边的值加到左边的变量上,并将结果赋给左边的变量
-=: 减法赋值运算符,将右边的值减去左边的变量,并将结果赋给左边的变量
*=: 乘法赋值运算符,将右边的值乘以左边的变量,并将结果赋给左边的变量
/=: 除法赋值运算符,将左边的变量除以右边的值,并将结果赋给左边的变量
%=: 取余赋值运算符,将左边的变量除以右边的值,并将余数赋给左边的变量
&=: 按位与赋值运算符,将左边的变量和右边的值进行按位与运算,并将结果赋给左边的变量
|=: 按位或赋值运算符,将左边的变量和右边的值进行按位或运算,并将结果赋给左边的变量
^=: 按位异或赋值运算符,将左边的变量和右边的值进行按位异或运算,并将结果赋给左边的变量
<<=:左移赋值运算符,将左边的变量左移右边的位数,并将结果赋给左边的变量
>>=:右移赋值运算符,将左边的变量右移右边的位数,并将结果赋给左边的变量
注意:赋值运算符,会修改变量的原始值
用于给变量赋值。
运算符 描述 示例 等价于 =
简单赋值 a = 5
+=
加后赋值 a += 3
a = a + 3
-=
减后赋值 a -= 2
a = a - 2
*=
乘后赋值 a *= 4
a = a * 4
/=
除后赋值 a /= 2
a = a / 2
%=
取模后赋值 a %= 3
a = a % 3
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
/*
赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
=: 赋值运算符,将右边的值赋给左边的变量
+=: 加法赋值运算符,将右边的值加到左边的变量上,并将结果赋给左边的变量
-=: 减法赋值运算符,将右边的值减去左边的变量,并将结果赋给左边的变量
*=: 乘法赋值运算符,将右边的值乘以左边的变量,并将结果赋给左边的变量
/=: 除法赋值运算符,将左边的变量除以右边的值,并将结果赋给左边的变量
%=: 取余赋值运算符,将左边的变量除以右边的值,并将余数赋给左边的变量
&=: 按位与赋值运算符,将左边的变量和右边的值进行按位与运算,并将结果赋给左边的变量
|=: 按位或赋值运算符,将左边的变量和右边的值进行按位或运算,并将结果赋给左边的变量
^=: 按位异或赋值运算符,将左边的变量和右边的值进行按位异或运算,并将结果赋给左边的变量
<<=:左移赋值运算符,将左边的变量左移右边的位数,并将结果赋给左边的变量
>>=:右移赋值运算符,将左边的变量右移右边的位数,并将结果赋给左边的变量
注意:赋值运算符,会修改变量的原始值
*/
// 赋值运算符,将右边的值赋给左边的变量
// 复合赋值运算符,将右边的值加到左边的变量上,并将结果赋给左边的变量
double a = 888888.88;
a += 11.11;
printf("a += 11.11: %f\n", a); // 888899.99
// 等价于i= i%(j-2),=后面是一个整体
int i = 10, j = 20;
i += i % (j - 2); // i = i + i % (j - 2)
printf("i += i %% (j - 2): %d\n", i); // 10 + 10 % (20 - 2) = 10 + 10 % 18 = 10 + 10 = 20
// 比较运算符的优先级低于算术运算符和位运算符,但高于逻辑运算符。
printf("i + j > i && i < j: %d\n", (i + j > i) && (i < j)); // 意思是 i + j > i 和 i < j 同时成立才返回真,i + j > i 的值为真,i < j 的值为假,所以结果为假
// 先乘除后加减,括号内的优先级最高
return 0;
}
4. 自增运算符
自增运算符:++,自减运算符:--
++: 自增运算符,将变量的值加1,并将结果赋给变量本身
--: 自减运算符,将变量的值减1,并将结果赋给变量本身
注意:自增和自减运算符的优先级高于赋值运算符,但低于算术运算符和位运算符。
注意:不能用于常量和表达式,只能用于变量。
注意:自增和自减运算符可以放在变量的前面或后面,前缀和后缀的区别在于自增和自减的顺序。
运算符 描述 示例 区别 a++
后置自增 int b = a++;
先使用 a 的值,再自增 ++a
前置自增 int b = ++a;
先自增 a,再使用新值 a--
后置自减 int b = a--;
同上逻辑 --a
前置自减 int b = --a;
同上逻辑
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
/*
自增运算符:++,自减运算符:--
++: 自增运算符,将变量的值加1,并将结果赋给变量本身
--: 自减运算符,将变量的值减1,并将结果赋给变量本身
注意:自增和自减运算符的优先级高于赋值运算符,但低于算术运算符和位运算符。
注意:不能用于常量和表达式,只能用于变量。
注意:自增和自减运算符可以放在变量的前面或后面,前缀和后缀的区别在于自增和自减的顺序。
*/
// 自增运算符,将变量的值加1,并将结果赋给变量本身 (a++)
int a = 10;
int b = 20;
a++; // a = a + 1; // 先赋值后自增
++b; // b = b + 1; // 先自增后赋值
--b
printf("a++: %d\n", a); // 11
printf("--b: %d\n", b);
printf("++b: %d\n", b); // 21
// 自减运算符,将变量的值减1,并将结果赋给变量本身 (a--)
int c = 10;
int d = 20;
c--; // c = c - 1; // 先赋值后自减
--d; // d = d - 1; // 先自减后赋值
printf("c--: %d\n", c); // 9
printf("--d: %d\n", d); // 19
// 自增和自减运算符高于算数运算符和位运算符,但低于赋值运算符。
int e = 3 * ++b; // 先处理++b,b = 21 + 1 = 22,然后再处理3*22 = 66
printf("e = 3*++b: %d\n", e); // 66
return 0;
}
5. 逻辑运算符
逻辑运算符:&&、||、!
逻辑与:&&,当且仅当两个操作数都为真时,结果才为真。
逻辑或:||,当至少有一个操作数为真时,结果为真
逻辑非:!,对操作数取反,真变假,假变真。
逻辑运算符的优先级:! > && > ||
用于组合或反转布尔表达式。
运算符 描述 示例 规则(短路求值) &&
逻辑与 a && b
全为真则真,否则假 ` ` 逻辑或 !
逻辑非 !a
反转布尔值
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
/*
逻辑运算符:&&、||、!
逻辑与:&&,当且仅当两个操作数都为真时,结果才为真。
逻辑或:||,当至少有一个操作数为真时,结果为真
逻辑非:!,对操作数取反,真变假,假变真。
逻辑运算符的优先级:! > && > ||
*/
// 逻辑与运算符:&&
//短路机制:如果第一个操作数为假,则不计算第二个操作数
int a = 1, b = 2, c = 3;
// 逻辑与运算符的优先级高于赋值运算符,所以先计算逻辑与运算符
printf(a > 0 && b > 0 && c > 0 ? "a、b、c都是正数\n" : "a、b、c不是正数\n");
// 逻辑或运算符:||
//短路机制:如果第一个操作数为真,则不计算第二个操作数
printf(a > 0 || b > 0 || c > 0 ? "a、b、c至少有一个是正数\n" : "a、b、c都不是正数\n");
// 逻辑非运算符:!
printf(a != 0 ? "a不是0\n" : "a是0\n"); // a不是0
// 逻辑运算符的优先级:! > && > ||
// 逻辑与运算符的优先级高于逻辑或运算符,所以先计算逻辑与运算符
if (a > 0 && b > 0 || c > 0)
{
printf("a、b、c至少有一个是正数\n");
}
else
{
printf("a、b、c都不是正数\n");
}
return 0;
}
6. 条件运算符(三目运算符)
唯一的三元运算符,用于简化条件判断。
语法 描述 条件 ? 表达式1 : 表达式2
若条件为真,返回表达式1,否则返回表达式2
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
/*
条件运算符:?:,也称为三目运算符,语法格式为:条件表达式 ? 表达式1 : 表达式2
条件表达式为真时,返回表达式1的值;条件表达式为假时,返回表达式2的值。
attention:
条件运算符的优先级低于赋值运算符,所以在使用条件运算符时,要注意括号的使用。
条件运算符的优先级:?: > = > + - > * / %
三目运算符返回值的类型由表达式1和表达式2决定,如果表达式1和表达式2的类型不同,则返回值的类型为更高的类型。
条件运算符的返回值可以作为赋值语句的右值,也可以作为函数的参数。
*/
// 条件运算符
int a = 1, b = 2, c = 3;
(a > b) ? printf("a大于b\n") : printf("a小于等于b\n");
// 条件运算符的优先级低于赋值运算符,所以要加括号
c = (a > b) ? a : b; // 如果a大于b,则b等于a,否则b等于b
printf("b = %d\n", b);
return 0;
}
7. 逗号运算符
用于分隔多个表达式,返回最后一个表达式的值。
语法 描述 表达式1, 表达式2
依次执行表达式1和表达式2,返回表达式2的值
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
/*
逗号运算符:,,逗号运算符的优先级最低,逗号运算符的左操作数和右操作数都要计算,返回右操作数的值。
*/
int a = 3;
int b = (a++, ++a, a + 5); // 逗号运算符的优先级最低,逗号运算符的左操作数和右操作数都要计算,返回右操作数的值。
printf("a = %d\n", a); // a = 5
printf("b = %d\n", b); // b = 10
int a = 3;
int b = ((a + a), a + 5);
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
8. 运算符优先级(未完待续.....)
不必理会
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
/*
逗号<赋值<逻辑(不包含非)<比较<算数
*/
// 案例一
int x = 0, y = 1;
int res = x++ != !y;
return 0;
}
9. 位运算符
按位取反:
其余的暂时不用进行描述,简单易懂,在这里只对按位取反进行描述。
1. 补码的核心定义
在计算机中,所有整数均以补码形式存储,补码规则如下:
正数:补码 = 原码(二进制直接表示,符号位为0)。
0111 1111 1111 1111 1111 1111 1111 1111
负数:补码 = 原码取反(反码) + 1(符号位为1)。
1000 0000 0000 0000 0000 0000 0000 0000
2. 按位取反 (~) 的本质
操作对象:直接对整数的补码逐位取反(包括符号位)。
结果类型:结果仍为补码,需转换回原码才能得到十进制值。
3. 按位取反步骤(公式为~ a = -(a + 1))
取补码 (正数的补码为原码)
对补码进行取反(0为1,1为0)
取反过的补码转原码(负数的转换规则)
补码 → 原码(符号位为1,说明是负数)
符号位不变,其他位取反 → 得到原码
算出结果。
cpp
#include <stdio.h>
int main(int argc, char const *argv[])
{
/*
位运算
原码:符号位+绝对值
0:0000 0000 0000 0000 0000 0000 0000 0000
1:0000 0000 0000 0000 0000 0000 0000 0001
-1:1000 0000 0000 0000 0000 0000 0000 0001
-2:1000 0000 0000 0000 0000 0000 0000 0010
4:0000 0000 0000 0000 0000 0000 0000 0100
反码:符号位+绝对值的反码
3:0000 0000 0000 0000 0000 0000 0000 0011
-3:1111 1111 1111 1111 1111 1111 1111 1100
补码:符号位+绝对值的补码
3:0000 0000 0000 0000 0000 0000 0000 0011
-3:1000 0000 0000 0000 0000 0000 0000 0011
位运算是c语言中一种直接对二进制位进行操作的运算符,位运算符有:
按位与:&,按位或:|,按位异或:^,按位取反:~,左移:<<,右移:>>。
*/
// 1. 按位与:& (二进制对应位都为1时候,新结果二进制对应位才为1,否则为0)
int a = 3; // 0011
int b = 5; // 0101
int c = a & b; // 0001
printf("a & b = %d\n", c); // 1
/*
示例
*/
int a1 = 4; // 0100
int b1 = 5; // 0101
int c1 = a1 & b1; // 0100
printf("a1 & b1 = %d\n", c1); // 4
// 0100 & 0101 = 0100
// 2. 按位或:| (两个都为0的时候才为0,否则为1)
c = a | b; // 0111
printf("a | b = %d\n", c); // 7
/*
示例
int a1 = 4; 0100
int b1 = 5; 0101
*/
c1 = a1 | b1; // 0101
printf("a1 | b1 = %d\n", c1); // 5
// 3. 按位异或:^ (两个相同为0,不同为1)
c = a ^ b; // 0110
printf("a ^ b = %d\n", c); // 6
/*
示例
int a1 = 4; 0100
int b1 = 5; 0101
*/
c1 = a1 ^ b1; // 0001
printf("a1 ^ b1 = %d\n", c1); // 1
// 4. 按位取反:~
//公式为~a = -(a + 1 );
c = ~a; // 1100 ,将1100转为十进制,即1100-1=1100-1=-4
printf("~a = %d\n", c); // -4,注意:按位取反是对补码进行操作的,所以结果是负数
/*
示例
int a1 = 4; 0100
利用公式可得~a为-5
*/
c1 = ~a1; // 1011 = -5
printf("~a1 = %d\n", c1); // -5,注意:按位取反是对补码进行操作的,所以结果是负数
// 5. 左移:<< (左移n位,相当于乘以2^n)
c = a << 1; // 原本为0011,左移之后为0110
printf("a << 1 = %d\n", c); // 6
// 6. 右移:>> (右移n位,相当于除以2^n)
c = a >> 1; // 原本为0011,0001
printf("a >> 1 = %d\n", c); // 1
// 7.位运算场景,例如:交换两个数的值 (两个相同为0,不同为1)
int x = 3, y = 5;
printf("x = %d, y = %d\n", x, y); // x = 3, y = 5,交换前
x = x ^ y; // x = 3 ^ 5 = 6,5 = 0101,3 = 0011,结果为0110
y = x ^ y; // y = 6 ^ 5 = 3,6 = 0110,5 = 0101,结果为0011
x = x ^ y; // x = 6 ^ 3 = 5,6 = 0110,3 = 0011,结果为0101
printf("x = %d, y = %d\n", x, y); // x = 5, y = 3,交换成功
return 0;
}