0
点赞
收藏
分享

微信扫一扫

嵌入式C语言复习——Day2

西特张 2022-01-13 阅读 61

嵌入式C语言复习——Day2

C语言关键字及运算符操作

32个关键字

  1. 杂项:sizeof、return

    #include<stdio.h>
    
    int main()
    {
        int a;
        printf("the a is %d\n", sizeof(a));
        return 0;//输出为4
    }
    

    sizeof:编译器给我们查看内存空间容量的一个工具
    return:返回值

  2. 数据类型:int, char, long, short, unsigned, signed, float, double, void
    c语言操作对象:内存
    c语言通过数据类型关键字描述内存大小
    short、int、long、char、float、double 这六个关键字代表C 语言里的六种基本数据类型。
    在不同的系统或编译器上,这些类型占据的字节长度是不同的:
    8bit = 1B
    在32 位的系统上:
    short 占据的内存大小是2 个byte;
    int占据的内存大小是4个byte;
    long占据的内存大小是4个byte;
    float占据的内存大小是4个byte;
    double占据的内存大小是8个byte;
    char占据的内存大小是1个byte;//硬件处理的最小单位8bit ,1个字节
    具体可以用sizeof测试
    在这里插入图片描述
    int根据具体硬件决定
    编译器最优的处理大小:系统一个周期,所能接受最大处理单位
    32位系统 32bit 4B int
    16位系统 16bit 2B int
    2B数的表示范围为65536
    进制表示
    十进制、八进制、十六进制、二进制
    3bit——8进制 111——0x7 1000——0x8
    int a = 010 //a为8
    4bit——16进制 int a = 0x10//a为16

    unsigned无符号:多表示数据
    signed有符号:多表示数字
    char a = -1; //0xff 补码
    a>>1//a右移 符号位仍然存在
    unsigned char b = -1
    b>>1//b右移,可以变成0x00

    浮点运算float、double
    float 4B double 8B
    浮点型常量
    1.0 1.1 double
    1.0f float

    格式化输出语句格式化输出语句
    void 语义符

    自定义数据类型:struct、union、enum、typedef
    自定义 = 基本元素集合

    结构体struct 元素之间的和

     struct myabc{
    	unsigned int a;
    	unsigned int b;
    	unsigned int c;
    	unsigned int d;
    }; //告诉编译器自己定义一个内存组织,四个unsigned int的集合
    struct myabc mybuf;//对结构体变量化,实例一个变量
    

    结构体顺序有要求;

    共用体 union 共用起始地址的一段内容,技巧型代码

    union myabc{
    	char a;
    	int b;
    }; //共用体声明
    union myabc abc;//使用
    

    enum 枚举 被命名的整形常数的集合
    定义常数例如:

    #define mon 0
    #define tue 1
    #define wed 2
    enum abc{mon, tue, wed};//效果同上,不定义的话默认为0,1,2
    
    

    enum 枚举名称{常量列表};
    例如

    enum abc{MOD = 100, TUE, WED};
    int main()
    {
    	enum abc a1;
    	printf("the a1 is %lu\n",sizeof(a1));
    	printf("the %d\n",WED);
    	return 0;
    }
    

    typedef 数据类型的别名
    可以自己定义数据类型的名字,提高代码可读性
    例如:int a = 100; //a是一个int类型的变量
    typedef int a; //a是一个int类型的外号
    typedef usigned int u16;
    通常定义为xxx_t;

  3. 逻辑结构关键字:
    两大结构:分支和循环
    if、else 条件

    if(条件表达式)
    	xxx;
    else
    	xxx;
    

    switch、case、default 多分支

    switch(整型数字)
    switch(a)
    {
    	case 1:
    		break;
    	case 2:
    		break;
    }
    

    do、while、for 循环
    for:次数

    	for(表达式1;表达式2;表达式3)
    	{
    		执行代码块
    	}
    

    执行表达式1,对循环变量做初始化;
    判断表达式2,若其值为真(非0),则执行for循环体中执行代码块,然后向下执行;若其值为假(0),则结束循环;执行表达式3,(i++)等对于循环变量进行操作的语句;执行for循环中执行代码块后执行第二步;第一步初始化只会执行一次。
    循环结束,程序继续向下执行。

    while:条件,当值为真(非0)时, 执行循环体代码块。

    	while(表达式)
    	{
    		执行代码块
    	}
    

    do:先执行再判断条件,它先执行循环中的执行代码块,然后再判断while中表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while循环至少要执行一次循环语句。

    	do{
    		执行代码块
    	}while(表达式); 
    

    continue、break、goto
    goto只能在同一函数下跳转

  4. 对于资源属性中位置的限定关键词
    auto、register、static、const、extern、volatile
    C语言根据变量的生存周期来划分,可以分为静态存储方式和动态存储方式。
    静态存储方式:是指在程序运行期间分配固定的存储空间的方式。静态存储区中存放了在整个程序执行过程中都存在的变量,如全局变量。
    动态存储方式:是指在程序运行期间根据需要进行动态的分配存储空间的方式。动态存储区中存放的变量是根据程序运行的需要而建立和释放的,通常包括:函数形式参数;自动变量;函数调用时的现场保护和返回地址等。

    auto 默认情况,可省略,属于动态存储方式,可读可写
    register 定义一些快速访问的变量,编译器会尽量安排CPU的寄存器去存放变量,若寄存器不足,变量还是放在存储器中
    register int a;// 限制变量定义在寄存器上的修饰符
    &取地址这个符号对register不起作用

    int main()
    {
    	register int a;
    	a = 0x10;
    	printf("the a is %d\n",a);
    	return 0;
    }
    

    static 静态
    应用场景:修饰3种数据
    1)函数内部变量
    普通局部变量的值在初始时是不确定的,除非对其显式赋值。普通局部变量存储于进程栈空间,使用完毕会立即释放。
    静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。变量在全局数据区分配内存空间;编译器自动对其初始化
    其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束

    #include <stdio.h>
    
    void fn(void)
    {
        int n = 10;
    
        printf("n=%d\n", n);
        n++;
        printf("n++=%d\n", n);
    }
    
    void fn_static(void)
    {
        static int n = 10;
    
        printf("static n=%d\n", n);
        n++;
        printf("n++=%d\n", n);
    }
    
    int main(void)
    {
        fn();
        printf("--------------------\n");
        fn_static();
        printf("--------------------\n");
        fn();
        printf("--------------------\n");
        fn_static();
    
        return 0;
    }
    

    static定义的变量在全局数据区分配内存空间,只有定义它的函数结束作用域才结束,不会自己释放,普通变量位于栈区,由系统自动分配内存,使用完毕立即释放

    2)函数外部的变量
    全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。
    普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。
    静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。
    file1.h如下:

    #include <stdio.h>
    void printStr();
    

    在file1.c中定义一个静态全局变量hello, 供file1.c中的函数printStr访问

    #include "file1.h"
     
    static char* hello = "hello cobing!";
     
    void printStr()
    {
    	printf("%s\n", hello);
    }
    

    file2.c是我们的主程序所在文件,file2.c中如果引用hello会编译出错

    #include "file1.h"
     
    int main()
    {
    	printStr();
    	return 0;
    }
    

    上面的例子中,file1.c中的hello就是一个静态全局变量,它可以被同一文件中的printStr调用,但是不能被不同源文件中的file2.c调用。只在定义它的源文件内有效,其他源文件无法访问它

    3)函数的修饰符
    函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:
    静态函数只能在声明它的文件中可见,其他文件不能引用该函数,不同的文件可以使用相同名字的静态函数,互不影响

    /* file1.c */
    #include <stdio.h>
    
    static void fun(void)
    {
        printf("hello from fun.\n");
    }
    
    int main(void)
    {
        fun();
        fun1(); //无法访问fun1
    
        return 0;
    }
    
    /* file2.c */
    #include <stdio.h>
    
    static void fun1(void)
    {
        printf("hello from static fun1.\n");
    }
    

    static函数可以很好地解决不同原文件中函数同名的问题,因为一个源文件对于其他源文件中的static函数是不可见的。

    extern:在一个文件中引用另一个文件中定义的变量或者函数
    1)引用同一个文件中的变量

    #include<stdio.h>
    
    int func();
    
    int main()
    {
        func(); //变量num在func函数中是可以正常使用,因为func对num的调用是发生在num的声明和初始化之后
        extern int num;// 需要使用extern告诉编译器变量存在
        printf("%d",num); //2
        return 0;
    }
    
    int num = 3;
    
    int func()
    {
        printf("%d\n",num);
    }
    

    2)引用另一个文件中的变量
    extern这个关键字的真正的作用是引用不在同一个文件中的变量或者函数
    main.c

    #include<stdio.h>
    int main()
    {
        extern int num;
        printf("%d",num);
        return 0;
    }
    

    b.c

    #include<stdio.h>
    int num = 5;
    void func()
    {
        printf("fun in a.c");
    }
    

    b.c中定义了一个变量num,如果main.c中想要引用这个变量,那么可以使用extern这个关键字,注意这里能成功引用的原因是,num这个关键字在b.c中是一个全局变量,也就是说只有当一个变量是一个全局变量时,extern变量才会起作用

    3)引用另一个文件中的函数
    extern除了引用另一个文件中的变量外,还可以引用另一个文件中的函数,引用方法和引用变量相似

    const:只读的变量
    1)修饰局部变量

    const int n=5;
    int const n=5;
    

    都表示变量n的值不能被改变,需要注意的是,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了。
    2)常量指针与指针常量
    常量指针是指针指向的内容是常量,可以有一下两种定义方式

    const int * n;
    int const * n;
    

    需要注意的是一下两点:
    1、常量指针说的是不能通过这个指针改变变量的值,但是还是可以通过其他的引用来改变变量的值的。

    int a = 5;
    const int *n = &a;
    a=6;
    

    2、常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变,常量指针可以指向其他的地址。

    int a=5;
    int b=6;
    const int* n=&a;
    n=&b;
    

    指针常量是指指针本身是个常量,不能在指向其他的地址

    int *const n;
    

    需要注意的是,指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向改地址的指针来修改。

    int a=5;
    int *p = &a;
    int* const n = &a;
    *p=8;
    

    3)修饰函数的参数
    1、防止修改指针指向的内容
    2、防止修改指针指向的地址

    volatile:告知编译器编译方法的关键字,不优化编译
    volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。volatile 提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如 果没有 volatile 关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。所以遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
    一般说来,volatile用在如下的几个地方:
    中断服务程序中修改的供其它程序检测的变量需要加volatile;
    多任务环境下各任务间共享的标志应该加volatile;
    存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

举报

相关推荐

0 条评论