C语言基础(超细节)

计算机中数据的存储

1 计算机中的数据

所谓的计算机,指的是对输入的数据进行运算和处理,并将数据处理结果进行存储和输出。

在计算中数据主要包含:

文件(文本、图像、音频、视频在计算机中都是以文件的方式进行存储);

数据库(依赖于文件);

计算机中数据的存储:

不管数据的具体表示形式是什么,都会以二进制编码(实质是由编码0和编码1构成的有序序列)形式进行存储。

2 计算机中的数据

2.1 数据进制

  1. 二进制编码

    二进制编码是计算机中数据的存储编码。

    每一位数据包含两种状态(0 和 1),整个数据在进行运算的过程中:逢二进一。

    编码特点:

    1. 读取具体位数值方便;

    2. 用户在访问读取整个数据值不方便。

    二进制编码形式其实质使用计算机思维对数据进行存储和运算。

  2. 十进制编码

    十进制编码是人在进行数据读写的时候所采用的编码形式。

    每一位数据包含(0 ~ 9)十种状态,整个数据在进行运算和存储过程中:逢十进一。

    编码特点:

    1. 用户在访问读取整个数据值方便;

    2. 读取具体位数值不方便。

    十进制编码形式其实质使用人的思维对数据进行存储和运算。

  3. 十六进制编码:

    每一位数据符号包含(0 ~ 9、a ~ f)十六种状态:

    每一个数据符号包含存储空间中的4bit位。

    整个数据在进行运算和存储过程中:逢十六进一。并且整个数据以符号(0x)标识。

    编码特点:同时方便于读取数据具体某位的状态,也方便读取整个数据。

    十六进制:更多是满足人以计算机思维分析数据。

  4. 八进制编码

    每一位数据符号包含(0 ~ 7)

    每一个数据符号值可以标识存储空间中3bit位的数值信息。

    整个数据在进行运算和存储过程中:逢八进一。并且整个数据以符号(0)标识。

2.2 数据进制之间的转换

2.2.1 十进制数据和二进制数据之间的转换
  1. 整型数据的转换

    a) 十进制转换为二进制

    ​ i. 将需要转换的数据对2取余数;

    ​ ii. 将需要转换的数据对2取整数;

    ​ iii. 判断整数是否为0,

    如果为0,余数编码倒序输出;否则重复当前过程。

    b) 二进制转换为十进制

    二进制位编码 * 2的位数次方,并累加;

  2. 小数的转换

    十进制转换为二进制编码

    ​ i. 将需要转换的数据乘以2取整数;

    ​ ii. 将需要转换的数据乘以2取整数;

    ​ iii. 判断小数是否为0

    如果小数为0,整数编码输出;否则重复当前过程。

2.2.2 十进制和八进制或者十六进制之间转换

针对于整型数据的转换:

  1. 将十进制转换为八进制或者十六进制

    具体实现可以有两种思路实现:

    1. 转换为二进制的思路一致,此时的除数变为进制数。

    2. 将十进制转换为二进制数,在由二进制数转换为八进制或者十六进制。

    使用数据位表示(八进制数:将二进制数的每3bit位分段实现;十六进制数:将二进制数的每4bit位分段实现)

  2. 八进制或者十六进制转换为十进制

    1. 使用二进制作为中间编码转换;

    2. 位编码*进制的位数次方 累加。

3 数据的存储编码

所有的数据在计算机中都是以二进制的形式存储,数据具体的存储编码:

  1. 正数:

    正数的存储是以数据的源码存储,而数据的源码也就是数据的二进制编码

    123:

    源码:0111 1011

  2. 负数

    负数的存储是以数据的补码存储,而负数的补码是正数部分的源码取反+1

    -123:

    123源码:0111 1011

    反码:1000 0100

    补码1000 0101

    -123的存储编码:1000 0101

  3. 如果两个数据相加和为-1,此时这两个数存储编码中的所有为均相反。

C语言中的关键字

所谓的关键字是在C语言中保留的具有特殊含义的标识符,一共有32个,可以将其分为4类

  1. 数据类型关键字:12个

    a) 基本数据类型关键字:

    整型:char、short、int、long

    浮点型:float、double

    符号:signed、unsigned

    b) 构造数据类型关键字:

    struct、union、enum

    c) 特殊数据类型关键字:

    void

  2. 存储类型关键字:4个

    auto、register、extern、static

  3. 控制语句关键字:12个

    a) 条件分支:if、else、switch、case、default

    b) 循环控制:for、while、do

    c) 特殊:break、continue、return、goto

  4. 其它特殊关键字:4个

    const、typedef、sizeof、volatile

C语言中的数据类型

在C语言中,所有的数据的定义,有数据存储类型修饰,包含基本数据类型和构造数据类型。

1 基本数据类型

基本数据类型:包含整型数据和浮点型数据,在分析数据的数据,主要通过数据类型所在内存空间的大小和值域范围考虑,值域范围是有内存空间的大小决定。

1.1 整型数据

整型数据包含:char、short、int、long

  1. char类型数据:

    在C语言中,char所占内存空间的大小为8bit位(1字节);根据是否存在符号位将数据分为无符号数和有符号数

    1. 无符号数:

    unsigned char

    所有的位都是数据位:一共8bit位。

    值域范围:0 ~ 255

    1. 有符号数:

    signed char

    数据的最高位为符号位(0 = 正数,1 = 负数),其余位为数据位(7位)。

    值域范围:-128 ~ 127

  2. short类型数据

    在C语言中,short所占内存空间大小为16bit位(2字节)

    unsigned short值域: 0 ~ 65535

    signed short值域: - 32768 ~ 32767

  3. int类型数据

    在16位系统中,所占内存空间大小为16bit位(2字节),32位或者64位系统中占用内存空间大小位32bit位(4字节)。

  4. long类型数据

    在32位系统中所占内存空间大小位32bit位,在64位系统中占用内存空间大小位64bit位(8字节)。

    #include <stdio.h>
    
    int main()
    {
        
            /* sizeof 计算数据类型数据所占内存空间的大小 */
            printf("%d\n", sizeof(char));        /* 输出char所占内存空间大小:1 */
            printf("%d\n", sizeof(short));
           /* 输出short所占内存空间大小:2 */
            printf("%d\n", sizeof(int));
             /* 输出int所占内存空间大小:4 */
            printf("%d\n", sizeof(long));        /* 输出int所占内存空间大小:8 */                 
    }
    

1.2 浮点型数据

C语言中浮点型数据包含:单精度浮点型数据float和双精度浮点型数据double

  1. float数据类型

    所占内存空间的大小为32bit位(4字节),根据是否存在符号位可以将其分为无符号float数据和有符号float数据。

    精度范围:保留小数点后6位数据。

    具体的存储:

    在这里插入图片描述

  2. double数据类型

    双精度浮点类型数据,所占内存空间的大小64bit位(8字节);存储编码格式

    【63】符号位:0 = 正数,1 = 负数

    【62:52】指数位:可以表示指数的值域范围为 -1023 ~ 1024 (指数位:移位+1023)

    【51:0】尾数:二进制编码中的小数部分;

2 数据常量

所谓的常量,指的是在数据运算和存储的过程中,其数据值不能被改变的量,称为数据常量。

2.1 基本常量

  1. 整型常量

    所谓的整型常量,指的是数据值为整型值的数据常量,称为整型常量。可以是使用八进制、十进制和十六进制表示。

    实例:123、0x7b、0173

  2. 字符常量

    所谓的字符常量,指的是常量值的存储空间为单个字节空间的数据常量,称为字符常量。可以整型数值和字符表示。

    字符使用单引号包含(字母、数值);存储的字符常量值的ASCII编码值。

  3. 字符串常量

    所谓的字符串常量,指的是使用双引号包含的连续多个字符所构成的常量,称为字符串常量。

    注意:字符串常量以字符’\0’结尾,可以不可见。

  4. 浮点型常量

    所谓的浮点型常量,指的是数据值为浮点型数据的常量,称为浮点型常量

    实例:3.1415926、1234.567

  5. 指数常量

    所谓的指数常量,实质是特殊的浮点型常量,对不浮点型常量的优势是可以表示很大或者很小的小数数据。

    指数常量语法格式:(+/-)ME(+/-)N

    第一个(+/-)符号:表示指数常量的数据的符号(+ = 正数,- = 负数);

    M:表示指数常量的底数部分,一般使用0-10范围内的浮点数据表示;

    E:特殊符号,表示10

    第二个(+/-)符号:表示指数部分的符号

    N:指数值

    12.34E-5

2.2 标识符常量

所谓的标识符常量,指的是使用标识符替换常量值,通过标识符直接访问,此时的标识符称为标识符常量。

2.2.1 语法规则

#define 标识符号 常量值或者常量表达式

注意:

标识符号:定义需要满足标识符的命名规则

  1. 由字母、下划线和数值构成,不能以数值开头

  2. 不能和关键字同名

  3. 严格区分大小写;

  4. 尽量做到见名知意的效果

  5. 在标识常量中一般使用大写表示。

常量值或者常量表达式:

  1. 可以是常量数据值;

  2. 常量表达式:在标识符替换过程中会导致和预想的结果不一致,在使用建议多使用括号明确表达式的含义。

  3. 还可以是语句块,类似于函数的功能,不涉及函数调用的入栈等操作。

标识符常量在程序预处理阶段做符号替换;

#include <stdio.h>

#define ONE 1                            /* 标识符是常量值 */
#define TOW     ((ONE)+(ONE))            /* 标识符是常量表达式 */

#define MUL     ((TOW) * (TOW))
#define T    ONE+ONE
#define MU   T*T        

int main()
{
        printf("%d\n", ONE);
        printf("%d\n", TOW);
        printf("%d\n", MUL);
       /* ((1)+(1))*((1)+(1)) 结果为4 */

        printf("%d\n", MU);        /* 1+1*1+1 结果为3 */
        return 0;
}
2.2.2 标识符作为语句块的替换
  1. 不带参数

    作为DEBUG调试语句。

    #include <stdio.h>
    /* 在C库中提供特殊的标识符
     * __FILE__        :值为所访问该标识的文件名称
     * __FUNCTION__    :值为所访问该标识符的函数名称,也可以使用标识 __func__ 访问
     * __LINE__        :值为所访问该标识的所的行序号
     */
        
    /* DEBUG调试:TEST标识符常量的为0的时候关闭调试;非0开启调试 */
    #define TEST 0
    #if TEST
    #define DEBUG printf("%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__)
    #else
    #define DEBUG
    #endif
    
    int main()
    {
            printf("-----------------%d----------------------\n", __LINE__);
            DEBUG;
            printf("-----------------%d----------------------\n", __LINE__);
            printf("-----------------%d----------------------\n", __LINE__);
            printf("-----------------%d----------------------\n", __LINE__);
            printf("-----------------%d----------------------\n", __LINE__);
            printf("-----------------%d----------------------\n", __LINE__);
            DEBUG;
            printf("-----------------%d----------------------\n", __LINE__);
            printf("-----------------%d----------------------\n", __LINE__);
            printf("-----------------%d----------------------\n", __LINE__);
            DEBUG;
            printf("-----------------%d----------------------\n", __LINE__);
    }
    
    
  2. 带参数:

    也称为宏函数。此时的宏函数从功能上和函数一样,区别与函数。

    //#define MUL(a, b) ((a)*(b))
    #define MUL(a, b) a*b
    
    int main()
    {
            printf("%d\n", MUL(3+4, 4+5));        /* 使用前者输出64, 后者输出24 */
    }
    

    练习:

    题目: 1个水分子的质量约为3.0×10−23 克。1夸脱水大约是950克。编写一个程序,计算1夸脱水分子中的数量、使用常量表达式实现。

    #include <stdio.h>
    
    #define M 3.0E-23
    #define K 950
    
    int main()
    {
            printf("%e\n", K/M);            
         
            return 0;
    }
    
    

3 数据变量

所谓的数据变量,指的是在数据运算和存储的过程中,数据值发生改变的量称为数据变量,当数据变量定义成功,此时在内存中开辟数据类型存储空间存储。

3.1 变量定义

  1. 变量定义的语法规则:

    存储类型 数据类型 变量名称;

    a) 存储类型:变量的存储属性,有auto、register、extern、static。

    b) 数据类型:变量数据类型,数据变量存储空间的大小;

    数据类型可以是基本数据类型,也可以构造数据类型

    c) 变量名称:提供后续对变量的访问,变量名的定义需要满足标识符命名的语法规则。

  2. 变量可以是局部变量和全局变量

    所谓的局部变量:指的是在函数或者具体某个模块内部定义的变量,称为局部变量;此时只能在定义模块内访问;

    所谓的全局变量:指的是在函数外部定义的变量,称为全局变量;此时可以任意位置访问。

  3. 变量定义初始化

    a) 可以设置变量初始值,此时变量的初始值为设定值;

    b) 也可以不设置初始值,此时变量的初始值和变量存储属性有关。

    静态存储类型变量(全局变量和static修饰的局部变量):默认初始值为零值;

    register寄存器变量和auto自动存储类型变量:默认初始值为随机值。

    c) 变量初始值设置的时候,使用相同数据类型的数据值设置。

3.2 数据存储类型

3.2.1 内存分布

程序在系统中运行的时候,在32位系统构建4G虚拟内存实现程序中数据的存储。

在这里插入图片描述

3.2.2 存储类型关键字
  1. auto存储类型

    所谓的auto存储类型,也称为自动存储类型。

    只能用来修饰局部变量,在默认情况下,未被其它存储类型关键字修饰的局部变量,均为auto自动存储类型变量。

    auto存储类型变量存储位置为栈区;生命周期为语句执行到模块结束;作用域(也称为链接属性)为模块域(只能在当前定义模块内部访问)。

    auto存储类型的变量如果在定义的时候未设置初始值,则默认初始值未随机值。

    #include <stdio.h>
    
    int c;
    //auto int d;        /* 语法错误:auto只能修饰局部变量,不能修饰全局 */
    
    int main()
    {
            int a = 3;
            auto int b;
            int num;
    
            printf("a = %d, b = %d, num = %d\n", a, b, num);
    }
    
  2. register存储类型

    所谓的register存储类型,所修饰的变量为寄存器变量。

    对于寄存器变量存储在寄存器中,不存储在内存中,不能取地址运算;

    寄存器变量只能修饰局部变量;

    寄存器变量变量访问的效率更高;

    寄存器的数量有限,只能定义有限数量的寄存器变量。如果寄存器变量定义失败,此时自动为auto存储类型变量。

    #include <stdio.h>
    
    register int a = 3;    /* 语法错误:register不能修饰全局变量 */
    
    int main()
    {
            register int b = 4;
            int c = 5;
    
            printf("b = %d, &b = %p\n", b, &b);
            printf("c = %d, &c = %p\n", c, &c);        /* register变量存储在寄存器中,不能取地址运算 */
    }
    
  3. extern存储类型

    所谓的extern存储类型,称为外部引用类型;

    不用于变量变量的定义,是用于对全局变量和函数进行外部引用声明使用。

    在外部声明引用的时候:告诉编译器变量或者函数在其它位置有做定义,在当前位置不需要关心定义,引用之后可以直接使用。

    extem.c源文件

    int a = 123;        /* 定义全局变量 */
    

    main.c源文件

    extern int a;                                /* 对全局变量a的外部声明引用 */
    extern int printf(const char *format, ...);  /* 对printf函数的外部声明引用 */
    
    int main()
    {
            printf("a = %d\n", a); 
    }
    
  4. static存储类型

    所谓的static存储类型所修饰的变量为静态变量,其存储区间为静态存储区(数据段)。

    static可以修饰局部变量和全局变量

    a) static修饰局部变量

    改变变量的存储位置和生命周期:

    未被static修饰的局部变量,其存储位置为栈区,生命周期为语句执行到模块结束;

    被static修饰的局部变量,其存储位置为静态存储器,生命周期为程序执行到程序结束。

    变量在重复定义的时候,不会每次进行空间的开辟,并且访问的结果为上一次修改后的结果。

    #include <stdio.h>
    #include <unistd.h>
    
    int main()
    {
            while(1) {
                    static int a = 1;            /* 只会开辟1次空间,不会每次执行都开辟空间 */
                    printf("a = %d\n", a);       /* 当前访问的上一次修改后的数据 */
                    a++;
                    sleep(1);
            }
    }
    
    /* 输出结果:a 的值每次递增1 */
    

    b) static修饰全局变量和函数

    改变变量的作用域(链接属性)

    未被static修饰的全局变量和函数,作用域为整个程序域,在其它文件中使用extern进行外部声明引用访问。

    被static修饰的全局变量和函数,作用域为文件域,只能在当前所定义的文件中访问。

    函数和全部变量定义的源文件

    #include <stdio.h>
    
    static int a = 123;        /* 全局变量使用static修饰,此时作用域为当前文件域,不能在其它文件中做外部声明引用 */
    int b = 234;
    
    void test()
    {
            printf("test\n");
    }
    /* 全局函数使用static修饰,此时作用域为当前文件域,不能在其它文件中做外部声明引用 */
    static void static_test()
    {
            printf("static test\n");
    }
    

    函数和变量使用的源文件

    #include <stdio.h>
    
    extern int a;    /* 语法错误:外部声明引用失败 */
    extern int b;
        
    extern void test();
    extern void static_test();    /* 语法错误:外部声明引用失败 */
    
    int main()
    {
            printf("a = %d\n", a); 
            printf("b = %d\n", b); 
            test();
            static_test();
    }
    

3.3 数据类型之间的转换

在数据的运算和处理的过程中,可能涉及到不同数据类型数据之间的运算。此时涉及不同数据类型之间数据的强制转换。

所谓的数据类型之间的强制转换,实质是对内存编码数据的表示形式进制转换。在数据强制转换过程中不会改变数据在内存中存储编码,只会改变读取内存空间的大小和数据的表示数据类型形式。

数据类型的强制转换主要包含两种方式:

  1. 隐式规则转换

    所谓的隐式规则转换,也称为自动转换,具体的转换规则由编译器按照默认的隐式规则实现不同数据类型之间的转换

    1. 不同数据类型所占空间大小:将小数据类型向大数据类型转换(char --> short --> int --> long)

    2. 相同数据类型有无符号:将有符号数据转换为无符号数据(signed --> unsigned)

    3. 不同精度数据类型:将低精度数据类型转换高精度数据类型 (float --> double 、long --> double )

    4. 在使用赋值运算符的时候,将右值数据类型转换左值数据类型。

    在这里插入图片描述

    #include <stdio.h>
    
    int main()
    {
            int a = 3;
            int b;
            float f = 3.456;
    
            a = a+f;        /* 数据类型转换:将a和f转换为double数据类型数据相加;在将相加后的double数据转换为int类型赋值给变量a */
            b = f;
            printf("a = %d, b = %d\n", a, b);     /* a = 6, b = 3 */  
            
            a = 3;
            f = 4.567;
            printf("%d\n", a+f);        /* 1 */
            printf("%f\n", a+f);        /* 7.567000 */
            a = a+f;       
            b = f;
            printf("a = %d, b = %d\n", a, b);        /* a = 7, b = 4 */                                                  
    }
    
    

    注意:在隐式规则转换的时候,不同编译器可能存在不同的转换规则;不同的应用场景转换规则也可能不同。

  2. 显示规则转换

    实质是打破原有默认数据类型转换的规则,而由设计者按照实际的需求实现不同数据类型之间数据的类型转换。

    显示转换规则语法:

    (类型数据符)(表达式);

    类型数据符 :数据类型转换过程中的目标数据类型;

    表达式:需要转换的源数据表达式,可以单个符号,也可以是复杂表达式。

    #include <stdio.h>
    
    int main()
    {
            int a = 3;
            float f = 3.456;
    
            printf("%f\n", (float)(a+f));        /* 显示转换规则:6.456 */
            printf("%f\n", a+f);                 /* 隐式转换规则:6.456 */
            printf("%d\n", (int)(a+f));          /* 显示转换规则:6 */
            printf("%d\n", a+f);                 /* 隐式转换规则:1 */
    
    }
    

    注意:由于显示规则会打断默认隐式转换规则,所以在处理过程中有可能出现不同的结果。

  3. 注意事项

    a) 在数据类型的强制转换过程中,不会改变数据在内存中原有存储编码,只会改变数据的输出数据类型和数据结果;

    b) 在隐式转换规则是由编译系统设定,转换规则不明确,最终处理结果不确定,谨慎使用。

    c) 在隐式转换规则不明确的情况,尽可能使用显示转换规则实现。

C语言运算符

在C语言中,不仅提供了多种数据类型的数据,还提供多种运算符。

1 算术运算符:

在C语言中的算术运算符,主要针对整型数据和浮点型数据的算术运算,其中包含:+、-、*、/、%、++、–

  1. +、-、*

    可以支持整型或者浮点型数据的运算。

  2. /

    a) 操作数都为整数的时候,取整数运算符;

    b) 操作数其中一个为浮点型数据,除法运算符,实质将两个浮点型数据进行除法运算。

  3. %

    操作数只能是整数才有意义,取余数运算符。

  4. ++、–

    自加(1)、自减(1)运算符

    ++:

    前加加(++a):先将变量a的值自加1(a = a+1),在取变量a的值(a作为右值赋值给左值);

    后加加(a++):在取变量a的值(a作为右值赋值给左值),在将变量a的值自加1(a = a+1).

            int a = 1;
            int b;
            b = a++;        /* 先取a的值为1赋值给b(b = 1),在自加1(a = a+1),此时a=2 */
            printf("a = %d, b = %d\n", a, b);       //a = 2, b = 1
            b = ++a;        /* a先自加1(a = a+1),此时a=3;在取a的值3赋值给b(b=3) */          
            printf("a = %d, b = %d\n", a, b);       //a = 3, b = 3
    

    –:

    前减减(–a)

    后减减(a–)

2 关系运算符和逻辑运算符

2.1 bool类型数据

在标准C库中未提供bool数据类型,在外部有提供:

bool数据类型,是一种特殊的数据类型,值域只有只有true(真)和false(假)两种状态值。

在使用的时候,需要包含数据类型bool及其值true和false 所定义的头文件:#include <stdbool.h>

#include <stdio.h>
#include <stdbool.h>

int main()
{
        bool mybool = false;
        if(mybool) {
                printf("mybool is true\n");
        } else {
                printf("mybool is false\n");
        }   
}

2.2 关系运算符和逻辑运算符

对于关系运算符和逻辑运算符,运算结果只有两种结果:分别为true(真、非零值)和false(假、零值);

  1. 关系运算符:

    常用的关系运算符:>、>=、<、<=、==、!=

    int a = 5;
    int b = 4;
    a > b        /* 结果为:true */
    a >= b        /* 结果为:true */
    a < b        /* 结果为:flase */
    a <= b        /* 结果为:false */   
    a == b        /* 结果为:false */
    a != b        /* 结果为:true */
    
  2. 逻辑运算符:

    a) !:逻辑非

    输入为true输出为false;否则输入为false输出为true

    b) &&:逻辑与

    语法格式:表达式1 && 表达式2

    输出结果:当两个表达式都为true,其结果为true;否则只要有一个为false,其结果为false;

    表达式1为false的时候,表达式2不在执行;

    c) ||:逻辑或

    语法格式:表达式1 || 表达式2

    输出结果:当两个表达式都为false,其结果为false;否则只要有一个为true,其结果为true;

    表达式1为true的时候,表达式2不在执行;

    int a = 5;
    int b = 4;
    !b;        /* 结果为false */
    a++ && b++        /* 结果为true,a = 6, b = 5 */
    a++ || b++;       /* 结果为true,a = 7, b = 5 */        
    

3 位运算符

所谓的位运算,实质是对数据的具体某位或者某些进行运算,包括符号位也参与位运算。其中位运算符包含:

  1. ~

    按位取反运算符:将数据的所有位取反

    位为1,取反结果为0;否则位为0,取反结果为1。

    123 的二进制编码:0111 1011

    ~123 的二进制编码,将123的二进制编码所有位取反,包括符号,其结果:1000 0100

  2. &

    按位相与运算符:将两个数据的位相与

    对应位都为1,结果为1;否则其结果为0;

    常用于将数据的指定的某位或者某些位清零运算

    0x34 & 0x56

    0x34的二进制编码:0011 0100 ,

    0x56的二进制编码:0101 0110;

    按位相与的编码: 0001 0100

  3. |

    按位相或运算符,将两个数据的位相或

    对应位都为0,结果为0;否则其结果为1;

    常用于将数据的指定的某位或者某些位置位(1)运算。

    0x34 | 0x56

    0x34 的二进制编码:0011 0100

    0x56 的二进制编码:0101 0110

    按位相或的编码: 0111 0110

  4. ^

    按位异或运算符,

    实质是对应位不同则或运算,结果为1,否则其结果为0;

    常用于统计两个数编码相同和不同的位。

    异或结果中1 = 不同的位,0 = 相同的位。

    0x34 ^ 0x56

    0x34 的二进制编码:0011 0100

    0x56 的二进制编码:0101 0110

    按位异或的编码:0110 0010

  5. 移位运算符

    将数据编码的位向左或者先右顺序移位;

    a) <<

    左移位运算符,

    语法格式:data << n其中data表示需要移位的操作,n表示左移位的位数。

    将数据data的二进制编码,整体向左移动n位;左边的n位溢出,右边的n位空出(空出的位使用0填充)

    0x34 << 3

    0x34 的二进制编码: …… 0000 0011 0100

    移位后的编码:…… 0001 1010 0000

    b) >>

    右移位运算符

    语法格式:data >> n 其中data表示需要移位的操作,n表示右移位的位数

    在右移位的时候:分为算术右移和逻辑右移

    逻辑右移:将数据data的二进制编码,整体向右移动n位;右边的n位溢出,左边的n位空出,空出的位使用0填充;

    算术右移:将数据data的二进制编码,整体向右移动n位;右边的n位溢出,左边的n位空出,空出的位使用符号位(负数 = 1,正数 = 0)填充;

    注意:设计者不能决定具体采用算术右移还是逻辑右移,由编译运行系统决定。

    -0x34 >> 3

    -0x34的存储编码(补码): 1111 1111 1111 1111 1111 1111 1100 1100

    逻辑右移:左边的位使用0补充 0001 1111 1111 1111 1111 1111 1111 1001 十六进制:0x1ffffff9

    算术右移:左边的位使用符号位(1)补充1111 1111 1111 1111 1111 1111 1111 1001 十六进制:0xfffffff9

    练习:

    1. 将某一个数据a的第7,14, 25 位清零;

    a = a & ( ~(0x1 << 7)) & (~(0x1 << 14)) & (~(0x1 << 25));

    1. 将某一个数据a的第9,18,20位置位;

    a = a | (0x1 << 9) | (0x1 << 18) | (0x1 << 20);

    1. 将某一个数据a的[22:15]设置为0x35;

    思路:

    向将需要设置值的连续多个位清零;

    a = a & (~(0xff << 15));

    设置需要设置的连续多个位设置数据值

    a = a | (0x35 << 15);

4 赋值运算符和复合赋值运算

  1. 赋值运算符

    =: 表示的将右值(等号右边表达式)的处理结果赋值给左值(等号左边表达式)。

    左值部分不能是常量,只能是变量;

    右部分既可以是常量,也可以是变量。

    a = b+c;# a 为左值,b+c 为右值

  2. 复合赋值运算符

    a) 算术复合赋值运算符:

    +=、-=、*=、/=、%=不会改变算术运算符本身的含义。

    a += b; 等价于:a = a+b;

    b) 位运算复合赋值运算符

    |=、&=、^=、=>>、=<<

    a |= 3;等价于:a = a | 3;

    a =>> 3;等价于:a = a >> 3;

5 三目运算符(?:)

在C语言中,提供了唯一的三目运算符(?:),也称为条件运算符。

具体的语法格式:

条件表达式 ? 表达式1 : 表达式2;

执行过程:

执行条件表达式,对结果进行判断

如果结果为true(真),则执行表达式1;否则结果为false(假),则执行表达式2.

#include <stdio.h>

int main()
{
        int a = 3;
        int b = 5;

        a > b ? a++ : b++;      /* 由于(a = 3) > (b = 5) 不成立,执行表达式2(b++); 执行结束结果:a = 3, b = 6 */
        printf("a = %d, b = %d\n", a, b); 

        a < b ? a++ : b++;     /* 由于(a = 3) < (b = 6) 成立,执行表达式1(a++); 执行结束结果:a = 4, b = 6 */
        printf("a = %d, b = %d\n", a, b); 
}

6 逗号运算符

所谓的逗号运算符,指的是在一个圆括号中的多个表达式,使用逗号分隔,此时的多个表达式从左到右顺序依次执行,最终结果由最后一个表达式决定。

格式:(表达式1, 表达式2, 表达式3);

#include <stdio.h>

int main()
{
        int a = 3;
        int b = 4;
        int c = 5;
        int d;
    
        d = (++a, ++b, c++, a+c);        /* 将括号中表达式顺序执行,并将最后一个表达式的结果赋值给变量d */
        printf("a = %d, b = %d, c = %d, d = %d\n", a, b, c, d);     /* a =4, b = 5, c = 6, d = 10 */
}

7 sizeof运算符

sizeof是一个特殊的关键字,运算符关键字,用于计算数据所占内存空间的大小。

格式:

sizeof(数据);

其中数据可以是常量、变量或者数据类型;

在32位系统中运算结果返回数据类型为unsigned int 数据类型;

在64位系统中运算结果返回数据类型为 unsigned long 数据类型;

#include <stdio.h>

int main()
{
        int a;
        printf("%ld\n", sizeof(3));        /* 计算常量所占用内存空间的大小 */
        printf("%ld\n", sizeof("hello"));  /* 如果常量为字符串常量,计算整个字符串所占内存空间的大小,注意包含字符串结束符\0,此处的输出结果为6 */

        printf("%ld\n", sizeof(a));        /* 计算变量所占用内存空间的大小 */
        printf("%ld\n", sizeof(char));     /* 计算数据类型的数据所占用内存空间大小 */
        printf("%ld\n", sizeof(short));
        printf("%ld\n", sizeof(long));
        printf("%ld\n", sizeof(double));     
}

8 运算的优先级

在C语言中,除了前面所提到运算符之外

  1. 数组包含数组元素访问运算符[]

  2. 指针包含取地址访问运算符&和指针解引用访问运算符*

  3. 结构体和共用体数据变量包含成员访问运算符**.**,和结构体和共用体指针包含成员访问运算符->

  4. 数据类型显示规则转换,有提高类型转换运算符(t)

……

在运算符种类很多的情况,对于表达式的运算,注意运算优先级的处理,也就需要注意表达式中运算符的优先级判断。

在这里插入图片描述

在这里插入图片描述

在做表达式的运算过程中,按照运算符的优先级进行运算:

  1. 在相同优先级情况下:从左向右依次运算;

  2. 在不同优先级的运算中:先运行高优先级,在运行底优先级

运算符的优先级,一般满足以下规则:

  1. 单目运算符优先级高于双目运算符优先级;

  2. 非、算、关、与或、赋;对于绝大多数的运算符满足的规则。

  3. 在优先级,不确定的情况下,可以多使用圆括号改变实现表达式,以便于明确表达式的运行优先级。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值