嵌入式基础 C语言篇 运算符

一、算术运算符

运算符

功能说明

举例

+

加法,一目取正

a+b

-

减法,一目取负

a-b

*

乘法

a*b

/

除法

a/b

%

取模(求余)

a%b

++

自加1

a++, ++b

--

自减1

a--, --b

  • 注意点

    • 减号也是负号,比如 -a 是取变量 a 的相反数(a = -a(取正负号,0还是0))
    • 取模运算要求左右两边操作数必须是整型数据。
    • 自加和自减运算不仅可以对整型操作,也可以对浮点数、指针操作。
  • 自加和自减的原理说明:
  1. i++是先使用 i 的值,再i+1;++i是先 i+1 ,再使用 i 的值

底层原理 (目前可以不看):

  1. i++和++i都是带有返回值的函数
  2. i++返回的是一个常量值
  3. ++i返回的是i的引用/指针

 二、关系运算符

运算符

功能

举例

说明

>

大于

a > b

判断a是否大于b

>=

大于或等于

a >= 5

判断a是否大于或等于5

小于

3 < x

判断3是否小于x

小于或等于

x

判断x是否小于或等于y+1

==

等于

(x+1) == 0

判断x+1是否等于0

!=

不等于

c != ‘\0’

判断c是否不等于’\0’

关注点

    • 关系运算符用于判断运算符两边的表达式是否满足给定的大小条件。
    • 由关系运算符组成的表达式称为关系表达式,其值为布尔型(即TRUE和FALSE)。
    • 判断是否相等是双等号==,而不是一个等号(是赋值号)。
  • 示例代码

  • #include <stdio.h>
    int main(int argc, char const *argv[])
    {
        // (1)、数学表达式(人的看法)和关系运算符表达式(机器的看方法)的区别
        /*
            数学表示式(人的看法):
                100<x<200: x的取值范围为100到200
    
            关系运算符表达式(机器的看方法):
                100<x: 判断是否为真,为真返回true(非0:1), 为假返回false(0)
                再用(1或者0)与200相比较,所以此式子永远为真
        */
        int x = 80;
        if (100<x<200)      // 不管x为多少,都会一直为真(因为1或者0永远小于200,所以为真)
        {
            printf("执行这个语句1\n");
        }
    
        // 正确的写法:
        if (x>100 && x<200) // x>100:假   x<200:真 ,
        {
            printf("执行这个语句2\n");
        }
        else
        {
            printf("执行这个语句3\n");
        }
        
        // (2)、判断是否相等(==),而不是一个等号(=:赋值号)
        int num1 = 200;
    
        // 正确的写法:
        if ( num1 == 300)
        {
            printf("执行这个语句4\n");
        }
        else
        {
            printf("执行这个语句5\n");
        }
        
        // 错误的写法:
        if ( num1 = 100)        // 赋值语句一直为真,所以会一直执行
        {
            printf("执行这个语句6\n");
        }
        else
        {
            printf("执行这个语句7\n");
        }
        
        return 0;
    }
    

    三、逻辑运算符

运算符

功能说明

举例

!

逻辑反

!(x==0)

&&

逻辑与

x>0 && x

||

逻辑或

y10

  • 运算规则

    • 逻辑反:将逻辑真、假翻转,即真变假,假变真。
    • 逻辑与:将两个关系表达式串联起来,当且仅当左右两个表达式都为真时,结果为真。
    • 逻辑或:将两个关系表达式并联起来,当且仅当左右两个表达式都为假时,结果为假。

  • 特殊规则

    • 在逻辑与运算中,如果左边表达式的值为假,那么右边表达式将不被执行。
    • 在逻辑或运算中,如果左边表达式的值为真,那么右边表达式将不被执行。

 

  • 示例代码

#include <stdio.h>
#include <stdbool.h>    // 布尔类型
#include <unistd.h>     // 延时函数

int main(int argc, char const *argv[])
{
    // // (1)、逻辑翻转(比如:标志位取反、led灯的闪烁)
    // bool led = 1;       // 非0为真:一般为1

    // while (1)
    // {
    //     led = !led;     // led的值为真,遇到这句话,变为假,反之亦然
    //     if (led == true)
    //     {
    //         printf("led亮............\n");
    //     }
    //     else
    //     {
    //         printf("led灭............\n");
    //     }
    //     sleep(1);      // 延时1s;  usleep(1)微妙:  1s == 100 0000us
    // }
    

    // (2)、逻辑与和逻辑或
    int num1 = 100;
    int num2 = 200;

    // 1、逻辑与
    if ( (num1 == 100) && (num2 == 0) ) // &&的两边的表达式都为真,那么为真
    {
        printf("执行这个语句1\n");
    }
    else
    {
        printf("执行这个语句2\n");
    }
    
    // 注意:- 在逻辑与运算中,如果左边表达式的值为假,那么右边表达式将不被执行。
    if ( (num1 == 99) && (num2 = 99) )
    {
        printf("执行这个语句3\n");
    }
    else
    {
        printf("执行这个语句4\n");
    }
    printf("num1 == %d\n", num1);
    printf("num2 == %d\n", num2);


    // 2、逻辑或
    int num3 = 0;
    int num4 = 0;
    if ((num3 == 100) || (num4 == 0))   // ||的两边的表达式只要有一个为真,那么为真
    {
        printf("执行这个语句5\n");
    }
    else
    {
        printf("执行这个语句6\n");
    }
    
    // 注意:- 在逻辑或运算中,如果左边表达式的值为真,那么右边表达式将不被执行。
    if ( (num3 == 0) || (num4 = 888))
    {
        printf("执行这个语句7\n");
    }
    else
    {
        printf("执行这个语句8\n");
    }
    printf("num3 == %d\n", num3);
    printf("num4 == %d\n", num4);

    // 题:
    int m = 1;
    int n = 1;
    int a = 1;
    int b = 1;
    int c = 1;
    int d = 1;

    if ( (m=a>b) || (n=c>d))       // ||左边的式子为赋值为0,所以为假; 右边的式子还需计算,确定是否为真为假
    // if ( (m=a>b) && (n=c>d))    // &&左边的式子为赋值为0,所以为假;右边的式子无需计算了
    {
      
    }
    printf("m == %d\n", m);
    printf("n == %d\n", n);


    return 0;
}

四、位运算符

运算符

名称

举例

功能说明

~

位逻辑反

~a

将变量 a 中的每一位取反

&

位逻辑与

a & b

将变量 a 和 b 逐位进行与操作(任何数与上0都为0)

|

位逻辑或

a | b

将变量 a 和 b 逐位进行或操作(任何数或上1都为其1)

^

位逻辑异或

a ^ b

将变量 a 和 b 逐位进行异或操作(相同为0,不同为1)

左移

a

将变量 a 中的每一位向左移动4位

>>

右移

x >> 4

将变量 x 中的每一位向右移动4位

  • 位运算符操作的对象是数据中的每一位
  • 运算规则:
    • 位逻辑反、位逻辑与、位逻辑或拥有与逻辑运算相似的规则和一样的真值表
    • 异或运算:相同为0,不同为1
    • 移位运算:移出去的不要,空出来的补零。移位运算都是针对无符号数的运算。

左移运算:int a;

 

右移运算 

示例代码 

#include <stdio.h>
int main(int argc, char const *argv[])
{                               //      31                    15                  0
    unsigned int num = 123456;  // (高位)0000 0000  0000 0001  1110 0010  0100 0000(低位)

    // 问题1:将num的第15位置为0, 其它位保持不变
    num &=~(1<<15);         // 等价于:num = num  & (~(1<<20));
    /*
        解析:
            1:        0000 0000  0000 0000  0000 0000  0000 0001
            1<<15:    0000 0000  0000 0000  1000 0000  0000 0000
            ~(1<<15): 1111 1111  1111 1111  0111 1111  1111 1111
            &
            num(原来):0000 0000  0000 0001  1110 0010  0100 0000
            =
            num(现在):0000 0000  0000 0001  0110 0010  0100 0000 
                                            第15位这个位置变0了,其它位不变
    */
   
    // 问题2:将num的第30位置为1, 其它位保持不变
    num |=(1<<30);          // 等价于:num = num | (1<<30);
    /*
        解析:
            1:        0000 0000  0000 0000  0000 0000  0000 0001
            1<<30:    0100 0000  0000 0000  0000 0000  0000 0000
            |
            num(原来):0000 0000  0000 0001  1110 0010  0100 0000
            =
            num(现在):0100 0000  0000 0001  1110 0010  0100 0000
                    第30位这个位置变1了,其它位不变
    */
  
    // 问题3:将num的第10位取反, 其它位保持不变
    num ^=(1<<10);          // 等价于:num = num ^ (1<<10);
    /*
        解析:
            1:        0000 0000  0000 0000  0000 0000  0000 0001
            1<<10:    0000 0000  0000 0000  0000 0100  0000 0000
            ^
            num(原来):0000 0000  0000 0001  1110 0010  0100 0000
            =
            num(现在):0000 0000  0000 0001  1110 0110  0100 0000
                                            第10位这个位置被取反了,其它位不变
    */
    

    // 问题4:判断num的第15位是否为1??
    if ( num & (1<<15))     // 等价于:if( (num & (1<15) != 0 )
    {
        /* code */
    }

    /*
        解析:
            1:        0000 0000  0000 0000  0000 0000  0000 0001
            1<<15:    0000 0000  0000 0000  1000 0000  0000 0000
            &
            num:      0000 0000  0000 0001  1110 0010  0100 0000 (num的第15位如果为1,那么num & (1<<15)的结果就为真,否则为加)
            =
            值:       0000 0000  0000 0000  1000 0000  0000 0000
    */

    // 问题5:知道一个地址0xc000 0000,向该地址写数据(int)0x12345678?
    /*
        解析:
            地址:0xc000 0000 (因为题目告诉我它是一个地址(如果题目没有明确说明,那么这个十六进制数是数据))
            数据:(int)0x12345678 

        那我们怎么再代码中表示地址呢??指针
            代码中直接写(int)0x12345678 : 表示数据

            代码中直接写(int*)0xc000 0000 :表示地址(表示指针)

            代码中,我想获取地址里面的内存: *(int*)0xc0000000(获取这个地址的内存(内存的范围是int型))
    */

    // *(int*)0xc0000000 = (int)0x12345678;     // 在linux应用层开发的时候:内存是由linux系统分配的,所以我们没有这个内存的所有权限、
                                                // 在单片机开发中可以:这个地址一般是具体的寄存器地址(实际存在,且程序员可以区读取访问修改)
    // int型的指针内存    =  int型数据


    // 问题6:读取地址0xc000 0000下的内容(假设这个地址指向的内存是int型类型的)
    // printf("地址为0xc000 0000的内存里面的数据为 == %d\n", *(int*)0xc0000000);

    return 0;
}

 五、特殊运算符

  • 赋值运算符

    • 不能对常量赋值,只能对变量赋值
    • 不能对数组赋值
    • 可以连续赋值,顺序从右到左
  • 示例代码

#include <stdio.h>
int main(int argc, char const *argv[])
{
    // (1)、赋值运算符
    // 1、- 不能对常量赋值,只能对变量赋值
    int num1 = 100;     // 可以,将常量100初始化给变量num1(初始化)
    int num2 = 200;     // 可以,将常量200初始化给变量num2(初始化)
    // 100   = num1;    // 不可以,常量不可被赋值(因为常量的内存区域在内存的常量区中(可读不可写))
    // 200   = num2;    // 不可以 
    num2     = 300;     // 可以(赋值)

    // (2)、不能对数组赋值
    char buf[128] = "niweismyaozhuiwo,woyaojizhitangjiang"; // 可以,但这个不算赋值,这个是初始化
    // buf           = "wangwangzeihaohe";                  // 不可以,直接赋值(因为buf是一个常量指针,buf不能移动,所以不能赋值)
    // strcpy(buf, "woxiangqukankan");                      // 可以,使用函数进行赋值操作(而不是直接=赋值操作)

    // (3)、可以连续赋值,顺序从右到左 
    int num3;
    num3=num2=num1=400;
    printf("num1 == %d\n", num1); 
    printf("num2 == %d\n", num2);
    printf("num3 == %d\n", num3);

    return 0;
}

  • 复合赋值符

    • 当左右两边有相同的操作数时,采用复合赋值符不仅直观,且能提高运算效率
    • 除了下述10个复合运算符之外,生造别的复合运算符是非法的
  • 示例代码

// 加减乘除:
a += n; // 等价于 a = a+n;
a -= n; // 等价于 a = a-n;
a *= n; // 等价于 a = a*n;
a /= n; // 等价于 a = a/n;

// 求余:
a %= n; // 等价于 a = a%n;


// 位运算:
a &= n; // 等价于 a = a&n;
a |= n; // 等价于 a = a|n;
a ^= n; // 等价于 a = a^n;
a >>= n; // 等价于 a = a>>n;
a <<= n; // 等价于 a = a<<n;

 六、条件运算符

  • 唯一需要三个操作数的运算符(三目运算符)
  • 语法:表达式1 ? 表达式2 : 表达式3
  • 释义:当表达式1为真时,取表达式2,否则取表达式3
  • 示例代码

#include <stdio.h>
#include <unistd.h>

#define ON  1
#define OFF 0
#define LED(X)    X?printf("LED_ON!\n"):printf("LED_OFF!\n")
#define MAX(A,B)  (A>B)?(A):(B)

int main(int argc, char const *argv[])
{
    // // (1)、亮灭led的操作 (开发)
    // while (1)
    // {
    //     LED(ON); 
    //     sleep(1);   // 延时1s钟

    //     LED(OFF);
    //     sleep(1);  
    // }
    
    // (2)、比较两个数的大小
    // 1、宏定义
    int max_num1 = MAX(100, 200);
    printf("max_num1 == %d\n", max_num1);

    // 2、直接调用
    int a = 800;
    int b = 200;
    int max_num2 = a>b?a:b;
    printf("max_num2 == %d\n", max_num2);
    
    return 0;
}

七、sizeof运算符

  • 含义:计算指定数据类型或者变量所占据内存的字节数
  • 语法:sizeof(数据类型) 、sizeof(变量) ,计算变量、常量的字节数时圆括号可以省略(但是类型不可省略)
  • 示例代码

    #include <stdio.h>
    int main(int argc, char const *argv[])
    {
        // (1)、sizeof计算类型的大小
        printf("数据类型的大小\n");
        printf("char   size == %lu\n", sizeof(char));
        printf("int    size == %lu\n", sizeof(int));
        printf("float  size == %lu\n", sizeof(float));
        printf("double size == %lu\n", sizeof(double));
    
        // (2)、sizeof计算变量的大小
        char ch   = 'a';
        int  num  = 100;
        float f1  = 3.14;
        double f2 = 6.18;
        printf("变量的大小\n");
        printf("ch size  == %lu\n", sizeof(ch));
        printf("num size == %lu\n", sizeof(num));
        printf("f1 size  == %lu\n", sizeof(f1));
        printf("f2 size  == %lu\n", sizeof(f2));
    
        // (3)、sizeof的括号可以省略
        printf("\n");
        printf("100    size == %lu\n", sizeof 100);        // 可以,整型常量
        printf("nihao  size == %lu\n", sizeof "nihao");    // 可以,字符串常量
        printf("ch     size == %lu\n", sizeof ch);         // 可以,字符变量
        // printf("int    size == %lu\n", sizeof int);     // 不可以
    
        // 笔试题、面试题
        // 1、sizeof和strlen的区别???
        /*
            sizeof是一个运算符,能够计算出变量的字节大小,对于字符串来说,sizeof会把字符串
            最后的哪个'\0'字节也计算进去
    
            strlen是一个C语言库里提供的一个字符串处理函数,只能计算字符串的长度,遇到'\0'就会结束
            不会把'\0'也一并计算进去
         */
    
        // 2、以下为Linux下的32位C程序,请计算sizeof的值
        char str[] = "hello";       // 字符串后面有一个结束标记符''
        char *p = str;              // 指针大小只看操作系统的位数,不看修饰的数据类型
        int n = 10;
        // 请计算   (1) sizeof (str) = 6    (2) sizeof(p) = 4  (3) sizeof(n) = 4
    
        // 3、sizeof计算字符常量的时候,会默认将其认为int数据来对待
        printf("'a' size == %lu\n", sizeof 'a');            // 可以,字符常量   // 会将其当成int型数据来对待
        printf("'a' size == %lu\n", sizeof ((char)'a'));    // 可以,字符常量 
    
        return 0;
    }
  • 七、sizeof运算符

  • 含义:计算指定数据类型或者变量所占据内存的字节数
  • 语法:sizeof(数据类型) 、sizeof(变量) ,计算变量、常量的字节数时圆括号可以省略(但是类型不可省略)
  • 示例代码

  • #include <stdio.h>
    int main(int argc, char const *argv[])
    {
        // (1)、sizeof计算类型的大小
        printf("数据类型的大小\n");
        printf("char   size == %lu\n", sizeof(char));
        printf("int    size == %lu\n", sizeof(int));
        printf("float  size == %lu\n", sizeof(float));
        printf("double size == %lu\n", sizeof(double));
    
        // (2)、sizeof计算变量的大小
        char ch   = 'a';
        int  num  = 100;
        float f1  = 3.14;
        double f2 = 6.18;
        printf("变量的大小\n");
        printf("ch size  == %lu\n", sizeof(ch));
        printf("num size == %lu\n", sizeof(num));
        printf("f1 size  == %lu\n", sizeof(f1));
        printf("f2 size  == %lu\n", sizeof(f2));
    
        // (3)、sizeof的括号可以省略
        printf("\n");
        printf("100    size == %lu\n", sizeof 100);        // 可以,整型常量
        printf("nihao  size == %lu\n", sizeof "nihao");    // 可以,字符串常量
        printf("ch     size == %lu\n", sizeof ch);         // 可以,字符变量
        // printf("int    size == %lu\n", sizeof int);     // 不可以
    
        // 笔试题、面试题
        // 1、sizeof和strlen的区别???
        /*
            sizeof是一个运算符,能够计算出变量的字节大小,对于字符串来说,sizeof会把字符串
            最后的哪个'\0'字节也计算进去
    
            strlen是一个C语言库里提供的一个字符串处理函数,只能计算字符串的长度,遇到'\0'就会结束
            不会把'\0'也一并计算进去
         */
    
        // 2、以下为Linux下的32位C程序,请计算sizeof的值
        char str[] = "hello";       // 字符串后面有一个结束标记符'\0',sizeof计算时,也需要计算进去
        char *p = str;              // 指针大小只看操作系统的位数,不看修饰的数据类型
        int n = 10;
        // 请计算   (1) sizeof (str) = 6    (2) sizeof(p) = 4  (3) sizeof(n) = 4
    
        // 3、sizeof计算字符常量的时候,会默认将其认为int数据来对待
        printf("'a' size == %lu\n", sizeof 'a');            // 可以,字符常量   // 会将其当成int型数据来对待
        printf("'a' size == %lu\n", sizeof ((char)'a'));    // 可以,字符常量 
    
        return 0;
    }
    

    八、return运算符

  • 含义:退出某个函数(如果退出的是主函数main,那么整个程序也就退出了)
  • 语法:必须出现在函数体内,可以带函数对应类型的数据
  • 示例代码

  • #include <stdio.h>
    // 普通函数
    // 比较两个整型数据的大小
    int Max(int a, int b)
    {
        return a>b?a:b; // 返回的数据一定要是int型
    }
    
    // 加
    float Add(float a, float b)
    {
        return a+b;     // 返回的数据一定要是float型
    }
    
    // 减
    float Substract(float a, float b)
    {
        return a-b;
    }
    
    // 乘
    float Multiply(float a, float b)
    {
        return a*b;
    }
    
    // 除
    float Divide(float a, float b)
    {
        return a/b;
    }
    
    // 取余
    int Remainder(int a, int b)
    {
        return a%b;
    }
    
    // 主函数
    int main(int argc, char const *argv[])
    {
        printf("Max(100,200) == %d\n", Max(100,200));
        printf("Add(100,200) == %f\n", Add(100,200));
        printf("Substract(100,200) == %f\n", Substract(100,200));
        printf("Multiply(100,200) == %f\n", Multiply(100,200));
        printf("Divide(100,200) == %f\n", Divide(100,200));
        printf("Remainder(10,3) == %d\n", Remainder(10,3));
        return 0;
    }

    九、优先级和结合性

  • 示例代码

    #include <stdio.h>
    int main(int argc, char const *argv[])
    {
        // (1)、优先级的说明(比如:加减乘除)
        int a = 100;
        int b = 200;
        int c = 5;
        int d = 2;
    
        int num1 = a-d*b+a/c;    // 原来的想法:a-d==》得数*b==>得数+a==》得数/c
                                 // 实际的执行:先执行d*b(得数1),再执行a/c(得数2), 再执行a-得数1+得数2
        printf("num1 == %d\n", num1);
    
        // 应改为:
        int num2 = ((a-d)*b+a)/c;    
        printf("num2 == %d\n", num2);
    
        // (2)、一般为了不产生歧义,以及为了更好的运算,所以你的表达式最好都加上()(因为()是除了[]优先级合结合性都是最高的)
        int m  = 1;
        int n  = 1;
        int a2 = 1;
        int b2 = 1;
        int c2 = 1;
        int d2 = 1;
     
        // 不推荐:if ( m= a2>b2 || n= c2>d2)   // 原来的想法: 先求出||的左边的式子:m=a2>b2是否为真,如果不是再求出||右边的式子:n=c2>d2是否为真 
        // 推荐: if ( (m=a2>b2) || (n=c2>d2) )
        {                                       /*
                                                    实际的执行: 
                                                    1、执行a2>b2(求出来:1或0)
                                                    2、执行c2>d2(求出来:1或0)
                                                    3、1或0(a2>b2) || n  == 1或0
                                                    4、m=1或0=1或0  // 此处常量不能被赋值
                                                */
    
        }
        printf("m == %d\n", m);
        printf("n == %d\n", n);
    
        // (3)、普通的优先级合结合性
        int n1,n2,n3=100,n4;            // =赋值号优先于,号,先执行n3=100的操作
        printf("n3 == %d\n", n3);
    
    
        // (4)、辨别复杂类型的方法
        // 技巧1:先看变量名(函数名)
        // 技巧2:再看其优先级,看其和谁先结合,那么其本质就是什么
        int p1;             // int型p1
        int p2[5];          // int型数组p2
        int *p3;            // int型指针p3
        int **p4;           // int型指针指针p4(二级指针)
        int *p5[5];         // int型指针数组p5
        int (*p6)[5];       // int型数组指针p6
        int (*p7[5])(int);  // int型函数指针数组p7
        return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值