6 C语言 *** 作符

6 C语言 *** 作符,第1张

6 C语言 *** 作符 *** 作符和表达式 *** 作符

分类:

  • 算数 *** 作符
  • 移位 *** 作符
  • 位 *** 作符
  • 赋值 *** 作符
  • 单目 *** 作符
  • 关系 *** 作符
  • 逻辑 *** 作符
  • 条件 *** 作符
  • 逗号表达式
  • 下表引用、函数调用的结构成员
算数 *** 作符
+	-	*	/	%
  1. 除了%都可以用于整数和浮点数之间的运算
  2. 除法 *** 作符"/":如果两个 *** 作数都是整数,执行整数除法。只要其中有浮点数执行运算那整个表达式就是浮点数的除法。
  3. %的两个 *** 作数必须是整数。
移位 *** 作符
<< 左移位 			>> 右移位	

左移位 左边抛弃右边补0

右移位

  1. 算术右移

右边丢弃,左边补符号位(*)

  1. 逻辑右移

右边丢弃,左边补0

警告:对于移位运算符,不要移动负数位,这个标准没有定义。

int num = 10;
num>>-1;//错误的
位 *** 作符

位 *** 作符:

&		按位与			 有0就为0
|		按位或			 有一就为1
^		按位异或		相同为0相异为1

*** 作数必须是整数。

小练习

int num1 = 1;
int num2 = 2;
printf("%d  ",num1&num2);
printf("%d  ",num1|num2);
printf("%d  ",num1^num2);

题:不创建临时变量实现两数的交换

方法一 思路 (加减法可能会溢出)

代码实现

int a = 2;
int b = 3;
a = a+b;
b = a-b;
a = a-b;

方法二 (思路) 一个数对同一个数两次异或回到本身。而且异或运算有交换律

代码实现

int a = 2;
int b = 3;
a = a^b;
b = a^b;
a = a^b;
printf("%d  ",a);
printf("%d  ",b);

练习:

一个整数存储在内存中的二进制中的1的个数。

思路:1. 短除法思想。(只能计算正数)。

  1. 按位与1进行计算可以得到最后一位是什么,再加上右移 *** 作。

代码实现:

int main(){
    int number = 0;
    int count = 0;
    scanf("%d",&number);
    //不能计算负数
    // while(number){
    //     if (number%2 == 1){
    //         count++;
    //     }
    //     number = number /2;
    // }
    for(i = 0;i<32;i++){		//32是因为int占32位
        if(1==((num>>i)&1))
            count++;
    }
    printf("%d",count);
    return 0;
}
赋值 *** 作符(=)

初始化赋值、重新赋值、连续赋值。

复合赋值 *** 作符
+=    -=    *=    /=    %=    >>=    <<=    &=    |=    ^=	
单目 *** 作符 *** 作符作用!逻辑取反-负+正&取地址sizeof *** 作数的类型长度(字节位单位)~按位取反–前置、后置自减++前置、后置自加*间接访问 *** 作符(解引用 *** 作符)(类型)强制类型转化

正、负是单目运算符,加、减是双目运算符

1是真但是真不一定是1。

计算数组长度 = sizeof(arr)/sizeof(arr[0])

坑题:

int main(){
    short s = 0;
    int a = 10;
    printf("%dn",sizeof(s = a + 5));
    printf("%dn",s);
}

解析:sizeof(s = a+5)的大小最后取决于s的类型。

​ s==5是因为sizeof运算符中的表达式仅仅是表达一下,并不会真正参与运算。

sizeof和数组
void test1(int arr[]){
    printf("%d  ",sizeof(arr));//(3)
}
void test2(char ch[]){
    printf("%dn",sizeof(ch));//(4)
}
int main(){
    int arr[10] = {0};
    char ch[10] = {0};
    printf("%d  ",sizeof(arr));//(1)
    printf("%dn",sizeof(ch));//(2)
    test1(arr);
    test2(ch);
    return 0;
}

解析:

(1)处 = sizeof(int)*10 = 40

(2)处 = sizeof(char)*10 = 10

(3)处 = sizeof(arr第一个元素的指针) = 8或者4 (64位电脑和32位电脑)

(4)处 = sizeof(ch第一个元素的指针) = 8或者4 (64位电脑和32位电脑)

关系 *** 作符 关系 *** 作符
>    >=    <    <=    !=    ==
逻辑 *** 作符
&&    ||

区分逻辑与和按位与 && 和&

区分逻辑或和按位或 ||和 |

逻辑是两个,按位是一个。

笔试题

程序运行结果是?

int main(){
    int i = 0,a=0,b=2,c=3,d=4;
    i = a++ && ++b &&d++;
    //i = a++||++b||d++;
    printf("a=%d  b=%d  c=%d  d=%d",a,b,c,d);
    return 0;
}

运行的结果是:a=1 b=2 c=3 d=4

解析:逻辑与左边为假右边就不计算了。在a++是先使用后运算。相当于i式第一部分就为假了。

程序运行结果是?

int main(){
    int i = 0,a=0,b=2,c=3,d=4;
    i = a++||++b||d++;
    printf("a=%d  b=%d  c=%d  d=%d",a,b,c,d);
    return 0;
}

运行结果:a=1 b=3 c=3 d=4

解析:逻辑或左边为真。右边就不计算了。a++为假运行,++b为真运行。d++就不运行了。

条件 *** 作符(三目 *** 作符)
表达式1?表达式2:表达式3
逗号表达式
表达式1,表达式2,表达式3,表达式4……,表达式n

从左向右以此计算。整个表达式的结果是最后一个表达式的结果。

int a = 1;
int b = 2;
int c = (a>b,a = b+10,a,b=a+1);
printf("%d",c);//结果为13
下表引用、函数调用和结构成员

1.[ ]下标引用 *** 作符

*** 作数:一个数组名+一个索引值

int arr[10];//创建数组
arr[9] = 10;//使用下标引用 *** 作符
[]的两个 *** 作数是arr和9.

2.( )函数调用 *** 作符,接受一个或者多个 *** 作数:第一个 *** 作数是函数名,剩余的 *** 作数就是传递给函数的参数。

3.访问一个结构的成员

. 结构体对象.成员

-> 结构体指针->成员名

//创建了一个结构体类型  Stu    C语言中叫成员就类似于面向对象中的属性
struct Stu{
    char name[10];
    int age;
    char id[20];
};

int main(){
    //使用结构体struct Stu这个类型创建了一个学生对象,并初始化
    struct Stu s1 = {"张三",20,"201808520"};
    printf("%sn",s1.name);
    printf("%dn",s1.age);
    printf("%sn",s1.id);
    struct Stu* ps = &s1;
    printf("%sn",ps->name);
    printf("%dn",ps->age);
    printf("%sn",ps->id);
    return 0;
}

表达式求值

表达式求值的顺序一部分是由 *** 作符的优先级和结和性决定。

同样,有些表达式的 *** 作数在求值的过程中可能需要转化成其他类型。

隐式类型转化

C的整型算术运算总是至少以缺省整型类型的精度来进行的。

为了获得这个精度,表达式中的字符和短整型 *** 作数在使用之前被转换为普通整型,这种转换称为整型提升。

​ 整型提升的意义:

	表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的 *** 作数的字节长度一般就是int的自己长度,同时也是CPU的通用寄存器的长度。
	因此,及时两个char类型相加,在CPU执行时实际上也要先转换为CPU内存整型 *** 作数的标准长度。
	通用CPU是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加的指令)。所以表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能进入CPU去执行运算。

实例:

问:输出的结果是什么?

char a = 3;
char b = 127;
char c = a + b;
printf("%dn",c);
//输出-126

解析

char a = 3;
//00000000000000000000000000000011
//截断  ->  00000011  ->  存给a
char b = 127;
//00000000000000000000000001111111
//截断  ->  01111111  ->  存给b
char c= a+b;
//a:00000011  整型提升  ->  (按符号位提升)
//00000000000000000000000000000011
//b:01111111  整型提升  ->  
//00000000000000000000000001111111
//相加
//00000000000000000000000010000010
//截断  ->  10000010  ->存给c
printf("%dn",c);
//a:10000010  整型提升  ->  (按符号位提升)
//11111111111111111111111110000010 -补码
//11111111111111111111111110000001 -反码
//10000000000000000000000001111110 -原码
//转化为十进制:-126

b和c的值被提升为普通整型,然后执行加法运算。加法运算完成之后,结果将被阶段,然后再存储于a中。

那么,什么是整型提升呢?

整型提升是按照变量的数据类型的符号位来提升的。无符号数直接在前面补0即可。

# 负数的整型提升
char a = -1;
整型提升   11111111  (补码)  补码-1-> 反码  正数不变 负数取反
# 正数的整形提升
char = 1;
整型提升   00000001(补码)   正数的三种码都一样

整型提升的例子

//案例1
int main(){
    char a= 0xb6;//10110110
    short b = 0xb600;
    int c = 0xb6000000;
    if(a==0xb6)
        printf("a");
    if(b==0xb600)
        printf("b");
    if(c==0xb6000000)
        printf("c");
    return 0;
}

说明整型提升的存在。

算数转换

如果某个 *** 作符的各个 *** 作数属于不同的类型,那么除非其中一个 *** 作数的转换为另一个 *** 作数的类型,否则 *** 作就无法进行。寻常算数转换

long double
double
float
unsigned long int
long int 
unsigned int 
int

如果某个 *** 作数的类型在上面这个列表中的排名较低,那么首先要转化为另一个 *** 作数的类型后执行运算。

注意:算术转化要合理进行,要不然会有一些潜在的问题。

float f = 3.14;
int num = f;//因式转换,会精度丢失。
*** 作符属性

复杂的表达式的求值有三个影响的因素。

  1. *** 作符的优先级
  2. *** 作符的结核性
  3. 是否控制求值顺序

两个相邻的 *** 作符先执哪个? 取决于优先级。如果相同的优先级,取决于他们的结合性。

*** 作符的优先级

注:图片引自 [https://blog.csdn.net/zhanghong056/article/details/76667298]

问题代码:

int main(){
    int i = 1;
    int ret = (++i) + (++i) +(++i);
    printf("%d",ret);
    printf("%d",i);
    return 0;
}
//初步判定计算顺序是三个数的加法 但是是先第一个++i存储下来再计算还是 先改变值再计算。这是又歧义的。

划重点:

写表达式时如果不能通过 *** 作符的属性确定唯一的计算路径,那个表达式就是存在问题的。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5692092.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存