算数操作符
+, - , * ,/ ,%
"/"很特殊,对于 / (除号) 两边都是整数,执行的是整数除法 操作数中有浮点数才执行浮点数除法 %:取模 取模操作符只能针对整型类型
移位操作符
<< :左移操作符 如果a= -1(正整数原反补相同)
int a = -1;
int b = a << 1;
printf("%d\n", b);
">>"右移操作符
- 逻辑移位
右边丢弃,左边补0 - 算术移位(vs用的是算术右移)
右边丢弃,左边补原符号位
不管是左移还是右移,操作符的两边都必须是整型
位操作符
💥这些都是在 补码 下操作
& 按位与:有0则0,全1则1
int a = 3;
int b = -2;
int c = a & b;
printf("%d\n", c);
| 按位或 : 有1则1,全0则0
int a = 3;
int b = -2;
int c = a | b;
printf("%d\n", c);
^按位异或 :相同为0,相异为1
int a = 3;
int b = -2;
int c = a ^ b;
printf("%d\n", c);
面试题:用异或实现a,b的交换(只适用于整型)
int a = 3;
int b = 5;
a = a ^ b;
b = a ^ b;
a = a ^ b;
a^a=0 ; 0^a=a ,并且异或支持交换律
所以a,b用异或交换的代码可以理解为
赋值操作符
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;
复合操作符
单目操作符
单目操作符:只有一个操作数的操作符
1. !— 逻辑反操作符 0为假,非0为真 2. + – 正号,负号 正负号不必多说 3. & — 取地址 操作符; * — 解引用操作符
int arr[10] = { 0 };
arr;
&arr[0];
&arr[19];
&arr;
int a = 10;
int* p = &a;
int b = *p;
*p = 20;
等号左边的理解为左值,等号右边的理解为右值 左值 – 空间 右值 – 空间的内容
4.sizeof
1.sizeof是操作符,不是函数 2.sizeof是计算变量或者类型创建变量的内存大小,单位是字节,和内存中存放什么数据没有关系
char arr[10] = "abc";
printf("%d\n", sizeof(arr));
printf("%d\n", strlen(arr));
我们也要知道sizeof 内部的表达式是不参与运算的!
int a = 5;
short s = 10;
printf("%d\n", sizeof(s = a + 2));
printf("%d\n", s);
5.~ 按位取反
int a = 0;
int b = ~a;
printf("%d\n", b);
int a = 13;
a |= (1 << 1);
printf("%d\n", a);
a &= (~(1 << 1));
printf("%d\n", a);
}
6.前置++和后置++
int a = 10;
int b = a++;
int c = ++a;
7.(类型)强制类型转换 如果我们int a = 3.14;//直接写出的浮点数是double类型 这么写,编译器会有警告 所以我们可以在前面加(int),int a = (int)3.14;
sizeof和数组
#include <stdio.h>
void test1(int arr[])
{
printf("%d\n", sizeof(arr));
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));
}
int main()
{
int arr[10] = {0};
char ch[10] = {0};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(ch));
test1(arr);
test2(ch);
return 0;
}
问:
(1)、(2)两个地方分别输出多少?
(3)、(4)两个地方分别输出多少
因此(1),(2)输出40,4;(3)(4)输出10,4
逻辑操作符
💝 逻辑与 &&
int a = 3;
int b = 0;
int c = a && b;
💝 逻辑或 ||
int a = 3;
int b = 0;
int d = a || b;
360面试题
#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\nd = %d\n", a, b, c, d);
return 0;
}
所以程序结果是1,2,3,4
如果把a改成1 (真)结果又是什么?
所以结果就是2,3,3,5
如果把&& 改成|| 结果又是什么?
结果就为 2 2 3 4
条件操作符
exp1 ? exp2 : exp3 (三目操作符) 表达式1的结果为真,表达式2的结果就是整体的结果,否则整体的结果就是表达式3的结果
int a = 3;
int b = 5;
int m = (a > b ? a : b);
逗号表达式
exp1, exp2, exp3, …expN 逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果,前面表达式的改变可能会改变结果。
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);
c是多少?
执行a>b不会改变a,b;执行a=b+10,a就改变成12;a也不改变什么;b=a+1,b改变成13 所以c就是最后一个表达式的值13
下标引用,函数调用,结构体
1.[ ] 下标引用操作符 操作数:一个数组名 + 一个索引值
int arr[10];
arr[9] = 10;
2.()函数调用操作符 接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1();
test2("hello bit.");
return 0;
}
3.访问一个结构的成员
. 结构体.成员名 -> 结构体指针->成员名 (*结构体指针).成员名
struct Book
{
char name[20];
float price;
char id[10];
};
void Print(struct Book b)
{
printf("书名:%s 价格:%.2f 书号:%s", b.name, b.price, b.id);
printf("\n");
}
void Print1(struct Book* pb)
{
printf("书名:%s 价格:%.2f 书号:%s", pb->name, pb->price, pb->id);
printf("\n");
}
int main()
{
struct Book b = { "三体",55.4f,"c2022213" };
Print(b);
b.price = 100.1f;
strcpy(b.name, "数据结构");
Print1(&b);
return 0;
}
隐式类型转换
C的整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。 整型提升主要针对short和char类型
如何整型提升?
整型提升是按照变量的数据类型的符号位来提升的; 负数高位补1,正数高位补0;
下面代码进行了两次整型提升,a+b 一次,printf("%d",c) 一次
int main()
{
char a = 3;
char b = 127;
char c = a + b;
printf("%d", c);
return 0;
}
不要觉得char 和short 在存储时,就发生了整型提升!发生这个整型提升的前提是它们参与整型运算!!而且整型运算结束后,4个字节的数据将发生截断,再返回值。也就是说,运算完成后,CPU返回值的字节数仍为这个数据原本类型的字节数,而不是提升后的字节数。截断的规则是留低位弃高位 。
整型提升的例子:
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;
}
a,b要进行整形提升,但是c不需要整形提升
例2:
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}
算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
操作符的三个属性
1.操作符的优先级 2.操作符的结合性 3.是否控制求值顺序; 比如&& ,左边为假,右边的就不用算了 两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
问题表达式1:a*b + c*d + e*f (二义性)
注释:代码1在计算的时候,由于比+的优先级高,只能保证的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行 所以表达式1计算顺序可能是
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; ,可能会计算出不同结果
假设c=3,最左边的c不知道什么时候准备好
可能1:3+2
可能2:2+2
问题代码3:
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf( "%d\n", answer);
return 0;
}
我们只知道*优先级高于 -,但是我们不知道谁先调用fun(),没有唯一计算路径
|