算数操作符

+ - * / % ( 加法,减法,乘法,除法(商),取模(取余) )

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a = 5%2;        //商2余1--结果为1
    int A = 5/2;        //      --结果为2
    
    //float b=5%2.0;      无法使用
    float b =5/2.0;     //正确(用%f),结果为2.500000
    
    printf("a=%d\n",a);
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

还要注意:

除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数% 操作符的两个操作数必须为整数,返回的是整除之后的余数

移位操作符

>> 右移操作符 ;

整数二进制有三种表示形式 --- 原码 , 反码 , 补码

存储到内存中的是 补码

正数三码相同

负数 -1 ---  原码 : 10000000 00000000 00000000 00000001

反码: 11111111 11111111 11111111 11111110 (符号位不变,其他位按位取反)

补码: 11111111 11111111 11111111 11111111 (反码加1)

右移操作符

移位规则:

  1. 算术右移 --- 右边丢弃 , 左补原符号(正0负1)
  2. 逻辑右移 --- 右边丢弃 , 左边补0
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a=16;                // 00000000 00000000 00000000 00010000 --16
    int b=a>>1;              // 00000000 00000000 00000000 00001000 --8
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

左移操作符

移位规则: 左边抛弃、右边补0。

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

int num=10;

num>>-1; //error

位操作符

& 按位与

|  按位或

^ 按位异或(相同为0,不同为1)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
   // & 按位与 --- 按 二进制位 与 --- (有零为零,全一则一)
   int a = 3;        //00000000 00000000 00000000 00000011
   int b = 5;        //00000000 00000000 00000000 00000101
   int c =a&b;       //00000000 00000000 00000000 00000001 --- 1
   
   // | 按位或 --- 按 二进制位 或 --- (有一为一,全零则零) 
   int a = 3;        //00000000 00000000 00000000 00000011
   int b = 5;        //00000000 00000000 00000000 00000101
   int c =a|b;       //00000000 00000000 00000000 00000111 --- 7
   
   // ^按位异或 --- 按 二进制位 异或 ---(相同为0,相异为1)
   int a = 3;        //00000000 00000000 00000000 00000011
   int b = 5;        //00000000 00000000 00000000 00000101
   int c =a|b;       //00000000 00000000 00000000 00000110 --- 6
   return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

练习 1: 不创建临时变量完成a,b的互换

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a=3;        //011
    int b=5;        //101
    
    //方案 1 - 加减法(缺陷:可能溢出)
    a = a+b;        //a=3+5=8
    b = a-b;        //b=8-5=3
    a = a-b;        //a=8-3=5
    
    //方案 2 - 异或法
    a=3;            //011
    b=5;            //101
    a = a^b;        //110 --- 6
    b = a^b;        //011 --- 3
    a = a^b;        //101 --- 5
    
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

练习 2: 求一个整数储存在内存中的二进制中的1的个数

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a=0;  
    int count=0;
    scanf("%d",&num)
    //方案 1
    while(num)            //只能算正数
    {
        if(num%2==1)
            count++;
        num=num/2;
    }
    //方案 2
    int i = 0;
    for (i=0;i<32;i++)
    {
        if(1==(num>>1)&1)
            count++;    
    }
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

赋值操作符

=

复合赋值符:

+=            --->   a+=1  <--->   a=a+1

*=

/=

%=

>>=

<<=

& =

| =

^ =

单目操作符

  • ! 逻辑反操作
  • -/+ 负值/正值
  • & 取地址
  • sizeof 操作数的类型长度(以字节为单位) --- ()中的表达式不会真实执行; 传地址(指针变量)占4个字节
  • ~ 对一个二进制取反
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a=11;            // a      00000000 00000000 00000000 00001011
    a=a|(1<<2);          //1<<2    00000000 00000000 00000000 00000100
    printf("%d\n",a)     // 新a    00000000 00000000 00000000 00001111
    
    a=a & (~(1<<2));     //~(1<<2) 11111111 11111111 11111111 11111011
                         // 新a    00000000 00000000 00000000 00001011
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

  • --/++ 前置(先--/++,再使用),后置(先使用,再--/++)
  • * 间接访问操作符(解引用操作符)
  • (类型) 强制类型转换

关系操作符

  • >
  • >=
  • <
  • <=
  • != 用于测试 ' 不相等 '
  • == 用于测试 ' 相等 '

逻辑操作符

  • && 逻辑 与(全真为真)
  • | | 逻辑 或(一真为真)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int a = 3; 
    int b = 5;
    int c = a && b;
    printf("%d\n",c);        //结果--1          
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    int i=0,a=0,b=2,c=3,d=4;
    i=a++ && ++b && d++;            //i=0, a=1, b=2, c=3, d=4
                                    //电脑偷懒 左边为假 && 右边不再运算
    int i=0,a=1,b=2,c=3,d=4;
    i=a++ || ++b || d++;            //i=1, a=2, b=2, c=3, d=4                   
                                    
    int i=0,a=1,b=2,c=3,d=4;
    i=a++ && ++b && d++;            //i=8, a=2, b=3, c=3, d=5      
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

条件操作符(三目操作符)

exp1 ? exp2 : exp3 (解释: 1如果真,执行2 ; 1如果假,执行3 )

#include<stdio.h>
int main()
{
    int a = 10;
    int b = 20;
    int max;
    //if (a > 5)
    //    b = 3;
    //else
    //    b = -3;
    //b = a > 5 ? 3 : -3;
    max = a > b ? a : b;
    printf("a = %d\nb = %d\nmax = %d\n", a, b, max);
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

逗号表达式

exp1, exp2, exp3, …….expN

逗号表达式:就是用逗号隔开的多个表达式。

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

#include<stdio.h>
int main()
{
    int a = 1;
    int b = 2;
    int c = (a > b, a = b + 10, a, b = a + 1);    //c=13
    printf("%d\n", c);
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

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

下标引用操作符 [ ]

#include<stdio.h>
int main()
{
    //1.定义一个数组
    int a[10] = { 0 };
    //2.如果我们想要访问第5个数组元素,并给其赋值
    a[4] = 10;//3.用数组变量名+[]+下标索引数字
    //4.[ ] 对应的两个操作数一个是变量名a  另外一个就是下标/索引值 4
    printf("%d\n", a[4]);
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

函数调用操作符 ( )

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

#include<stdio.h>
//2.这个地方的()不是函数调用操作符,是函数定义的语法规则
int get_max(int x, int y)
{
    return x > y ? x : y;
}
int main()
{
    int a = 10;
    int b = 20;
    //1.调用函数的时候使用的() 就是函数调用操作符
    int max = get_max(a, b);
    //3.这里的函数调用操作符()的操作数为 函数名get_max,函数参数a,函数参数b  总共三个操作数
    //4.对于函数调用操作符()而言,其操作数至少要有一个(函数名),可以有多个
    printf("max = %d\n", max);
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

访问一个结构的成员 . ->

结构体. 成员名

结构体指针->成员名

思考:如何创建结构体类型和结构体类型变量呢?

#include<stdio.h>
//1.现实中为了描述复杂的对象,构建结构体类型
//2.比如学生,有姓名,年龄,学号等信息
//创建一个结构体类型
struct Stu              //3.struct Stu是一个结构体类型,表示学生类型
            //类型是用来创建变量的
{
    char name[20];         //姓名
    int age;               //年龄
    char id[20];           //学号
};
int main()
{
    int a = 10;
    //使用struct Stu这个类型创建了一个学生对象s1,并初始化
    struct Stu s1 = { "张三",20,"20240803" };
    printf("%s\n", s1.name);
    printf("%d\n", s1.age);
    printf("%s\n", s1.id);
    
    struct Stu* ps = &s1;
    //结构体指针->成员名
    printf("%s\n", ps->name);        //等价于  printf("%s\n", (*ps).name)
    printf("%d\n", ps->age);
    printf("%s\n", ps->id);
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

表达式求值

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

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

隐式类型转换(悄悄的进行类型转换)

整型提升
  • C的整型算术运算总是至少以缺省整型类型的精度来进行的。
  • 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
  • 是不是看的不太懂?这里我们举例说明:
#include<stdio.h>
int main()
{
        char a = 3;
        //00000000 00000000 00000000 00000011  整数3
        //0000 0011  ->a   a是char类型,只有一个字节,这时候就会发生截断
        //截断的规则:挑最小最低位的字节内容
        char b = 127;
        //00000000 00000000 00000000 01111111  整数127
        //0111 1111  ->b
        //a+b  a和b如何相加 按照变量数据类型的符号位来提升的
        //00000011 ->00000000 00000000 00000000 00000011
        //01111111 ->00000000 00000000 00000000 01111111
        //           00000000 00000000 00000000 10000010
        char c = a + b;//这时候a,b被提升为普通整型
        //10000010 ->c
        //后面我们要以%d的形式打印,需要进行整型提升:按照变量数据类型的符号位(第一位)来提升的 
        //11111111 11111111 11111111 10000010  ---补码
        //11111111 11111111 11111111 10000001  ---反码
        //10000000 00000000 00000000 01111110  ---原码 -126
        printf("%d\n", c);
        printf("%c\n", c);
        return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
printf("%u\n"),sizeof(c);    //1
printf("%u\n"),sizeof(+c);   //4
printf("%u\n"),sizeof(!c);   //1
  • 1.
  • 2.
  • 3.
算术转换

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

long double

double

float

unsigned long int

long int

unsigned int

int

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

(比如说int类型跟float类型参与运算,先要将int转换为float类型,然后float类型与float类型运算)

操作符的属性

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

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

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

C语言初阶5 -操作符_操作符

C语言初阶5 -操作符_操作符_02

C语言初阶5 -操作符_操作数_03

常见问题解答:

1、如何记住运算符的15种优先级和结合性?

C语言中运算符种类比较繁多,优先级有15种,结合性有两种。

2、如何记忆两种结合性和15种优先级?下面讲述一种记忆方法。

结合性有两种,一种是自左至右,另一种是自右至左,大部分运算符的结合性是自左至右,只有单目运算符、三目运算符的赋值运算符的结合性自右至左。

优先级有15种。

记忆方法:

记住一个最高的:构造类型的元素或成员以及小括号。

记住一个最低的:逗号运算符。

剩余的是一、二、三、赋值。

意思是单目、双目、三目和赋值运算符。 在诸多运算符中,又分为:

算术、关系、逻辑。

两种位操作运算符中,移位运算符在算术运算符后边,逻辑位运算符在逻辑运算符的前面。再细分如下: 算术运算符分 * , / , % 高于 + , - 。 关系运算符中,〉,〉 = , < , <= 高于 == ,! = 。

逻辑运算符中,除了逻辑求反(!)是单目外,逻辑与( && )高于逻辑或( || )。 逻辑位运算符中,除了逻辑按位求反(~)外,按位与(&)高于按位半加(^),高于按位或( | )。

这样就将15种优先级都记住了,再将记忆方法总结如下: 去掉一个最高的,去掉一个最低的,剩下的是一、二、三、赋值。双目运算符中,顺序为算术、关系和逻辑,移位和逻辑位插入其中。

问题表达式

下面我们来看一些问题表达式:

//表达式的求值部分由操作符的优先级决定。

表达式1

a * b + c * d + e * f ;

注释∶代码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;

注释︰同上,操作符的优先级只能决定自减–的运算在 + 的运算的前面,但是我们并没有办法得知, + 操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。