算术操作符 + - * / %
// % - 取模
// & 取模操作符只能针对整型类型
int main()
{
int ret1 = 9 / 2;
printf("%d\n", ret1); // 4
// 对于 /(除号) 两边都是整数,执行的是整数除法
double ret2 = 9 / 2.0;
printf("%lf\n", ret2); // 4.500000
return 0;
}
移位操作符,移动的是二进制位
对于整数的二进制有3种表示形式:原码,反码,补码
- 正整数 - 原码,反码,补码相同
- 负整数:
原码 - 直接按照数字的正负写出的二进制序列
反码 - 原码的符号位不变,其它位按位取反得到的
补码 - 反码+1
整数在内存中存储的是二进制的补码
int main()
{
int a = 5; // 4byte = 32bit
int b = a << 1;
//00000000000000000000000000000101 - 正整数 原反补相同
//00000000000000000000000000000101
//00000000000000000000000000000101
int c = -1;
int d = c << 1;
//10000000000000000000000000000001 - -1的原码
//11111111111111111111111111111110 - 反码
//11111111111111111111111111111111 - 补码
return 0;
}
左边丢弃,右边补0
int main()
{
int a = 5;
int b = a << 1;
printf("%d\n", b); // 10
int c = -1;
int d = c << 1;
printf("%d\n", d); // -2 打印的是原码的值
//11111111111111111111111111111111 - -1补码
// (1)1111111111111111111111111111111(0) - 左移 左边丢弃右边补0
//1111111111111111111111111111111(0) - -1左移1的补码
//11111111111111111111111111111101 - 反
//10000000000000000000000000000010 - 原 -2
return 0;
}
右移运算分两种:
- 逻辑移位–右边丢弃,左边补0
- 算术移位–右边丢弃,左边补原符号位(VS2019采用算术右移)
int main()
{
int a = -1;
int b = a >> 1;
printf("%d\n", b);
//11111111111111111111111111111111 - -1补码
//()1111111111111111111111111111111(1)
// 如果是逻辑移位就补0
// 如果是算术移位就补1 结果是还是-1(当前是算术移位)
return 0;
}
int main()
{
int a = 15;
int b = a >> -1; // err - 标准为定义行为
return 0;
}
int main()
{
float c = 4.5f;
c >> 1; // err
}
他们的操作数必须是整数
& 按位与
| 按位或
^ 按位异或
int main()
{
int a = 3;
int b = -2;
int c = a & b;
printf("%d\n", c);
// 00000000000000000000000000000011 - 3
// 10000000000000000000000000000010 - -2的原码
// 11111111111111111111111111111101 - -2的反码
// 11111111111111111111111111111110 - -2的补码
// 00000000000000000000000000000011 - 3补码
// 11111111111111111111111111111110 - -2的补码
// 00000000000000000000000000000010 - a&b
// %d - 说明我们要打印c的值,以有符号的形式
// 00000000000000000000000000000010 - a&b
// 正数 原反补相同 2
return 0;
}
int main()
{
int a = 3;
int b = -2;
int c = a | b;
printf("%d\n", c);
// 00000000000000000000000000000011 - 3补码
// 11111111111111111111111111111110 - -2的补码
// 11111111111111111111111111111111 - a|b
// 以%d打印
// 算的是补码 最高位是负 要算原码
// 11111111111111111111111111111111 - a|b 补码
// 11111111111111111111111111111110 - 反
// 10000000000000000000000000000001 - 原 -1
return 0;
}
int main()
{
int a = 3;
int b = -2;
int c = a ^ b; // 二进制位异或
printf("%d\n", c);
// 00000000000000000000000000000011 - 3补码
// 11111111111111111111111111111110 - -2的补码
// 11111111111111111111111111111101 - a^b
// %d打印 负数计算原码
// 11111111111111111111111111111101 - a^b 补
// 11111111111111111111111111111100 - 反
// 10000000000000000000000000000011 - 原 -3
return 0;
}
int main()
{
int a = 15;
a & 1;
// 00000000000000000000000000001111 - 15
// 00000000000000000000000000000001 - 1
// 00000000000000000000000000000001 - a&1
return 0;
}
int main()
{
// 可以统计所有的内存中的补码二进制序列位有多少个1 循环32次
int a = 15;
a & 1;
a = a >> 1; // 让每一位都有机会来到最低位
return 0;
}
不能创建临时变量(第三个变量),实现两个数的交换。
int main()
{
int a = 3;
int b = 5;
int tmp = 0;
tmp = a;
a = b;
b = tmp;
printf("a=%d b=%d\n", a, b);
return 0;
}
存在溢出的问题
int main()
{
int a = 3;
int b = 5;
a = a + b;
b = a - b; // 和减b是a 此时b是a
a = a - b; // 和减b(此时是a)是a
printf("a=%d b=%d\n", a, b);
return 0;
}
int main()
{
int a = 3;
int b = 5;
a = a ^ b;
// 011
// 101 b
// 110 a^b a
b = a ^ b;
// 110 a
// 101
// 011 a^b b
a = a ^ b;
// 110
// 011 b
// 101 a
// a^b得出一个密码 密码与原来的a^得到原来的b 密码与原来的b^得到原来的a
printf("a=%d b=%d\n", a, b);
return 0;
}
用变量求最好 用异或代码的可读性不够好,且只适用于整形
a^a=0 对应的二进制位都相同,都为0
0^a=a 000 011–011
aab=b a^a=0 0^b=b
aba=b
异或是支持交换律的
int main()
{
int a = 10;
int x = 0;
int y = 20;
a = x = y + 1; // 连续赋值
// 清晰且便于调试
x = y + 1;
a = x;
return 0;
}
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
int main()
{
int a = 10;
a = a >> 1;
a >>= 1; // 符合赋值
a = a + 10;
a += 10;
return 0;
}
只有一个操作数的操作符
! 逻辑反操作
负值- +正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
前置,后置-- ++
间接访问操作符(解引用操作符)*
(类型) 强制类型转换
int main()
{
int a = 5;
int b = !a;
printf("%d\n", b); // 0
int c = 0;
int t = !c;
printf("%d\n", t); // 1
return 0;
}
int main()
{
int a = 10;
if (a) // a为真打印hehe
{
printf("hehe\n");
}
if (!a) // a为假打印haha
{
printf("haha\n");
}
return 0;
}
int main()
{
int a = 10;
a = -a;
printf("a=%d\n", a); // -10
a = -a;
printf("a=%d\n", a); // 10
return 0;
}
int main()
{
int a = 10;
int* p = &a;
*p = 20;
printf("%d\n", a);
return 0;
}
左值 - 指向的空间
右值 - 空间里的内容
int main()
{
int a = 10;
int* p = &a;
int b = *p; // 指向那块空间的值 赋给b
*p = 20; // 通过解引用找到a的空间 把空间改成20
printf("%d\n", a);
return 0;
}
int main()
{
int arr[10] = { 0 };
arr+1; // 取首元素的地址
&arr[0]+1; // 取首元素的地址
&arr+1; // 取出数组的地址
// 此三个地址一样,但意义不同
// 加1 前两个加4 后一个加40
return 0;
}
sizeof是操作符,不是函数
sizeof是计算变量或者类型创建变量的内存大小,单位是字节,和内存中存放什么数据没有关系
int main()
{
char arr[10] = "abc";
printf("%d\n", sizeof(arr)); // 10
printf("%d\n", strlen(arr)); // 3 字符串的长度,计算的是\0之前出现的字符个数
return 0;
}
int main()
{
int a = 10;
printf("%d\n", sizeof(a)); // 4
printf("%d\n", sizeof(int)); // 4
return 0;
}
sizeof内部的表达式是不参与运算
int main()
{
int a = 5;
short s = 10;
printf("%d\n", sizeof(s = a + 2));
printf("%d\n", s);
// sizeof内部的表达式是不参与运算的!
// 源文件sizeof变成可执行程序test.exe 会经过编译和链接 生成.exe后运行
// 最终取决于short的类型 大小是2 在链接是已经是2 在运行时没有机会执行
// 2 10
return 0;
}
int main()
{
int a = 0;
int b = ~a;
printf("%d\n", b);
// 00000000000000000000000000000000
// 11111111111111111111111111111111 - -1的二进制位补码是全1
// 打印%d 转原码
// 11111111111111111111111111111110 - 反
// 10000000000000000000000000000001 - 原 -1
return 0;
}
int main()
{
int a = 13;
// 00000000000000000000000000001101 - 正数原反补相同
a |= (1 << 1);
printf("%d\n", a);
// 1左移1 00000000000000000000000000000010
// 按位或等到a 00000000000000000000000000001111 - 15
// 00000000000000000000000000001111 - 15 此时的a
// 11111111111111111111111111111101 只要用这个&就可以改回来
// 这个二进制序列是由1<<1再按位取反得到
a &= (~(1 << 1));
printf("改回来%d\n", a);
return 0;
}
后置++,先使用,后++
前置++,先++,后使用
int main()
{
int a = 10;
int b = a++; // 后置++,先使用,后++
printf("%d\n", b); // 10
printf("%d\n", a); // 11
int c = 10;
int e = ++c; // 前置++,先++,后使用
printf("%d\n", e); // 11
printf("%d\n", c); // 11
return 0;
}
错误书写
//err
int main()
{
int a = 1;
int b = (++a) + (++a) + (++a); // err
printf("b=%d\n", b); // 12 在不同编译器上结果不同
return 0;
}
int main()
{
int a = (int)3.14; // 默认写出的是浮点数是double的
printf("%d\n", a);
return 0;
}
#include
void test1(int arr[])
{
printf("%d\n", sizeof(arr));//(2)
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));//(4)
}
int main()
{
int arr[10] = { 0 };
char ch[10] = { 0 };
printf("%d\n", sizeof(arr));//(1)
printf("%d\n", sizeof(ch));//(3)
test1(arr);
test2(ch);
return 0;
}
数组名是单独放在sizeof内部,数组名表示整个数组计算的是整个数组的大小
&数组名,数组名表示整个数组,取出的是整个数组的地址10个整形 40个字节。10个字符 10个字节
数组传参 首元素地址 本质上是void test1(int* arr) 求的是指针大小 4/8
40 4 10 4
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
- 用于同类型变量
- 注意==与=写错导致的错误
&& 逻辑与
|| 逻辑或
逻辑与判断的真用1表示 假用0表示
逻辑与两个都为真是1 逻辑或只要有一个为真就是1
int main()
{
int a = 0;
int b = 5;
int c = a && b; // 逻辑与
int e = a || b; // 逻辑或
printf("c=%d\n", c); // 0
printf("e=%d\n", e); // 1
return 0;
}
区分逻辑与与和按位与
区分逻辑或与和按位或1&2 ----0
1&&2---->11 | 2----->3
1 || 2---- > 1
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
//i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
// a++先使用后++ 所以0为假 后面不运算 a++是1 bcd都没有变 1 2 3 4
// 对于逻辑与来说,左边为假,右边就不用计算了
int main()
{
int i = 0, a = 1, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
// 改a=1,2 3 3 5
return 0;
}
int main()
{
int i = 0, a = 1, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
// a++是假 后面不用算了 只有a++了 2 2 3 4
return 0;
}
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\nd = %d\n", a, b, c, d);
// 1 3 3 4 ab自增
return 0;
}
exp1 ? exp2 : exp3
int main()
{
int a = 3;
int b = 5;
int m = 0;
m = (a > b ? a : b);
// 如果a>b 就把a赋给m 否则将b赋给m
// (a > b) ? (m = a) : (m = a);
}
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
int main()
{
//代码1
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
//代码2
if (a = b + 1, c = a / 2, d > 0) // 前面依次计算 最后一个是if的判断条件
//代码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)
{
//业务处理
}
}
输入一个正整数n(1 ≤ n ≤ 109)
输出描述:
输出一行,为正整数n表示为六进制的结果
int main()
{
int num = 0;
int arr[50] = { 0 };
//输入
scanf("%d", &num);
//转换
int i = 0;
while (num)
{
arr[i] = num % 6; // %6/6得到最低位 倒着打印出来
i++;
num = num / 6;
}
//输出
for (i--; i >= 0; i--) //循环停下来时i++ 指向下一个 先i-- 倒着打印 调整也是i--
{
printf("%d", arr[i]);
}
return 0;
}
n个人在等电梯。电梯每次可以乘坐12人,每次上下需要的时间为4分钟(上需要2分钟,下需要2分钟)。
输入包含一个整数n (0 ≤ n ≤ 109)
输出一个整数,即小乐乐到达楼上需要的时间。
int main()
{
int n = 0;
int t = 0;
//输入
scanf("%d", &n);
//计算
int t = n / 12 * 4 + 2;
//输出
printf("%d\n", t);
return 0;
}
int main()
{
long n = 0;
long m = 0;
//输入
scanf("%d %d", &n, &m);
//计算
//1. 计算最大公约数
long long max = (m > n) ? (n) : (m);
while (1)
{
if ((m % max == 0) && (n % max == 0))
{
break;
}
max--;
}
//2. 计算最小公倍数
long long min = (m > n) ? (m) : (n); // 最小公倍数从两数中较大开始
while (1)
{
if ((min % m == 0) && (min % n == 0))
{
break;
}
min++;
}
//3. 求和
long long sum = max + min;
//打印
printf("%lld", sum);
return 0;
}
int main()
{
long long n = 0;
long long m = 0;
scanf("%d %d", &n, &m);
long long n2 = n;
long long m2 = m;
long long c = 0;
while (c = m2 % n2)
{
m2 = n2;
n2 = c;
}
long long min = m * n / n2;
long long sum = min + n2;
printf("%lld", sum);
return 0;
}
从键盘任意输入一个字符,编程判断是否是字母(包括大小写)。
多组输入,每行输入包括一个字符。
输出该字符是字母(YES)或不是(NO)。
int main()
{
// 多组输入
int ch = 0;
while ((ch = getchar()) != EOF)
{
if (ch >= 'A' && ch <= 'Z' || (ch >= 'a' && ch <= 'z'))
{
printf("YES\n");
}
else
{
printf("NO\n");
}
getchar(); // 处理\n
}
return 0;
}
#include
int main()
{
int n = 0;
while (scanf("%d", &n) != EOF)
{
int i = 0;
for (i = 0; i < n; i++)
{
int j = 0;
for (j = 0; j < n; j++)
{
printf("* ");
}
printf("\n");
}
}
return 0;
}
int main()
{
int n = 0;
while (scanf("%d", &n) != EOF)
{
int i = 0;
for (i = 1; i <= n; i++) // i=0 i
{
// 打印一行
int j = 0;
for (j = 1; j <= n; j++)
{
// 如果判断条件是从0开始 这里的下标就是i==1和i==n-1
if (i == 1 || i == n || j == 1 || j == n)
{
printf("* ");
}
else
{
printf(" ");
}
}
printf("\n");
}
}
return 0;
}
输入7个整数(0-100)一行,输出去掉最高分和最低分的平均成绩,小数点后保留2位,每行输出后换行。
int main()
{
int i = 0;
int sc = 0;
int sum = 0;
int max = 0; // 假设0就是最高分
int min = 100; // 假设100就是最低分
for (i = 0; i < 7; i++)
{
scanf("%d", &sc);
sum += sc;
if (sc > max)
max = sc;
if (sc < min)
min = sc;
}
double avg = (sum - max - min) / 5.0;
printf("%.2lf\n", avg); // .2 保留两位小数点
return 0;
}
int main()
{
int a = 0;
int b = 0;
int c = 0;
//while (~scanf("%d %d %d", &a, &b, &c))
// EOF是-1 -1补码是全1 按位取法是全0 不等于EOF就不会为0
while (scanf("%d %d %d", &a, &b, &c) != EOF)
{
if ((a + b > c) && (a + c > b) && (b + c > a))
{
// 判断边
if (a == b && b == c)
{
printf("Equilateral triangle!\n");
}
else if ((a == b && a != c) || (a == c && a != b) || (b == c && b != a))
{
printf("Isosceles triangle!\n");
}
else
{
printf("Ordinary triangle!\n");
}
}
else
{
printf("Not a triangle!\n");
}
}
return 0;
}
int main()
{
int arr[] = { 1, 2, (3,4), 5 }; // 逗号表达式 1 2 4 5
printf("%d\n", sizeof(arr));
// 数组名表示整个数组 计算的是整个数组的大小 4个元素 每个元素是int
// 4*4 = 16
return 0;
}
sizeof计算的是内存占用空间的大小
strlen求字符串长度 \0之前的字符
void test(int arr[], int sz) // 传的是地址
{
int i = 0;
for (i = 0; i < sz; i++)
{
//printf("%d ", arr[i]); // arr[i] ---> *(arr+i)
printf("%d ", *(arr + i));
}
}
int main()
{
int arr[10] = { 1, 2, 3, 4 ,5, 6, 7, 8, 9, 10 };
int sz = sizeof(arr) / sizeof(arr[0]);
test(arr, sz); // 数组传参过去的是数组的首元素的地址
return 0;
}
void exchange(int a[], int b[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
int tmp = a[i];
a[i] = b[i];
b[i] = tmp;
}
}
int main()
{
int a[] = { 1, 3, 5, 7, 9 };
int b[] = { 2, 4, 6, 8, 10 };
int sz = sizeof(a) / sizeof(a[0]);
exchange(a, b, sz);
return 0;
}
#include
int main()
{
int arr1[10] = { 1, 2, 3, 4, 5 };
int arr2[10] = { 6, 7, 8, 9, 10 };
int i = 0;
for (i = 0; i < 10; i++)
{
int temp = 0;
temp = arr1[i];
arr1[i] = arr2[i];
arr2[i] = temp;
}
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
printf("\n");
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
创建一个整形数组,完成对数组的操作:
实现函数init() 初始化数组为全0
实现print() 打印数组的每个元素
实现reverse() 函数完成数组元素的逆置。
void Init(int* arr, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
*(arr + i) = 0;
}
}
void Print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
Reverse(int arr[], int sz)
{
int l = 0; // 左下标
int r = sz - 1; // 右下标
while (l < r)
{
int tmp = arr[l];
arr[l] = arr[r];
arr[r] = tmp;
l++;
r--;
}
}
int main()
{
int arr[10];
int sz = sizeof(arr) / sizeoff(arr[0]);
Init(arr, sz); //初始化为全0 0-9
Print(arr, sz); // 打印
Reverse(arr, sz); // 逆置
Print(arr, sz); // 逆置后再打印 9-0
return 0;
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%p ---- %p\n", &arr[i], arr+i);
// &arr[i] 和 arr+i 打印的地址完全一样
// arr+i - 就是数组arr中,下标为i的元素的地址
}
return 0;
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
int i = 0;
//arr[4]; 等价于*(arr+4)
// []是操作符 arr和4是两个操作数
// arr[4] --- *(arr+4)
// arr[4] ---> *(arr+4) ---> *(4+arr) ---> 4[arr]
printf("%d\n", arr[4]);
printf("%d\n", 4[arr]); // 可行 []仅仅是个操作符
return 0;
}
#include
int main()
{
// printf("%d", strlen("abc"));
// strlen返回的是size_t --- unsigned int 无符号整型
// %d打印的是有符号数 用%u
printf("%u\n", strlen("abc"));
return 0;
}
void test()
{
printf("hehe\n");
}
int main()
{
test();
// () - 函数调用操作符 接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数
return 0;
}
// 自定义类型
// struct Book 结构体类型
// struct Book b 结构体变量
struct book // 结构体关键字 - struct
{
char name[20];
float price;
char id[10];
};
void print1(struct book b)
{
printf("书名:%s\n", b.name);
printf("价格:%f\n", b.price);
printf("书名:%s\n", b.id);
}
int main()
{
struct book b = { "c语言程序设计", 55.5f, "c23479383" };
print1(b);
return 0;
}
库函数 头文件string.h
int main()
{
struct Book b = { "C语言程序设计", 55.5f, "C23479383" };
Print1(b);
b.price = 100.0f;// 可
//b.name = "数据结构"; // err
//name是数组 数组名是常量的地址 应该放在地址所指向的空间里
// 用==字符串拷贝 - strcpy== 库函数 头文件string.h
strcpy(b.name, "数据结构");
Print1(b);
return 0;
}
struct Book
{
char name[20];
float price;
char id[10];
};
void Print2(struct Book* pb) // 结构体指针接收
{
printf("书名:%s\n", (*pb).name);
printf("价格:%f\n", (*pb).price);
printf("书名:%s\n", (*pb).id);
}
int main()
{
struct Book b = { "C语言程序设计", 55.5f, "C23479383" };
Print2(&b); // 传地址
return 0;
}
// 左 -----------// 右
// 结构变量.成员名
// 结构体指针->成员名// (*结构体指针).成员名 —不常用
void Print2(struct Book* pb)
{
// . 结构体.成员名
//printf("书名:%s\n", (*pb).name);
//printf("价格:%f\n", (*pb).price);
//printf("书名:%s\n", (*pb).id);
// -> 结构体指针->成员名
printf("书名:%s\n", pb->name);
printf("价格:%f\n", pb->price);
printf("书名:%s\n", pb->id);
}
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
C的整型算术运算总是至少以缺省整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度
一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU(general - purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令
中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转
换为int或unsigned int,然后才能送入CPU去执行运算。
整形提升是按照变量的数据类型的符号位来提升的 - 高位补充符号位
负数的整形提升, 高位补充符号位补1
正数的整形提升, 高位补充符号位补0
无符号整形提升,高位补0
#include
int main()
{
//b和c的值被提升为普通整型,然后再执行加法运算。
//加法运算完成之后,结果将被截断,然后再存储于a中。
char a = 3; //a是1字节 - 8比特位
//00000000000000000000000000000011 - 3
//00000011 - a
char b = 127; //b是1byte - 8bit
//00000000000000000000000001111111
//01111111 - b
//a和b都是char类型,大小都是1byte,所以这里计算的时候都要进行整型提升
//高位补符号位
//00000000000000000000000000000011 a整型提升补0
//00000000000000000000000001111111 b
//00000000000000000000000010000010 a+b
char c = a + b;
//10000010 截断
//11111111111111111111111110000010 整型提升 负数补1
//打印原码
//11111111111111111111111110000001 反
//10000000000000000000000001111110 原 2^7-2=128-2
//-126
printf("%d\n", c);
return 0;
}
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");
//11010110 - c
//11111111111111111111111111010110 - 整型提升 负数补1
//转为原码与0xb6不一样 第二个判断表达式也一样
//c是int类型 不参与整型提升 判断条件一致 所以只打印c
/*表达式a == 0xb6, b == 0xb600 的结果是假,
但是c不发生整形提升, 则表达式 c==0xb6000000 的结果是真.*/
return 0;
}
int main()
{
char c = 1;
printf("%u\n", sizeof(c)); //1
printf("%u\n", sizeof(+c)); //4
//+-是操作符 表达式参与运算 char需要整型提升 变成整型
printf("%u\n", sizeof(-c)); //4
//c只要参与表达式运算, 就会发生整形提升, 表达式 + c, 就会发生提升, 所以 sizeof(+c) 是4个字节.
//表达式 - c 也会发生整形提升, 所以 sizeof(-c) 是4个字节, 但是 sizeof(c), 就是4个字节
return 0;
}
long double
double
float
unsigned long int
long int
unsigned int
int
向上转换 排名较低的,首先要转换为另外一个操作数的类型后执行运算。
int main()
{
int a = 4;
float f = 4.5f;
float r = a + f; // a算数转换成float
return 0;
}
两个相邻的操作符优先级不同的情况下,取决于优先级
两个相邻的操作符优先级相同时,取决于他们的结合性
// int c = a + b * 3 优先级 先算* 再算+
// int c = a + b + 3 优先级相同 看结合性 L-R 从左向右 先算a+b再算+3
// && 逻辑与(左边为假 右边就不用算了)
// || 逻辑或(左边为真,右边也不用算了)
// ?: 条件操作符(表达式1为真 2算3不算)
// , 逗号表达式(真正结果是最后一个表达式)
表达式1
//a* b + c * d + e * f
所以表达式的计算机顺序就可能是:
a* b
c* d
a* b + c * d
e * f
a * b + c * d + e * f
或者:
a* b
c* d
e* f
a* b + c * d
a * b + c * d + e * f
表达式2
//c + --c;
操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得知, + 操作符的左操作数的获取在右操作数之前还是之后求值
代码3 非法表达式
int main()
{
//在不同表达式结果都不同
int i = 10;
i = i-- - --i * (i = -3) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
代码4
int main()
{
int a = 1;
int d = 0;
d = (++a) + (++a) + (++a);
printf("%d\n", d);
// vs2019是12 gcc是10
//VS 三次加完4 4+4+4 GCC先算前两个是3 3+3+4
//调试后 右击转到反汇编
return 0;
}
代码5
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf("%d\n", answer);//输出多少?
return 0;
}
静态局部变量 出函数范围不销毁 VS -10
总结:
我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。