详解操作符

算术操作符 + - * / %

/

3/2 结果是整数1(除号两端都是整数执行整数除法)

3.0/2 3/2.0 结果是小数(除号两端至少有一个是小数执行小数除法)

%

9%2 求的是余数(取模操作符两端必须是整型)有范围

移位操作符

操作的是二进制位

<< 左移操作符

>> 右移操作符

注意:不能移动负数位(num>>-1)

左移

int main()
{
    int a= 5;
    int b = a << 1;//左边丢弃,右边补0
    printf("%d\n", a);//5       00000000000000000000000000000101
    printf("%d\n", b);//10      00000000000000000000000000001010
    return 0;
}
int main()
{
    int a= -5;
    int b = a << 1;
    printf("%d\n", a);//-5       11111111111111111111111111111011
    printf("%d\n", b);//-10       11111111111111111111111111110110补码
                              // 11111111111111111111111111110101反码   
                              // 00000000000000000000000000001010原码    
    return 0;
}

计算的是补码,打印出来的是原码

原码 --> 符号位不变其他位按位取反得到反码 --> 反码+1得到补码

符号位不变其他位按位取反得到原码 <-- 补码-1得到反码 <-- 补码

右移

右移操作符(到底用哪个方法取决于编译器):

  1. 算术右移: 右边丢弃,左边补原符号位 (当前编译器在右移采用算术右移)
  2. 逻辑右移: 右边丢弃,左边补0

int mian()
{
    int a = 5;
    int b = a >> 1;
    printf("%d\n", a);//5           00000000000000000000000000000101
    printf("%d\n", b);//2           00000000000000000000000000000010
    return 0;
}

int mian()
{
    int a = -5;
    int b = a >> 1;
    printf("%d\n", a);//-5           
    printf("%d\n", b);//-3   
    return 0;        
}

整数的二进制表示形式有3种:

例: int a = 5

原码 00000000000000000000000000000101

反码 0000000000000000000000000000101

补码 00000000000000000000000000000101

int a = -5

原码: 10000000000000000000000000000101

反码:11111111111111111111111111111010(原码的符号位不变,其他位按取反(0和1反一反)得到的就是反码)(补码-1就是反码)

补码:11111111111111111111111111111011 (反码+1就是补码)

符号位:

0 整数

1 负数

正整数的原码、反码、补码是相同的

负整数的原码、反码、补码是要计算的

整数在内存中存储的是补码的二进制

位操作符:

& - 按(二进制)位与

| - 按(二进制)位或

^ - 按(二进制)位异或

a&b 双目操作符

&a 单目操作符

int main()
{
    int a = 3;//00000000000000000000000000000011补码
    int b = -5;//11111111111111111111111111111011补码
    int c = a & b;//00000000000000000000000000000011补码(两个同时为1时按位与才能是1)
    printf("%d\n", c);//3 正数原反补相同 
    return 0;
}

int main()
{
    int a = 3;
    int b = -5;
    int c = a | b;//(按位或只要有1就是1)
                  //11111111111111111111111111111011补码    
    printf("%d\n", c);//-5 (恰好是-5的补码和原码)
    return 0;
}

int main()
{
    int a = 3;
    int b = -5;
    int c = a ^ b;//(异或:对应的二进制位是相同为0,相异为1)
                  //11111111111111111111111111111000补码
                  //11111111111111111111111111110111反码(补码-1)
    printf("%d\n", c);//-8   10000000000000000000000000001000原码
    return 0;
}

a^a = 0

0^a = a

不能创建临时变量(第三个变量)实现两个变量的交换

int main()
{
	int a = 3;//011
	int b = 5;//101
	printf("a=%d b=%d\n", a, b);

	a = a ^ b;//110
	b = a ^ b;//011
	a = a ^ b;//101

	printf("a=%d b=%d\n", a, b);

	return 0;
}

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

int main()
{
	int num = 0;
	scanf("%d", &num);
	int i = 0;
	int count = 0;

	for (i = 0; i < 32; i++)
	{
		if ((num >> i) & 1)
		{
			count++;
		}

	}
	printf("%d\n", count);
	return 0;
}

赋值操作符

=

连续赋值:a = x = y+1;(可读性较差)

复合赋值符

a+= a-= ……

单目操作符

! 逻辑反操作

C语言中0表示假,非0表示真

C语言中C99之前没有表示真假的类型

C99中引用了布尔类型 false true

- 负值

+ 正值

& 取地址

* 间接访问操作符(解引用操作符)

struct S
{
    char name[20];
    int age;
};

int main()
{
    int a=10;
    int*pa=&a;
    *pa=20;//解引用操作符
    //*&a ==> a
    
    int arr[10]={0};
    &arr;//取出数组地址,数组的地址应该放到【数组指针】中去
    
    struct S s = {0};
    struct S* ps = &s;
    
    return 0;
}

sizeof 操作数的类型长度(以字节为单位

是操作符,不是函数

用来计算类型创建的变量所占内存的大小

int main()
{
    /*int a = 10;
    printf("%d\n", sizeof(a));//4
    printf("%d\n", sizeof(int));//4*/

    //int arr[10] = { 0 };
    //printf("%d\n", sizeof(arr));//40(前提是数组没有传参)

    int a = 10;
    short s = 0;
    printf("%d\n", sizeof(s = a + 2));//2
    printf("%d\n", a);//10
    //sizeof()中的表达式不参与计算
    //sizeof是在编译期间处理的
    printf("%d\n", s);//0

    return 0;
}

~ 对一个数的二进制按位取反

int main()
{
	/*int a = 0;
	printf("%d\n", ~a);*///-1
	//00000000000000000000000000000000
	//11111111111111111111111111111111 补码
	//11111111111111111111111111111110 反码
	//10000000000000000000000000000001 取反 -1


	int a = 11;
	//00000000000000000000000000001011
	//00000000000000000000000000000100
	a |= (1 << 2);
	printf("%d\n", a);//15  1111

	a &= (~(1 << 2));
	printf("%d\n", a);//11  1011
	return 0;
}

~ (-1) --> 0

为什么while(~scanf("%d",&n))可以终止循环?

scanf()读取失败的时候,返回EOF(-1)

原来写法: while(scanf("%d", &n) !=EOF)

-- 前置、后置--

++ 前置、后置++

++a 前置++ 先++,后使用

a++ 后置++ 先使用,后++

int a=10;
int b=a+1;
int b=++a;//前置++//a=a+1;b=a;
int b=a++;//后置++//int b=a;a=a+1;
#include <stdio.h>
int main()
{
	int a, b, c;
	a = 5;
	c = ++a;// ++a:加给a+1,结果为6,用加完之后的结果给c赋值,因此:a = 6  c = 6
	b = ++c, c++, ++a, a++;
	// 逗号表达式的优先级,最低,这里先算b=++c, b得到的是++c后的结果,b是7
	// b=++c 和后边的构成逗号表达式,依次从左向右计算的。
	// 表达式结束时,c++和,++a,a++会给a+2,给c加1,此时c:8,a:8,b:7
	b += a++ + c; // a先和c加,结果为16,在加上b的值7,比的结果为23,最后给a加1,a的值为9
	printf("a = %d b = %d c = %d\n:", a, b, c); // a:9, b:23, c:8
	return 0;
}

(类型) 强制类型转换

int main()
{
	int a = (int)3.14;
	printf("%d\n", a);//3

	return 0;
}

关系操作符

比大小

> >= < <= != ==

两个字符串是不能比大小的

实际比的是两个字符串中首字母的地址

strcmp-专门用来比较字符串的大小

对应位置上字符的大小,而不是比较长度

逻辑操作符

&& 逻辑与

|| 逻辑或

只在乎真假

区分:

1&2 0

1&&2 1

1|2 3

1||2 1

条件操作符

exp1 ? exp2 : exp3

表达式1为真,表达式2执行,表达式2的结果是整个表达式的结果

表达式1为假,表达式3执行,表达式3的结果是整个表达式的结果

逗号表达式

exp1,exp2,exp3,…expn

从左到右依次执行,整个表达式的结果是最后一个表达式的结果

下标引用、函数调用和结构成员

[ ]下标引用操作符

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

int main()
{
    int arr[10]={0};//创建数组
   
     arr[9] = 10;//实用下标引用操作符
}

[ ]的两个操作数是arr和9

等价于:

arr[4]<==>*(arr+4)<==>*(4+arr)<==>a[arr]

( ) 函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

int Add(int x,int y)
 {
     return x+y;
 }
 
 int main()
 {
   int ret =  Add(2,3);//()函数调用操作符,操作数就是:Add,2,3
   printf("%d\n",ret);
        return 0;
 }

访问一个结构的成员

. 结构体.成员名

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

struct Stu
{
    char name[20];
    int age;
    float score;
};

void print1(struct Stu ss)
{
    printf("%s %d %f\n", ss.name, ss.age, ss.score);
}
//结构体变量.成员名

void print2(struct Stu* ps)
{
    //printf("%s %d %f\n",(*ps).name,(*ps).age,(*ps).score);
    printf("%s %d %f\n", ps->name, ps->age, ps->score);

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

int main()
{
    struct Stu s = { "张三",20,90.5f };
    strcpy(s.name, "李四");//修改用strcpy
    //scanf("%s",s.name);

   print1(s);
    print2(&s);
    return 0;
}

表达式求值

表达式求值的顺序一部分是由操作度的优先级和结合性决定

有些表达式操作数在求值过程中可能需要转换为其他类型。

隐式类型转换

C语言的整型算术总是以缺省整型类型的精度来进行的

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

int main()
{
	char c1 = 3;
	//00000000000000000000000000000011
	//00000011   c1
	char c2 = 127;
	//00000000000000000000000001111111
	//01111111    c2

	char c3= c1 + c2;
	//10000010   c3 补码
	//111111111111111111111111110000010提升
	//111111111111111111111111110000001反码
	//100000000000000000000000001111110补码

	printf("%d\n", c3);//-126
	return 0;
}

char == ? 到底是signed char还是unsigned char取决于编译器

signed char

unsigned char

short == signed short

unsigned short

int == signed int

unsigned int

整型提升

负数补1,正数补0

int main()
{
    char c = 1;
    printf("%u\n",sizeof(c));//1
    printf("%u\n",sizeof(+c));//4
    printf("%u\n",sizeof(-c));//4
    return 0;
}
//c只要参与表达式运算就会发生整型提升

算术转换

long double

double

float

unsigned long int

long int

unsigned int

int

操作符的属性

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

1. 操作符的优先级

2. 操作符的结合性(从左到右或者从右到左)

3. 是否控制求值顺序。

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

猜你喜欢

转载自blog.csdn.net/Ll_R_lL/article/details/122799650
今日推荐