【C语言初阶】操作符详解

目录

一、 算术操作符   

 二、移位操作符

 三、位操作符

四、赋值运算符

 五、单目操作符

 六、关系操作符

 七、逻辑操作符:

 八、条件操作符

 九、逗号表达式

 十、下标引用、函数调用和结构成员操作符

 十一、表达式求值


一、 算术操作符   

+       -       *       /        %               

 注:1、除了%操作符之外,其他的几个操作符可以做用于整数和浮点数。

2、对于/操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法,举例:5/2-->2,得不到2.5,想要得到小数的结果,必须保证除数和被除数中至少有一个小数(浮点数)。(5/2.0)或(5.0/2)或(5.0/2.0)---->2.5.

3、%操作符的两个操作数必须为整数返回的是整数之后的余数


 二、移位操作符

(1)、  移位操作符移动的是存储在内存中的补码,补码在内存中是以2进制存在 。所以需要知道二进制位的表示。

<<    左操作符

>>    右操作符

注:移位操作符的操作数只能是整数。

(2)、整数的2进制表示:3种表示方式 原码、反码、补码。(整数在内存中存储的是二进制的补码)

原码:用第一位表示符号,其余表示值。

符号位是0,表示正数;

符号位是1,表示负数。

举例:整数15,在C语言中可以存放到int类型的变量中,int类型是4个字节,32个bit位。

            0 0000000 00000000 00000000 00001111  —15的原码

            1 0000000 00000000 00000000 00001111  —  -15的原码

反码

(1)、正数的反码是其本身

(2)、负数的反码是原码符号位不变,其余位取反。

举例:整数15的反码0 0000000   00000000   00000000   00001111 —15的反码

                                 1 1111111 11111111 11111111 11110000 — -15的反码

  补码:

(1)、正数的补码是其本身,

(2)、负数的补码是在其反码的基础上+1

举例: 0 0000000  00000000  00000000  00001111 — 15的补码

            1 1111111 11111111 11111111 11110001 —  -15的补码

1、 移位操作符的代码实现

(1)、左移操作符举例

#include
int main()
{
    int a = -4;    //10000000000000000000000000000100 - -4的原码
                   //11111111111111111111111111111011 - -4的反码
                   //11111111111111111111111111111100 - -4的补码
    int b = a << 1;//把a向左移动一位,移动-4的补码
                   //b中存储的补码111111111111111111111111111111000
                   //b的反码11111111111111111111111111111011
                   //b的原码10000000000000000000000000001000
    printf("a = %d b = %d\n", a,b);
    return 0; 
}
结果是 a=-4, b=-8

注释:左移有  乘2的效果。 

画图解释:

【C语言初阶】操作符详解_第1张图片

 (2)、右操作符举例:

         这里有两种方法

1、逻辑右移:右边丢弃,左边补0

2、算术右移:右边丢弃,左边补原符号位

   右移操作符到底采用哪种方法,C语言中没有给出明确规定,主要取决于编译器,绝大多        数编译器采用的是算数右移。

        这段代码采用算术右移  

#include
int main()
{
    int a = -4;    //10000000000000000000000000000100 - -4的原码
                   //11111111111111111111111111111011 - -4的反码
                   //11111111111111111111111111111100 - -4的补码
    int b = a >> 1;//把a向右移动一位,移动-4的补码
                   //b中存储的补码 11111111111111111111111111111110
                   //b的反码11111111111111111111111111111101
                   //b的原码10000000000000000000000000000010
    printf("a = %d b = %d\n", a,b);
    return 0; 
}
结果是:a=-4,b=-2

    算术右移的效果:除2的效果

画图解释:

【C语言初阶】操作符详解_第2张图片

注释:对于位移运算符,不要移动负数,这个标准未定义

例如:int a=10;

              a>>-1;//这种写法是错的。 


 三、位操作符

    位操作符都是针对二进制位来计算的。

位操作符有:

&     按位与:  按位与的运算:对应位有0则为0,对应位都为1则位1

|       按位或:按位或:有1则为1

^      按位异或: 异或操作符的运算:相应二进制码相同位0,相异为1。

#include
int main()
{
    int a = 3;
    int b = -5;
    int c = a & b;//按二进制位与
    printf("%d\n",c);
    //00000000000000000000000000000011   3的补码
    //10000000000000000000000000000101   -5的原码
    //11111111111111111111111111111010   -5的反码
    //11111111111111111111111111111011   -5的补码
       按位与
    //00000000000000000000000000000011   3的补码
    //11111111111111111111111111111011   -5的补码
    //00000000000000000000000000000011    c的补码   正数原码和补码相同    --->3  
    return 0;
}
                 按位或
    int c = a | b;//按二进制位或
    //00000000000000000000000000000011   3的补码
    //11111111111111111111111111111011   -5的补码
    //11111111111111111111111111111011    c的补码
    //10000000000000000000000000000101    c的原码  ---> -5
       
        按位异或
    int c = a ^ b;
    //00000000000000000000000000000011   3的补码
    //11111111111111111111111111111011   -5的补码
    //11111111111111111111111111111000   c的补码
    //11111111111111111111111111110111   c的原码




四、赋值运算符

=    +=    -=     *=     /=      %=     >>=      <<=      &=     |=      ^= 

这里举例说明:   a = a << 1相当于a <<= 1,每个赋值运算符运算思路都相同。


 五、单目操作符

!   -     +    &    sizeof    ~     --    ++    * 解引用操作符      (类型):强制类型转换操作符

1、逻辑反操作符 (!)

常用来判断

int i = 0;
if(! i)//0为假,!0为真
{
    printf("haha\n");
}

  2、操作符sizeof

     sizeof值得注意两个小点:

(1)、

int main()
{
    short s = 10;
    int a = 2;
    printf("%d\n",sizeof(s = a + 5));//short类型是2个字节,sizeof计算类型长度的时候,只会看变 
                                     //量是什么类型,不会计算内部的表达式
    printf("%d\n",s);//sizeof()内部放的表达式不计算。
}
结果是:2   10

 画图解释:

      【C语言初阶】操作符详解_第3张图片                     

 (2)、

#include
void test1(int arr[])//这里本质是int* arr
{
    printf("%d\n",sizeof(arr));//4  arr在这里是指针变量
}
void test2(char ch[])//这里本质是char* ch
{
    printf("%d\n",sizeof(ch));//4    ch在这里是指针变量
}
int main()
{
    int arr[10] = {0};
    char ch[10] = {0};
    printf("%d\n",sizeof(arr));//40
    printf("%d\n",sizeof(ch));//10
    test1(arr);
    test2(ch);
    return 0;
}

注释:指针变量,不论是什么类型的指针,在相同的环境下大小是相同的,上述代码是在X86的环境下,所以两个指针变量的大小都是4。

3、&取地址操作符:

&数组名,数组名表示整个数组,不是首元素地址。

&数组名,取出的是整个数组的地址。

4、按位取反(~)

int main()
{
    int a = 0;
    //00000000000000000000000000000000   a的补码
    //11111111111111111111111111111111   a按位取反  内存中的补码
    //11111111111111111111111111111110   反码
    //10000000000000000000000000000001   原码  --> -1 
    printf("%d\n",~a);
    return 0;
}

 六、关系操作符

>     >=    <     <=      != 用于测试”不相等“     ==用于测试”相等“


 七、逻辑操作符:

&& :逻辑与       &&两边表达式均为真,才表示真

||: 逻辑或           ||  只要一边表达式为真,整个表达式为真

#include 
int main()
{
	int i = 0, a = 1, b = 2, c = 3, d = 4;
	//i = a++ && ++b && d++;//从前到后第一个为真,第二个为真,再判断下一个
	
	i = a++||++b||d++;//从前往后依次判断中间只要有一个为真,后边就不用再计算

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

	printf("%d\n", i);
	return 0;
}
代码的i的值,不是1就是0

 八、条件操作符

表达式1  ?表达式2  :表达式3

条件操作符又称为三目操作符

#include
int main()
{
    int a = 3;
    int b = 5;
    int max = (a > b ? a : b);
    printf("%d\n",max);
    return 0;
}

 九、逗号表达式

表达式1,表达式2,表达式3,……表达式n

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

 逗号表达式的一种使用方式:


//如果代码这样写显得有些冗余
a = get_val();
count_val(a);
while(a > 0)
{
    业务处理
    a = get_val();
    count_val(a);
}

//可以通过逗号表达式来改写
while(a=get_val(),count_val(a),a>0)
{
    业务处理
}

 十、下标引用、函数调用和结构成员操作符

1、[ ]下标引用操作符

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

int  arr[10];//创建数组

arr[9] = 10;//访问第10个元素,这里的[ ]就是下标引用操作符。

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

 2、()函数调用操作符

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

void test(int x, int y)
{

}
void test2()
{}

int main()
{
	test2();
	//操作数:test2
	test(3, 4);//()函数调用操作符
	//操作数:test,3,4
	return 0;
}

 3、结构成员访问操作符

   .     结构体.成员名

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

struct Book
{
	char name[20];
	int price;
};

int main()
{
	struct Book sb = {"明解c语言", 55};
	
	printf("%s %d\n", sb.name, sb.price);//结构体变量.结构体成员名

	struct Book* ps = &sb;//结构体指针
	printf("%s %d\n", (*ps).name, (*ps).price);//结构体指针在这里解引用
	printf("%s %d\n", ps->name, ps->price);//结构体指针->结构体成员名

	return 0;
}

 十一、表达式求值

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

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

11.1 、隐式类型转换 

1、整型提升

对于在内存中所占字节小于int的数据,例如 char,short类型的数据,在进行加减时会进行隐形转换,转换成int类型的数据后再进行加减。

整型提升的意义:

表达式的整形运算要在CPU的相应运算期间内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。

通用CPU是难以直接实现两个8比特字节直接相加运算。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。

int main()
{
	char a = 5;//截断
    //00000000000000000000000000000101——5的二进制,要存进a中,要发生截断
    //00000101 —— a中存的   发生截断
	char b = 126;//截断
    //00000000000000000000000001111110 —— 126的二进制,要存进b中,要发生截断
    //01111110 —— b中存的   发生截断
	char c = a + b;//截断
    //00000101 —— a中存的
    //01111110 —— b中存的
    整型提升 a,b
    //00000000000000000000000000000101 ——a
    //00000000000000000000000001111110 ——b
    //00000000000000000000000010000011 ——(a+b)
    //10000011 —— c      发生截断
    printf("%d\n",c);//%d 十进制的方式打印有符号整数
     整型提升
    //11111111111111111111111110000011  c的补码
    //11111111111111111111111110000010  c的反码
    //10000000000000000000000001111101  c的原码
    return 0;
}

结果是:-125

注释:整型提升前面添加的是1还是0由符号位说了算,发生截断后的二进制数首位是符号位,例如:a+b的二进制数发生截断是  10000011,首位是1,在整型提升是32为二进制数,首位为1.

 补充:无符号整型提升,高位补0

11.2 算术转换(针对的是类型大于int 的数据进行转换,向上转换 )

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

long double

double

float

unsigned long int 

long int 

int 

依次向上提升 

但是算数转换要合理,不然会有一些潜在的问题。

例如:float f =3.14;

           int num = f;//这里发生隐式转换,会有精度丢失

12.3、操作符的属性

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

1、操作符的优先级

2、操作符的结合性

3、是否控制求值顺序。

两个相邻的操作符先执行那个?取决于它们的优先级。如果两者的优先级相同,取决于它们的结合性。例如:5+6*2;*号的优先级高于+号的优先级,所以先算6*2,但当是2*5*6时没法判断优先级,这时候需要用结合性来判断。

 操作符优先级从上到下依次递减

操作符 描述 结合性 是否控制求值顺序
() 聚组 N\A
() 函数调用 从左到右
  [ ] 下标引用 从左到右
  . 访问结构成员 从左到右
-> 访问结构指针成员 从左到右
++ 后缀自增 从左到右
-- 后缀自减 从左到右

逻辑反 从右到左
~ 按位取反 从右到左
+ 单目,表示正值 从右到左
- 单目,表示负值 从右到左
++ 前缀自增 从右到左
-- 前缀自减 从右到左
* 间接访问(解引用) 从右到左
& 取地址 从右到左
sizeof 取其长度,以字节表示 从右到左
(类型) 类型转换 从右到左
* 乘法 从左到右
/ 除法 从左到右
% 整数取余 从左到右
+ 加法 从左到右
- 减法 从左到右
<< 左移位 从左到右
>> 右移位 从左到右
> 大于 从左到右
>= 大于等于 从左到右
<= 小于等于 从左到右
== 等于 从左到右
!= 不等于 从左到右
& 位与 从左到右
^ 位异或 从左到右
| 位或 从左到右
&& 逻辑与 从左到右
|| 逻辑或 从左到右
?: 条件操作符 N/A
= 赋值 从右到左
+= 加后赋值 从右到左
-= 减后赋值 从右到左
*= 乘后赋值 从右到左
/= 除后赋值 从右到左
%= 取模后赋值 从右到左
<<= 左移后赋值 从右到左
>>= 右移后赋值 从右到左
&= 位于后赋值 从右到左
^= 异或后赋值 从右到左
|= 位或后赋值 从右到左
逗号 从左到右

                                                            

你可能感兴趣的:(C语言,java,数据库,前端)