首页 > 其他分享 >C语言加强

C语言加强

时间:2024-05-06 17:36:36浏览次数:22  
标签:字节 int C语言 结构 加强 对齐 指针 变量

变量、指针和关键字

两个口诀:

  • 变量变量,能变,就是能读能写,必定在内存(RAM)里
  • 指针指针,保存的是地址,32 位处理器中的地址都是 32 位的,无论是什么类型的指针变量,都是 4 字节

指针

  1. 对于 32 位处理器里面,地址是 32 位的,所以指针的大小为 4 字节,sizeof(p) = 4sizeof(*p) = 指针所指向的类型所占的空间

变量

  1. 只读的常量一般放在 flash 中,所以只读的变量加上 const 可以节省内存,但有时候为了优化也可能会放在内存里

extern 关键字

  1. 如果想在 a.c 中引用 b.c 中的全局变量 int b ,需要在 a.c 中加入:
    1. extern int b
    2. 包含头文件 #include "b.h" ,然后在头文件中写 extern int b

注意 extern int b 不能被赋值!!!这个是声明,表示 b 是什么
不建议使用 extern,可以使用函数来进行值传递

static 关键字

  1. 对于全局变量,如果不加 static,全局变量的作用域为整个程序,加上 static 作用域就变为该文件了(函数前加 static 也是同样的作用)
  2. 对于在函数内定义的变量,加上 static 的变量仅会初始化一次,再次调用该函数时,仍为上一次函数调用的结果,不会再次初始化

volatile 关键字

  • 不能自作主张优化变量,直接存取原始内存地址

  • 应用场景:

    • 中断服务程序中修改的供其他程序检测的变量,需要用 volitile
    • 多任务环境中个任务间共享的标志加 volitile
    • 存储器映射的硬件存储器要加 volitile

    后面两个场景还没用到,等用到再具体补充

结构体

  • 结构体是不占内存的,是一种类型,用结构体定义了变量之后(实例化)才会分配内存空间

结构体对齐

为什么会有内存对齐

  1. 平台原因:不是所有的硬件平台都能访问任意内存地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。为了同一个程序可以在多平台运行,需要内存对齐。
  2. 硬件原因:经过内存对齐后,CPU 访问内存的速度大大提升

对齐规则

  1. 结构体每个成员相对结构体首地址的偏移量是对齐参数的整数倍,如有需要会在成员之间填充字节。编译器在为结构体成员开辟空间时,首先检查预开辟空间的地址相对于结构体首地址的偏移量是否为对齐参数的整数倍,若是,则存放该成员,若不是,则填充若干字节,以达到整数倍的要求。

这里的对齐参数取每个变量自身对齐参数和系统默认参数#pragma pack(n)(一般为 8)中较小的那个

  1. 结构体变量所占空间的大小是对齐参数大小的整数倍。如有需要会在最后一个成员末尾填充若干字节使得所占空间大小是对齐参数大小的整数倍。

这里的对齐参数取结构体中所有变量对齐参数的最大值和系统默认参数对比取较小的

  • 即结构体变量所占的空间大小需要经过两次对齐

图解

| char |     |     |     | 4字节
| int  | int | int | int | 4字节
| short|short|     |     | 4字节
| char |     |short|short| 4字节
| int  | int | int | int | 4字节

实例

  • 实例 1:
typedef struct
{
    char c;
    short d;
    static int a;
}A;
| char |     | 2字节
| short|short| 2字节

易错点:对于结构体中的 static int a ,静态数据成员存放位置与结构体实例的存储地址无关,不算在里面

只有 C++ 结构体中才有 static,C 语言中不允许有

  • 实例 2:
typedef struct 
{
    double b;
    int c;
}D;
    
typedef struct 
{
    bool a;   // bool为1字节
    D d;
    double b;
    int c;
}E;
|bool|-----------------------------------| 8字节
|--------------------D-------------------| 8字节
|--------------------D-------------------| 8字节
|------------------double----------------| 8字节
|---------int--------|-------------------| 8字节

易错点:D 与默认的 8 比,8 小,取 8 为对齐参数

变量赋值

  • a = 123 等价于
p = &a;
*p = 123;  // 将a的值变为123
  • A.age = 20 等价于(A 为结构体)
pt = &A;
pt->age = 20;  // pt为指针,取成员用"->",结构体用"."
*pt = A2;     // 将A变成A2的值

结构体指针

typedef struct student{
    char *name;
    int age;
    struct student *classmate;   // 结构体中只能用指针,长度为4个字节(链表)
}student, *pstudent;

student zhangsan = {"zhangshan", 10, NULL};
student lili = {"lili", 20, NULL};
zhangsan.classmate = &lili;  // 构成链表
name = zhangsan.classmate->name;   // zhangsan为结构体,用"."取值,classmate为指针,用"->"取值

函数指针

void (*add){};  // 函数指针,变量,占4字节

typedef struct student{
    char *name;
    int age;
    void (*good_work)(void);  // 函数指针
    struct student *classmate;
}student, *pstudent;

// add为函数指针,也可以写成&add
// 函数指针是变量,所以可以赋值为地址,函数不是变量,只能被调用
student ss[2] = {{"zhangshan", 10, add, NULL}, {"lili", 20, add, NULL}};  // 结构体数组

ss[1].good_work();   // 结构体调用函数指针,用"."
pstudent get(void){
    int type = 1;
    return &ss[type];  // 返回结构体指针
}  // 应尽量避免使用全局变量,可以将变量封装在函数里
pstudent a;
a = get();
a->good_work();   //这里a为结构体指针,用"->"

全局变量与局部变量

  • 全局变量:断电时无,运行时有,初值来自 Flash

    • 有值时初始化:类似于 memcpy,把 Flash 数据段整体拷贝到内存
    • 初始值为 0/没有初始化的:这些变量在内存里都放到了 ZI 段,类似于 memset,把 ZI 段全部清零
  • 局部变量:在栈里

参考资料

https://www.bilibili.com/video/BV1VM4y137Pm/?spm_id_from=333.999.0.0
https://blog.csdn.net/qq_41068271/article/details/83446623?spm=1001.2014.3001.5502

标签:字节,int,C语言,结构,加强,对齐,指针,变量
From: https://www.cnblogs.com/jll133688/p/18175474

相关文章

  • 高级C语言6
    结构:什么是结构:​ 是一种由程序员设计的复合数据类型,它由若干个其它类型的成员组成,用于统一描述事物的各项属性。​ 使用各类型的变量也可以描述事物的各项属性(如:通讯录项目),但使用麻烦且容易出错,没有使用结构方便,安全性高、统一性高,同时结构也是面向对象编程的基础。​ 基础C......
  • 高级C语言5
    输出缓冲区:​ 当我们使用标准库的输出系列函数打印数据到屏幕,数据并不会立即显示到屏幕上,而先存储到一块内存中,我们把这块内存称为输出缓冲区,等满足相关条件后,再从缓冲区中显示到屏幕,相关条件有:1、从输出状态切换到输入状态。2、缓冲区满了,1k=1024个字节,系统会把缓冲区中所有数......
  • 高级C语言2
    计算机的内存长什么样子?1、计算机中的内存就像一叠非常厚的“便签”,一张便签就相当于一个字节的内存,一个字节有8个二进制位2、每一张“便签”都有自然排序的一个编号,计算机是根据便签的编号来访问、使用"便签"3、CPU会有若干个金手指,每根金手指能感知高低电平,高电平转换成1,低电......
  • 高级C语言1
    一、程序的内存分段:(进程映像)​ 当执行程序的运行命令后,操作系统会给程序分配它所需要的内存,并划分成以下内存段供程序使用:text代码段:​ C代码被翻译成二进制指令后存储在可执行文件中,当可执行文件被操作系统执行时,它会把里面的二进制指令(编译后的代码)加载到这个内存段,它里面......
  • 06. C语言指针
    【指针】C语言使用数据名调用数据,数据名相当于C语言的直接寻址,直接寻址只能调用固定数据,而指针是间接寻址,指针存储了另一个数据的地址,使用指针调用数据时首先取指针存储的内存地址,之后使用此地址调用数据,使用间接寻址有如下几点优势:1.统一数据的调用方式,因为指针是调用数据的中间......
  • 标准C语言5
    进制转换:​ 现在的CPU只能识别高低两种电流,只能对二进制数据进行计算。​ 二进制数据虽然可以直接CPU计算识别,但不方便书写、记录,把二进制数据转换成八进制是为了方便记录在文档中。​随着CPU的不断发展位数不断增加,由早期的8位逐渐发展成现在的64位,因此八进制就不能满......
  • 标准C语言4
    一、函数什么是函数:function​函数就是一段具有某一项功能的代码集合,它是C语言中管理代码的最小单位,把具有某项功能的若干行代码封装在函数中方便管理代码且方便重复调用。函数的分类:标准库函数:​ C语言标准委员会为C语言以函数形式提供了一些基础功能,这些函数被封装在li......
  • 标准C语言3
    一、数组什么是数组:​ 数组就是变量的组合,是一种批量定义变量的方式如何定义数组:类型名数组名[数量]; intarr[8];// 相当于定义了8个int类型的变量 inta1,a2,a3,...;访问数组中的变量:数组名[下标]; 下标从0开始,范围0~数量-1遍历数组:与for循环配合,使用循环变量作......
  • 对C语言符号的一些冷门知识运用的剖析和总结
    符号目录符号注释奇怪的注释C风格的注释无法嵌套一些特殊的注释注释的规则建议反斜杠'\'反斜杠有续行的作用,但要注意续行后不能添加空格回车也能起到换行的作用,那续行符的意义在哪?反斜杠的转义功能单引号和双引号字面值,字符串,字符,字符变量的大小为什么sizeof('1')的大小是4?c......
  • 标准C语言2
    二、常量(了解)​ 常量就是程序运行过程中不能改变的量,C语言中常量有:字面值常量、宏常量、枚举常量。字面值常量100 int100l long100ll longlong100u unsignedint100lu unsignedlong100llu unsignedlonglong定义一个宏常量表示100年总共有多少秒,不考虑闰平年 #defin......