【Linux】C语言增补知识

[复制链接]
发表于 2025-9-19 00:22:07 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
有一些Linux常见的C语言用法必要好好复习一下。
   专栏目次:记载本身的嵌入式学习之路-CSDN博客

目次
1    布局体
1.1    布局体的界说
1.2    布局体的初始化
1.3    布局体数组的初始化
2    常用C函数
2.1    strcmp
3    指针
3.1    指针的初始化
3.2    指针利用的留意事项
3.3    指针的指针
3.4    内存泄漏
3.5    将一个内存开释过程封装成函数的留意事项
4    static修饰的意义
4.1    封装和隐蔽实现细节
4.2    克制命名空间的污染
4.3    链接优化

1    布局体

1.1    布局体的界说

(1)    最常用方法:先界说布局体后界说布局体变量

(2)    界说布局体同时初始化布局体变量

(3)    直接初始化匿名布局体变量(只管别用,能看懂就行)
 

1.2    布局体的初始化

(1)    次序初始化
 

(2)    引用成员初始化
        该方法也可以只初始化部分布局体成员,如不初始化name也可以。
 

(3)    界说布局体变量时趁便初始

1.3    布局体数组的初始化

        除了与布局体初始化相似的方法外,布局体数组还有一种看起来较为特殊的初始化方式:

 
        该方式在初始化时利用“[索引]=”指定初始化的布局体,好像是较新版本的C语言规范和编译器支持的。

2    常用C函数

2.1    strcmp

函数原型:
        int strcmp ( const char * str1, const char * str2 );
函数原理:
        逐字符比力两个字符串,若两个字符相等,则继承比力下一个字符,若两个字符不相等,则返回它们的ASCII码差值(ASCII码值大的字符串大)。
        若两字符串完全雷同,则返回0。

3    指针

3.1    指针的初始化

        假如一个指针变量被声明后没有被赋予任何值,它将包罗一个垃圾值(一个随机的内存地点),这个值是不可猜测的。因此对其进行利用就是不正当的,必要先初始化其值。


  • 指针的初始化并不是总是简单地指向一个变量的地点就可以了,下面的代码可行:
  1. int * ptr = NULL;
  2. int a = 100;
  3. ptr = &a;
复制代码
        如许可行的条件是,指针ptr的作用域和局部变量a的作用域雷同。


  • 要是局部变量的作用域和指针无交织,就无效了,如:
  1. int* init()
  2. {
  3.     int tmp = 100;
  4.     return &tmp;
  5. }
  6. int* ptr = init();
复制代码
        这种情况下ptr指向了一个已经被开释的地点,成为了悬挂指针,不正当。
        在指针的作用域比力广的时间,最好利用malloc/kmalloc等内存分配函数进行内存分配后或将指针指向静态变量的地点来给指针初始化。
3.2    指针利用的留意事项



  • 声明时赋NULL:int* ptr = NULL;
        由于没有途径知道指针是否已经被初始化,因此,声明时对其赋值NULL是一个很好的标志,后续看指针是否为NULL就知道其是否已经初始化了。


  • 开释后赋NULL:free(ptr); ptr = NULL;
        同上,只有开释后赋NULL,才气在别的地方知道这个指针是否被分配了内存。


  • 手动分配内存后肯定要记得开释:假如利用了像是malloc的内存分配函数,就肯定要记得本身free这个指针变量,否则会造成内存泄漏。
3.3    指针的指针

        假如必要将一个指针传入函数,并修改这个指针的地点值(留意,不是修改这个指针解引用的谁人值,而是指针自身的值)。那么,根据函数传参都是值传递这个说法(“传指针是引用传递”只是针对要传数值而言的,但假如我们要传的原来就是指针),传入的就应该是指针的指针,才气到达修改指针值的作用。
错误的例子:
  1.     void host_dev_delete(host_dev* host)
  2.     {
  3.         if (host)
  4.         {
  5.             kfree(host);
  6.             host = NULL;
  7.         }
  8.         else 
  9.         {
  10.             DRIVER_INFO_L("Trying to delete a NULL host_dev!");
  11.         }
  12.     }
复制代码
        该例子中,内存是开释掉了,但是函数外部host的实参依然指向已经被开释的内存空间,即host = NULL;没有发挥作用。
准确的例子:
  1. void host_dev_delete(host_dev** host)
  2. {
  3.     if (host && *host)
  4.     {
  5.         kfree(*host);
  6.         *host = NULL;
  7.     }
  8.     else if (!host)
  9.     {
  10.         DRIVER_INFO_L("Trying to delete a NULL host_dev pointer!");
  11.     }
  12.     else 
  13.     {
  14.         DRIVER_INFO_L("Trying to delete a NULL host_dev!");
  15.     }
  16. }
复制代码
        在准确的例子中,传入的是指针的指针,使得NULL赋值有效。同时,通过判定host是否为NULL,可以防止重复开释以及开释NULL的发生。
3.4    内存泄漏

        在用户空间中,要是手动分配了内存但没有开释,那么在用户步调竣事后,体系会去采取这部分没被用户开释的内存。
        在内核空间中,要是没有进行手动开释,体系也无法采取,只能等重启或关机的时间同一开释。而哪怕内核空间的步调写了开释,但是运行到开释前步调瓦解了,那就基本只能等重启和关机来开释了。别的方法的话,还可以思量利用以下思路:
(1)notifier机制:
        内核提供了notifier机制,允许模块在特定的体系变乱发生时注册回调函数。这些回调函数可以在体系halt、restart、power off、oops、panic等变乱发生时被调用,用于执行须要的清算利用。
(2)panic和oops处理:
        在内核发生oops或panic时,内核会实验执行一些标准的处理流程,包罗打印堆栈跟踪、生存日记等。在某些情况下,内核允许注册回调函数来处理这些非常变乱,比方通过aee框架生存关键信息。
3.5    将一个内存开释过程封装成函数的留意事项




  • 传入host_dev**,为了将host_dev*的内容赋值为NULL,只传入host_dev*开释内容是没题目标,但赋值NULL的利用会失效,具体缘故因由看前面的表明。
  • 查抄host_dev和*host_dev是否为空指针的缘故因由:

    • 查抄host_dev:确保函数内部不会对NULL指针进行解引用利用,这是未界说举动,大概导致步调瓦解;
    • 查抄*host_dev:确保不会开释已经被开释的内存或未初始化的内存。这同样会导致未界说举动,包罗步调瓦解、数据粉碎等。


4    static修饰的意义

4.1    封装和隐蔽实现细节

        被static修饰过的变量只能于本文件访问,因此可以克制被别的文件中的步调肆意修改。因此就可以将一些只在本文件利用的变量用static修饰,防止外部访问。
        函数也是云云,对于一些只是在本文件中利用的函数,利用static修饰,可以限定外部步调的权限,克制一些底层的函数被外部调用。对于外部访问,只袒露一些须要的接口就行,即接口函数倒霉用static修饰。
4.2    克制命名空间的污染

        倒霉用static修饰的函数和变量可被全局访问(include后),从而造玉成局命名空间的污染。多利用static修饰,可以低落污染,并有利于后续代码模块的增长,克制命名辩论。
4.3    链接优化

        假如一个static函数在其文件内未被利用,链接器可以优化掉这部分代码,淘汰最终二进制文件的巨细。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表