嵌入式学习-C语言-day07

一、修饰变量的关键字

1.auto自动变量

总结:auto可以看成是局部变量。auto是旧版C语言用来表示局部变量的。如果那些局部变量[没有显式地使用存储类别说明符(也就是数据类型)来修饰],那么将默认使用 auto 存储类别说明符。在现代的 C 语言编译器中,不必显式使用 auto 关键字,因为局部变量的存储类别默认就是 auto。       

下面有一些详解: 

只使用auto修饰变量,变量的类型默认为整型

自动变量也称局部变量。

将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。所有局部变量默认都是auto,一般省略不写。
auto声明全局变量时,编译会出错

       声明局部变量时,正常运行

#include <stdio.h>
int main() {
    //num1为整型
    auto int num1;
    
    //num2为整型(当不写数据类型,直接使用auto修饰变量的时候,变量为整型)
    auto num2;
    return 0;
}

2.static 静态变量

修饰的对象有:局部变量、全局变量、函数

修饰局部变量,改变局部变量的存储位置

        如果未初始化存储到bss段,并且值为0

        如果已初始化了在data段

        生存周期:局部静态变量的生存周期到整个进程结束。

        作用域:在本函数内

        ps:可能有人疑问,生存周期和作用域的区别

                以下面代码为例,

                当第一次调用test()函数,静态局部变量num1被赋初始值,局部变量num2也被赋初始值,num1、num2自增,输出的结果为:num1 = 11, num2 = 11

                当第二次调用test()函数,静态局部变量num1不再赋初始值,局部变量num2被赋初始值,num1、num2自增,输出的结果为:num1 = 12, num2 = 11

                当第三次调用test()函数,静态局部变量num1不再赋初始值,局部变量num2被赋初始值,num1、num2自增,输出的结果为:num1 = 13, num2 = 11

void test()
{
    static int num1 = 10;
    int num2 = 10;
    
    num1++;
    num2++;
    printf("num1 = %d, num2 = %d", num1, num2);
}

        也就是说,局部静态变量只在函数里初始化一次,然后值将被保存,函数调用结束,局部静态变量的值将被保存,不会被销毁。之后调用该函数,将不再对局部静态变量初始化。

如果修饰全局变量,那么改变全局变量的作用域到本文件

        使用static修饰全局变量,改变的只有作用域,也就是只能在本文件中使用

如果修饰函数,那么也会改变这个函数的调用权限,只能在本文件中调用了

        使用static修饰函数,改变的只有作用域,也就是只能在本文件中使用

3.const 只读变量

限制其变量的值不能再通过变量名来修改

4.volatile易失变量

防止编译器过度优化

volatile 关键字修饰变量的时候,告诉编译器该变量的值可能会被意外地修改,因此需要在每次使用该变量时都重新读取它的值,而不是使用缓存中的旧值。

这种被volatile关键字修饰的变量称为易失变量。

如果不使用 volatile 关键字修饰变量,在多线程或多进程的情况下,变量的值可能被其他线程或进程修改,如果此时你读取被修改的变量,读取的可能是没有改变时候的量。

5.register 寄存器变量

普通变量都存储在内存,寄存器变量存储在寄存器中,寄存器相对于内存效率要更高,但是寄存 器有限的。

register 关键字用来提高提高变量的访问速度。

使用 register 关键字声明的变量具有以下特性:

  1. register 关键字只是一个建议:register 关键字只是向编译器发出的一个建议,告诉它将该变量存储在寄存器中。编译器是否采纳这个建议取决于编译器本身以及当前的编译环境。

  2. 无法获取寄存器变量的地址:由于寄存器是位于 CPU 内部的一种特殊存储区域,因此无法获取寄存器变量的地址。因此,对 register 变量使用 & 运算符取地址时会导致错误。

  3. 可能在栈上分配内存:尽管使用 register 关键字可能会将变量存储在寄存器中,但编译器不一定遵循这个建议。如果编译器认为将变量存储在寄存器中没有明显的性能优势,或者寄存器数量不足,它可能会选择在栈上分配内存。

  4. 并非所有类型的变量都适合作为寄存器变量:通常,较小的基本类型(如 intcharshort)或指针类型的变量适合作为寄存器变量,而较大的结构体或数组则不太适合。因此,对于较大的复杂类型,编译器可能会忽略 register 关键字。

由于现代编译器在优化代码方面已经非常强大,能够自动进行寄存器分配和优化,在大多数情况下,手动使用 register 关键字并不会带来明显的性能改进。因此,现代编程中很少使用 register 关键字,而是依赖编译器的自动优化。

6.extern外部变量

主要用于函数或者是全局变量的外部(其他文件中)声明

  1. extern 只是一个声明,而非定义:extern 关键字只是向编译器声明变量的存在和类型,而不是对变量进行实际的分配空间。因此,在使用 extern 关键字声明外部变量时,不会为该变量分配内存。(另一个源文件调用extern修饰的变量时,不会为变量再次分配内存)

  2. 外部变量的定义:要使用 extern 关键字声明的变量,必须在另一个源文件中进行定义。在另一个源文件中,应该使用正常的方式来定义该全局变量。

  3. 外部变量的链接性:通过 extern 声明的外部变量具有外部链接性,这意味着它可以被其他源文件访问。如果希望限制外部变量的可见性,可以使用 static 关键字将其声明为静态变量,使其具有内部链接性

 二、#define与typedef的异同

相同点:

  1. 都可以在代码中自定义名称。
  2. 都是在编译前的预处理阶段,进行的代码替换。

不同点:

替换的程度不一样,#define只是进行简单的代码替换,typedef是创建的类型的别名

//例如
#define INT_1 int *
typedef INT_2 int *

int main()
{
    INT_1 num1, num2;    //#define的定义:int * num1; int num2;
    INT_2 num3, num4;    //typedef的定义:int * num3; int *num4;
    return 0;
}

三、C的存储空间布局

  1. 内核段
    1. 局部变量,函数的形参
    2. 未初始化值是随机值
    3. 生存周期随着函数调用结束而终止
  2. 数据段(生存周期整个进程)
    1. bss
      1. 未初始化的全局变量和局部静态变量
      2. 初始值为0
    2. data
      1. 已初始化的全局变量和局部静态变量
  3. 文本段

四、指针

1.指针就是存储地址的变量,实际上指针就是地址,地址就是编号。

2.指针的类型取决于要存储的地址类型

3.指针所占存储空间大小取决地址大小,地址大小与元素类型有关系吗?是没有关系的,都是64bit地址。所以指针都是 8bytes与类型无关

4.不同类型的指针有什么区别?运算能力不同

        char *p; int *q;

        ii.sizeof(p)==sizeof(q);都是8bytes

        iii.但是p+1!=q+1;

        iv.p+1偏移地址是1bytes

        v.q+1偏移地址是4bytes

5.作用

        i.形参改变实参

        ii.参数的回填

6.指针的运算

        ++和 *

7.const修饰指针
        指针常量

                char *const p;

                什么是指针常量?

                        就是指针本身是一个常量,也就是说,指针本身指向的内存地址(存储空间)不能改变。不过,内存地址中存储的值可以发生改变。

                        也就是p不能改变,*p可以改变

        常量指针

                const char *p; 或 char const *p;

                什么是常量指针?

                        就是指向一个常量的内存地址的指针。(指针本身不是常量)也就是说,指针指向的内存地址可以发生改变。但是目前指针指向的内存地址中的值(也就是那个常量)不能改变。

                

8.存储字符串

        字符串变量char sl]="hello";

        字符串常量char*p="hello";p存储的不是字符串,是字符串的起始地址

9.类型

        1.变量的指针

        2.数组的指针

                int (*p)[10];

        3.函数的指针

                char *(*p)(char *, const char *);    //p就是函数指针

                int (*q)(int , int );                            //q就是函数指针

        4.万能指针

                1. void*

                2.没有步长不能+、-、*等运算

五、结构体、共用体、枚举

1.结构体struct

struct stu_st {
    int age;
    char name[32];
    float score;
};

注意结构体的对齐规则

结构体内可以有结构体类型,也可以包含同类型的结构体指针,但是不能包含同类型的变量

struct stu_st {
    int age;
    //struct stu_st st;        错误的
    struct stu_st *prev, *next;允许的
};

 变长结构体

结构体中最后一个元素是一个有1个成员的数组

开辟存储空间的时候,连同变长的存储空间共同开辟

有一个成员的数组的数组名就是可变长的存储空间的起始地址

struct test_st{
    struct test_st *prev, *next;
    char data[0];//如果编译器不支持写0那么就写1
};
malloc(sizeof(struct test_st) + size);//size就是data中存储的数据大小

2.共用体union

注意共用体的对齐规则,存储空间大小按照成员的最大值

共用体的大小端存储问题

        大端存储

                一个数据的高字节存低地址,低字节存高地址

        小端存储

                数据的高字节存高地址,低字节存低地址

3.枚举enum

        枚举的都是常量值

enum {MONDAY, TUESDAY, WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY};

默认情况下,枚举的常量值是从0开始的整数序列,也就是MONDAY == 0, TUESDAY == 1

//也可以设置值
enum {MONDAY = 1, TUESDAY, WEDNESDAY,THURSDAY = 9,FRIDAY,SATURDAY,SUNDAY};
/*
MONDAY == 1
TUESDAY == 2
WEDNESDAY == 3
THURSDAY == 9
FRIDAY == 10
SATURDAY == 11
SUNDAY == 12
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值