在QEMU里学C语言-01- #define

QEMU中对C语言的使用非常高级,这里想从QEMU中对C语言的用法中,来重新认识C语言,今天的任务就是从弄懂这几段代码的意思开始吧!

#define module_init(function, type)                                         static void __attribute__((constructor)) do_qemu_init_ ## function(void)    {                                                                               register_module_init(function, type);                                   }
#endif
#define QTAILQ_INSERT_TAIL(head, elm, field) do {                       \
        (elm)->field.tqe_next = NULL;                                           (elm)->field.tqe_prev = (head)->tqh_last;                               *(head)->tqh_last = (elm);                                              (head)->tqh_last = &(elm)->field.tqe_next;                      } while (/*CONSTCOND*/0)
#define Q_TAILQ_HEAD(name, type, qual)                                  struct name {                                                                   qual type *tqh_first;           /* first element */                     qual type *qual *tqh_last;      /* addr of last next element */ }
  1. 先从C语言里关于define的语法进行整理

不带参数的define的格式是:

#define 名字  替换文本

在定义之后,程序出现定义的名字,都将用相应的替换文本代替。

  • 有些字符可以通过转义字符序列表示为字符和字符串常量
#define VTAB ‘\103‘  //ASCII纵向制表符
  • 常量表达式,仅仅包含常量的表达式。这种表达式在编译时求值,而不再运行时求值。
#define MAXLINE 100
char line[MAXLINE+1]
  • #define和枚举常量

    enum boolean {NO,YES} //NO的值为0,yes为1 ,以此类推

    枚举为常量值和名字之间的关联提供了一种遍历方式,相对于#define而言,它的优势在于常量值能自动生成。

宏替换:

  • 作用域:从宏定义开始,到被编译的源文件的末尾处结束
  • 替换文本是任意的
#define forever for(;;)   //无限循环
  • 宏定义也可以带参数,这样可以对不同的宏调用使用不同的替换文本
  • #define max(A,B) ((A)>(B)?(A):(B))

    这样很像是函数调用,但宏调用直接将替换文本插入到代码中,形参A,B每次都会被替换为实参。

  • x = max(p+q,s+r)
    将会被替换成
    x = ((p+q)>(s+r)?(p+q):(r+s))

    这样的好处是,不用每次都对不同的数据类型定义不同的函数

  • 缺陷是:当max(i++,j++) 会加两次,有错
  • 适当用括号以保证计算的正确性

    #define square(x)  x*x
    
    square(z+1) //会有错
  • 例如:在<stdio.h>头文件中有一个很实用的例子:getchar和putchar函数常被定义为宏,这样可以避免处理字符时调用函数所需要的运行时开销。(为什么?)
  • 用#undef可以取消名字的宏定义,这样可以保证后续的调用是函数调用,而不是宏调用
  • #undef getchar
    int getchar(void) {...}

    形参不能用带引号的字符串替换。(有什么能这样被替换吗?)但是,如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串

  • #define dprint(expr) printf(#expr " = %g\n", expr)
    
    dprint(x\y);
    
    结果为
    printf("x\y""=%g\n",x\y);而字符串被连接在一起了,所以等价于 printf("x\y = %g\n",x\y);

    预处理运算符##为宏扩展提供了一种连接实参的手段。若替换文本的参数与##响铃,则该参数会被实际参数替换!

#define paste(front, back)  front##back

paste(name,1) 

结果为: name1

2. 所以若type_init(ppc_heathrow_register_types):所以这一段代码的意思就是

#define type_init(function) module_init(function, MODULE_INIT_QOM)
#define module_init(function, type)                                         static void __attribute__((constructor)) do_qemu_init_ ## function(void)    {                                                                               register_module_init(function, type);                                   }
#endif

3. 这段代码也是完成了宏调用,当(/*CONSTCOND*/0)时,完成把节点插在队尾

#define QTAILQ_INSERT_TAIL(head, elm, field) do {                       \
        (elm)->field.tqe_next = NULL;                                           (elm)->field.tqe_prev = (head)->tqh_last;                               *(head)->tqh_last = (elm);                                              (head)->tqh_last = &(elm)->field.tqe_next;                      } while (/*CONSTCOND*/0)

总结:

#define 就是一种预编译时的替换(虽然不知道这样理解对不对)

用一个名字,替代掉复杂的函数,冗长的名字,和不让人理解的数字,其目的就是为了节约开销(为什么)和提高代码的阅读性

当然还有像这样的条件编译 : #ifdef 和 #endif 就不多说了

代码不管是用什么语言来写,最终都是编译成计算机能懂能执行的机器指令

路漫漫

#ifdef CONFIG_MODULES
static int module_load_file(const char *fname)
{
    GModule *g_module;
    void (*sym)(void);
    const char *dsosuf = HOST_DSOSUF;
    int len = strlen(fname);
    int suf_len = strlen(dsosuf);
    ModuleEntry *e, *next;
    int ret;

    if (len <= suf_len || strcmp(&fname[len - suf_len], dsosuf)) {
        /* wrong suffix */
        ret = -EINVAL;
        goto out;
    }
    if (access(fname, F_OK)) {
        ret = -ENOENT;
        goto out;
    }

    assert(QTAILQ_EMPTY(&dso_init_list));

    g_module = g_module_open(fname, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
    if (!g_module) {
        fprintf(stderr, "Failed to open module: %s\n",
                g_module_error());
        ret = -EINVAL;
        goto out;
    }
    if (!g_module_symbol(g_module, DSO_STAMP_FUN_STR, (gpointer *)&sym)) {
        fprintf(stderr, "Failed to initialize module: %s\n",
                fname);
        /* Print some info if this is a QEMU module (but from different build),
         * this will make debugging user problems easier. */
        if (g_module_symbol(g_module, "qemu_module_dummy", (gpointer *)&sym)) {
            fprintf(stderr,
                    "Note: only modules from the same build can be loaded.\n");
        }
        g_module_close(g_module);
        ret = -EINVAL;
    } else {
        QTAILQ_FOREACH(e, &dso_init_list, node) {
            e->init();
            register_module_init(e->init, e->type);
        }
        ret = 0;
    }

    QTAILQ_FOREACH_SAFE(e, &dso_init_list, node, next) {
        QTAILQ_REMOVE(&dso_init_list, e, node);
        g_free(e);
    }
out:
    return ret;
}
#endif

欢迎指正

原文地址:https://www.cnblogs.com/mumutoday/p/9744603.html

时间: 2024-11-03 03:06:06

在QEMU里学C语言-01- #define的相关文章

编程初步————学渣再学C语言之路

内存: 程序在执行时,程序的指令和数据都必须存储到主内存中,也可以说存在RAM(随机访问存储器)中,RAM是易失性存储器,PC关闭,RAM内容丢失: 变量是什么? 变量就是计算机的一块特定的内存,有一个或多个连续的字节组成,当然每个变量都会有一个名字叫变量名,就像一个ID,一个身份证号码,这个变量名独一无二的代表着这块内存空间,编译器就可以通过这个ID来直接调用这块内存里存储的数据了,这也很好的解释了变量名不占内存空间,编译器直接就把他翻译成了数据: 变量的声明其实也可以称为变量的定义,按照声明

黑 马 程 序 员_视频学习总结&lt;c语言&gt;----01 关键字、标识符、注释、 常量、变量

---------------------- ASP.Net+Unity开发..Net培训.期待与您交流! ---------------------- 一.关键字 1.关键字就是C语言提供的有特殊含义的符号,有些地方也叫做“保留字”. 2.C语言一共提供了32个关键字,这些关键字都被C语言赋予了特殊含义. auto double int struct break else long switch case enum register typedef char extern return uni

NPL ( neuro-linguistic programmers 神经语言程序员) / ( Neuro-Linguistic Programming 神经语言程序学/身心语言程式学/ 神经语言程式学/ 神经语言程式)

Neuro-Linguistic Programmers 神经语言程序员 Neuro-Linguistic Programming  神经语言程序学/身心语言程式学/ 神经语言程式学/ 神经语言程式 NLP是神经语言程序学 (Neuro-Linguistic Programming) 的英文缩写.在香港,也有意译为身心语法程式学的.N (Neuro) 指的是神经系统,包括大脑和思维过程.L (Linguistic) 是指语言,更准确点说,是指从感觉信号的输入到构成意思的过程.P (Program

和可乐geek学python【01】

python是一门计算机高级语言 计算机语言嘛,分3种,有低级语言(汇编语言,机器语言).还有高级语言,像c/c++ ,java 这样的语言都是高级语言. 我们可以打个比方,就像我们可以通过程序和计算机对话,计算机就像一个仆人,我们叫他做什么,他就能做什么,但是,这个仆人听不懂我们说的高级语言,但是我们应该如何给这个仆人下指令让他去干我们吩咐他给我们做的事情呢. 这里有一个方法,就是找个翻译.把我们的高级语言翻译成仆人能听懂的低级语言(机器语言). 这里有两个方案. 方案一:找解释器(inter

如何学一门语言

从大一开始,老师就说精通一门语言,就可以很快地学习入门其他的语言,这句话一点也不假.其深沉的含义是,思想区别不大,主要的区别在于语法.经过学习,觉得很多语言其实也是相同的,比如顺序,循环,结构体,类,抽象,封装,继承...基本所有的语言都离不开.在学校,从c.c++.sql server.data structure.java..net.android:自己学的ruby.ruby on rails.Ext.css. html .javascript,其实学这些语言,不是它有多难,往往不是思想束缚

Linux下零基础学C语言、C++系列实战视频教程

Linux下零基础跟我学C语言.C++系列实战教程(入门篇.项目实战与提高篇.软件设计与工程实践篇)适合人群:初级课时数量:194课时用到技术:C++涉及项目:windows版服务器端开发咨询qq:1840215592Linux下零基础学C语言.C++系列实战视频教程详细介绍:http://www.dwz.cn/Fk3mk1.1跟我一起学C(linux)课程详细介绍01.从helloworld程序认识计算机(一)Helloword程序什么是程序程序语言C程序执行环境02.从helloworld程

快看Sample代码,速学Swift语言(2)-基础介绍 快看Sample代码,速学Swift语言(1)-语法速览

快看Sample代码,速学Swift语言(2)-基础介绍 Swift语言是一个新的编程语言,用于iOS, macOS, watchOS, 和 tvOS的开发,不过Swift很多部分内容,我们可以从C或者Objective-C的开发经验获得一种熟悉感.Swift提供很多基础类型,如Int,String,Double,Bool等类型,它和Objective-C的相关类型对应,不过他是值类型,而Objective-C的基础类型是引用类型,另外Swift还提供了几个集合类型,如Array, Set, 和

跟着数百万人编程导师学C语言!

点击关注 异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 参与文末话题讨论,每日赠送异步图书 --异步小编 为什么说这不是一本完全C语言的书?因为<"笨办法"学C语言>不仅仅是一本书,随书附赠5个多小时充满激情的视频,这是一套完整的C语言视频课程! 这本书刚拿到样书,运营经理兴奋的发来消息:"手机扫码看视频,太方便了,速度也很快,配的字幕也很赞!"而本书的责编杨海玲老师很淡定:"这个是我尝试的第一本扫码看视频的书,虽然遇到些问

学C语言好,还是学C++好呢?这两个专业在哪些领域用得最多?

前言 从事嵌入式开发十几年,基本上围绕着这两种编程语言展开,都可以直接操作底层的编程语言,用的越熟练越是感觉工具属性越强. 虽然两种编程语言分属于不同的编程思想,用的时间长了觉得差异也不是很大,现在就个人的从业经历,讲述下两种编程语言的差异和共同之处.最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题. 学C语言好,还是学C++好呢?这两个专业在哪些领域用得最多?现在已经很多人觉得C语言过时了? 因为从