自学内容网 自学内容网

Linux和C语言(Day 12)

一、学习内容

  1. 存储类型

    1. 定义变量语法格式

      1. 数据类型 变量名; 存储类型 数据类型 变量名; 【定义变量可以省略存储类型,默认是auto】

    2. 定义函数语法格式

      1. 数据类型 函数名(参数){} 存储类型 数据类型 函数名(参数){} 【定义函数可以省略存储类型,默认是extern】

    3. 存储类型的作用是什么啊?

      1. 作用域——能够被使用的范围

      2. 生命周期——从定义分配内存到释放内存的周期,程序执行阶段

      3. 默认值——使用不同的存储类型,定义未赋初值的话,默认值不同【0、随机值】

    4. 存储类型auto

      1. 定义变量未定义存储类型,默认是auto。

        1. auto不能修饰全局变量。

        2. auto修饰局部变量,()形参、{}

        3. 默认值是 随机值。

    5. 存储类型 register

      1. register修饰的是寄存器变量。

      2. register修饰的变量 不能进行 &取地址操作。

      3. 默认值是 随机值

      4. CPU从内存取数据,发送给运算器进行数据处理,把结果有存入内存。CPU自己也有内存——寄存器,对于使用频率特别高的数据可以放在寄存器里面,CPU取数据的效率【寄存器 > cache(高速缓存) > 内存】。但是不能不能把所有变量都放在寄存器中,因为寄存器特别小。

    6. 存储类型static【静态】

      1. 延长生命周期 静态局部变量定义时分配内存,程序结束才释放内存,只赋初值一次。

      2. 限制作用域 静态全局变量和静态函数只能在该文件内使用,不能被外部文件引用。

      3. 定义未赋初值,默认值是0

    7. 存储类型extern【引入外部变量/函数】

      1. 若在1.c文件中定义了一个全局变量,想在2.c文件中使用,使用extern修饰该变量

      2. 若在1.c文件中定义了一个函数,想在2.c文件中使用,使用extern修饰该函数

    8. 存储类型const【常量】

      1. const修饰变量,表示常量化。

        1. 修饰变量,不可以修改值。 const int a = 666; a = 10; //报错

        2. const修饰指针,需要观察 const 与 * 的位置关系,位置不同,作用不同。

          1. cont int *p; 或者 int const *p 【可以修改地址、不可以修改值】

          2. nt * conts p; 【不可以修改地址、可以修改值】

          3. conts int * const p; 【值和地址都不可以修改】

  2. 宏 [不加分号]

    • 宏 是预处理命令,用于语法替换。

    • 语法格式: #define 宏名 字符串

    • 注意:

      • 以#开头的属于预处理命令,不是C语言语句,不加分号

      • 宏名符合命名规范 最好大写 MAX N MAXSIZE

      • 宏只做替换,不做运算

      • 宏没有类型检查,所以替换的全是字符串

    • 自定义宏函数

      • 使用({})

        • 使用({})定义宏函数,如函数只有一条语句,可以省略({})

        • 如有多条语句,返回最后一条语句的结果

        • 语法格式

          • #define 宏名(参数) ({语句1; 语句2; 语句3;})

          • 注意:宏函数的参数不带数据类型,只写参数名占位即可

      • 使用do...while(0)

        • 语法格式

          • #define 宏名(参数) do{语句1;语句2;语句3;}while(0)

    • 宏 与 条件编译

      • ==============判断宏是否为真====
        #if TURE
        执行语句
        #endif
        注释:
        #if 0
        被注释的语句……
        #endif
        #define TURE 1
        #define FALSE 0

      • ========================判断宏已经定义=====
        #ifdef 宏名
        执行语句
        #endif

      • =======================判断宏未定义=======
        #ifndef 宏名
        执行语句
        #endif

      • ======================判断多个宏定义=====
        #defined(宏名1) && defined(宏名2)
        执行语句
        #endif

      • =======================判断多个宏未定义=====
        #ndefined(宏名1) && ndefined(宏名2)
        执行语句
        #endif

      • ======================取消宏====
        #undef
        宏名

  3. typedef 【重定义数据类型】

    • 重定义数据类型

      • typedef int N; //N a; 等价于 int a;

      • typedef int N[3]; //N a; 等价于 int a[3];

      • typedef int *N; //N a; 等价于 int *a;

      • typedef int **N; //N a; 等价于 int **a;

      • typedef int *N[3]; //N a; 等价于 int *a[3];

      • typedef int (*N)[3]; //N a; 等价于 int (*a)[3];

      • typedef int (*N)(); //N a; 等价于 int (*a)();

      • typedef int (*N[3])(); //N a; 等价于 int (*a[3])();

    • 数据结构中

      • struct student{
        int id;
        char name[20];
        float score;
        };
        定义学生变量 struct student a;
        typedef struct student{
        int id;
        char name[20];
        float score;
        }XS;
        定义学生变量 XS a;

  4. 多文件编程

    • 实际的编程中,我们是把所有内容都写在一个.c文件吗

      • 不是的。我们会根据内容/数据的不同放在不同的文件中。

    • 基础学习的过程中: 一般把头文件、宏定义、全局变量、函数声明放在 XXX.h头文件中 把函数实现放在 XXX.c文件中 主函数单独放在 main.c文件中 使用vim -O XXX.h XXX.c main.c 新建三个文件

    • 问题

      • 一个#include只能包含一个头文件,若需要引入多个头文件,写多个#include

      • 一个头文件可以多次引入吗

        • 效果和引入一个一样 但是在预处理阶段,都会展开头文件 我们需要防止头文件多次引入 在XXX.h头文件中 #ifndef __HQYJ__ #define __HQYJ__ #include <stdio.h> #include <string.h> 定义全局 函数声明 …… #endif

      • #include 包含命令中 "" 和 <> 的区别是什么?

        • "" 先在当前路径下找,找不到去库文件中找 <> 直接去库文件中找

  5. 面试题总结

    • typedef和define的区别?

      • 语法不一样 typedef加; 是C语句 define不加; 不是C语句

      • 作用不一样 typedef用于给已有的数据类型重命名 define定义宏

      • define是预处理命令,在预处理直接替换,不做类型检查 typedef在编译时要进行类型检查

    • static的作用

      • 延长生命周期 静态局部变量定义时分配内存,程序结束才释放内存,只赋初值一次。

      • 限制作用域 静态全局变量和静态函数只能在该文件内使用,不能被外部文件引用。

      • 定义未赋初值,默认值是0

    • const的作用

      • 修饰变量,不可以修改值

      • const修饰指针,需要观察 const 与 * 的位置关系,位置不同,作用不同。

        • cont int *p; 或者 int const *p 【可以修改地址、不可以修改值】

        • int * conts p; 【不可以修改地址、可以修改值】

        • conts int * const p; 【值和地址都不可以修改】

  6. 脑图

二、总结

1. 学习内容概述

多文件编程

学习了如何将程序分成多个文件进行编写和组织,主要包括头文件的创建与引用、源文件的分离以及使用`extern`关键字进行跨文件变量共享。

typedef与结构体

学习了使用`typedef`为复杂的类型定义别名,尤其是在结构体中的应用。`typedef`可以简化代码,增加可读性,常用于定义结构体类型的别名。

宏定义

宏定义是C语言中的一种预处理器指令,通过`#define`定义常量和函数型宏,宏在程序编译前被预处理器替换。了解了宏的优势(如提高代码复用性和简化重复逻辑)以及潜在的陷阱(如宏扩展时的优先级问题)。

字符处理函数

学习了如何使用标准库中的字符处理函数(如`strlen`, `strcpy`, `strcat`, `strcmp`等)进行字符串操作。这些函数提供了常用的字符串处理功能,避免手动实现复杂的字符串操作逻辑。

 2. 学习难点

多文件编程的组织

虽然多文件编程可以提升程序的模块化和可维护性,但合理组织多个源文件、确保正确的头文件引用和依赖管理是学习的一个难点。特别是理解`extern`关键字的作用和如何避免重复定义变量。

宏定义的调试

宏并不像函数那样可以进行类型检查,容易导致隐藏的错误。复杂的宏定义在预处理器展开时,可能会因为操作符优先级问题导致意外的行为,调试较为困难。

字符数组与指针的混淆

在字符串操作时,字符数组和字符指针的使用容易混淆。尤其是在处理字符串长度或字符串拷贝时,可能会出现越界或者内存访问错误。

 3. 注意事项

头文件的保护机制

在编写头文件时,应该使用“头文件保护”机制,避免重复包含头文件。可以通过`#ifndef`, `#define`, `#endif`来确保头文件只会被编译器加载一次。

宏的使用

在编写宏定义时,应该谨慎处理宏中的操作符优先级问题,可以使用括号明确表达式的计算顺序。同时,避免使用过于复杂的宏,尽量用`inline`函数替代复杂的宏定义。

字符串处理中的边界问题

在处理字符串时,特别是使用`strcpy`, `strcat`等函数时,需确保目标数组有足够的空间存储结果,避免越界操作导致的缓冲区溢出问题。

多文件编程中的依赖管理

在多文件编程时,应该保持代码结构的清晰,合理拆分代码模块,确保每个文件职责单一,避免源文件之间的相互依赖和耦合。

4. 未来学习的重点

Makefile的学习与使用

在多文件编程中,Makefile是管理编译过程的重要工具。未来可以学习如何编写Makefile,自动化管理多个源文件的编译与链接,提升编程效率。

深入研究宏与内联函数的性能对比

宏虽然在一定程度上可以提高代码效率,但由于缺乏类型检查,容易出现问题。可以深入研究宏与`inline`函数的性能和使用场景,选择合适的优化手段。

字符串操作的安全性

未来可以进一步学习如何使用更安全的字符串操作函数,如`strncpy`, `strncat`等,避免因缓冲区溢出带来的安全隐患。

探索动态内存管理与指针

字符串操作涉及大量的内存管理问题,可以深入学习`malloc`, `free`等动态内存管理函数,掌握如何安全高效地分配和释放内存。


原文地址:https://blog.csdn.net/weixin_65095004/article/details/142174786

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!