13.1 总结

前面12节的课程,主要针对 Linux 内核中 GNU C 扩展的一些常用 C 语言语法进行了分析。GNU C 的这些扩展语法,主要用来完善 C 语言标准和编译优化。而通过 C 标准的发展过程我们又发现,对于一些编译器扩展的一些特性,或者其它编程语言(如:C++)中的好的特性和语法,C 标准也会适时地吸收进来,作为新的 C 语言标准。

在 GNU C 的这些扩展语法中,__attribute__ 和宏定义是两大特色。在嵌入式底层系统中,尤其是 Linux 内核和 U-boot 中,大量使用 GNU C 扩展的 __attribute__属性去辅助一些底层机制的实现,或者实现一些编译上的优化。在宏定义方面,通过语句表达式、可变参数宏等特性,我们可以定义一个功能复杂、安全可靠的高质量宏。

本教程所讲的一些特性,都是在实际工作或阅读 Linux 内核驱动源码时经常遇见的一些特性,掌握了这些扩展特性的使用,以后再遇到类似的“奇葩 C 语言”程序,就知道怎么去分析了。除此之外,GNU C 还有一些其它扩展特性,由于他们在内核中用得不是很多,或者说仅仅是做一些编译上的优化,即使不知道也不会影响我们理解代码,限于篇幅关系,所以就暂时不讲了,比如下面这些特性。

  • 属性声明:const
  • 属性声明:constructor、destructor
  • 属性声明:noreturn
  • 属性声明:used、unused
  • 局部标签
  • 嵌套函数
  • ……

大家以后遇到类似的扩展,可以到下面这几个网站上去看看。

13.2 C 语言习题测试

下面是几道 C 语言练习题,大家可以做一做。看看学完本教程后,有没有真正的掌握。有什么疑问,可以通过读者圈,或加入QQ群(475504428)与我讨论。

1.下面的程序,在不同编译环境下,比如分别在 C-Free、VC++6.0、TurboC 环境下编译运行,结果是否相等,为什么?

#include<stdio.h>
int main(void)
{
printf("size: %d\n", sizeof(int);
return ;
}

2.定义一个宏,求两个数的最小值。

3.将下面的程序编译为可以在 ARM 平台上运行的可执行文件 a.out,并对其进行反汇编,查看变量 global_val 的地址。

int global_val = ;
int uninit_val;
int main(void)
{
int local_val = ;
return ;
}

4. 在一个工程项目中,有两个源文件如下,分析下面程序的运行结果。

//func.c
int a = ;
int b;
int c __attribute__((weak)) = ;

//main.c
int a;
int b = ;
int c = ;
int main(void)
{
printf("a: %d\n",a);
printf("b: %d\n",b);
printf("c: %d\n",c);
return ;
}

5.定义一个变参函数,实现等级打印控制:ERROR、DEBUG、INFO。用这三个宏分别代表等级打印,比如定义 ERROR 时,只打印错误的信息;定义 DEBUG 时,打印错误和调试信息;定义 INFO 时,所有的打印信息都打印出来。

6.定义一个变参宏,实现等级打印控制:ERROR、DEBUG、INFO。用这三个宏分别代表等级打印,比如定义 ERROR 宏时,只打印错误的信息;定义 DEBUG 时,打印错误和调试信息;定义 INFO 时,所有的打印信息都打印出来。

7.下面是 Linux 内核(Linux4.4.0)中的一些宏定义,请分析它们实现的功能。

#define pr_emerg(fmt, ...) \
printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert(fmt, ...) \
printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit(fmt, ...) \
printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err(fmt, ...) \
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warning(fmt, ...) \
printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn pr_warning
#define pr_notice(fmt, ...) \
printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)

8.在 Linux 内核启动过程中,启动 log 的最后往往会有这么一行信息。

Freeing unused kernel memory: 468K

请用本课程中的 section 属性声明,分析这段 log 背后的内核初始化及内存释放过程。

9.在嵌入式 Linux 驱动开发中,驱动模块是没有 main() 入口函数的,请用本课程学过的知识分析:驱动是如何运行和初始化的。

10.驱动分析:在 linux4.4 源码 linux-4.4/arch/arm/mach-footbridge/ebsa285.c 中,分析下面代码的含义及 container_of 宏的作用。

MACHINE_START(EBSA285, "EBSA285")
/* Maintainer: Russell King */
.atag_offset = 0x100,
.video_start = 0x000a0000,
.video_end = 0x000bffff,
.map_io = footbridge_map_io,
.init_early = footbridge_sched_clock,
.init_irq = footbridge_init_irq,
.init_time = footbridge_timer_init,
.restart = footbridge_restart,
MACHINE_END
static void ebsa285_led_set(struct \
led_classdev *cdev, enum led_brightness b)
{
struct ebsa285_led *led = container_of(cdev,
struct ebsa285_led, cdev);

if (b == LED_OFF)
hw_led_state |= led->mask;
else
hw_led_state &= ~led->mask;
writeb(hw_led_state, xbus);
}

static enum led_brightness \
ebsa285_led_get(struct led_classdev *cdev)
{
struct ebsa285_led *led = container_of(cdev,
struct ebsa285_led, cdev);

return hw_led_state & led->mask ? LED_OFF : LED_FULL;
}

嵌入式C语言自我修养 13:C语言习题测试的更多相关文章

  1. 嵌入式C语言自我修养 06:U-boot镜像自拷贝分析:section属性

    6.1 GNU C 的扩展关键字:attribute GNU C 增加一个 __atttribute__ 关键字用来声明一个函数.变量或类型的特殊属性.声明这个特殊属性有什么用呢?主要用途就是指导编译 ...

  2. 嵌入式C语言自我修养 04:Linux 内核第一宏:container_of

    4.1 typeof 关键字 ANSI C 定义了 sizeof 关键字,用来获取一个变量或数据类型在内存中所占的存储字节数.GNU C 扩展了一个关键字 typeof,用来获取一个变量或表达式的类型 ...

  3. 嵌入式C语言自我修养 03:宏构造利器:语句表达式

    3.1 基础复习:表达式.语句和代码块 表达式 表达式和语句是 C 语言中的基础概念.什么是表达式呢?表达式就是由一系列操作符和操作数构成的式子.操作符可以是 C 语言标准规定的各种算术运算符.逻辑运 ...

  4. 嵌入式C语言自我修养 12:有一种宏,叫可变参数宏

    12.1 什么是可变参数宏 在上面的教程中,我们学会了变参函数的定义和使用,基本套路就是使用 va_list.va_start.va_end 等宏,去解析那些可变参数列表我们找到这些参数的存储地址后, ...

  5. 嵌入式C语言自我修养 11:有一种函数,叫内建函数

    11.1 什么是内建函数 内建函数,顾名思义,就是编译器内部实现的函数.这些函数跟关键字一样,可以直接使用,无须像标准库函数那样,要 #include 对应的头文件才能使用. 内建函数的函数命名,通常 ...

  6. 嵌入式C语言自我修养 10:内联函数探究

    10.1 属性声明:noinline & always_inline 这一节,接着讲 __atttribute__ 属性声明,__atttribute__ 可以说是 GNU C 最大的特色.我 ...

  7. 嵌入式C语言自我修养 01:Linux 内核中的GNU C语言语法扩展

    1.1 Linux 内核驱动中的奇怪语法 大家在看一些 GNU 开源软件,或者阅读 Linux 内核.驱动源码时会发现,在 Linux 内核源码中,有大量的 C 程序看起来“怪怪的”.说它是C语言吧, ...

  8. 嵌入式C语言自我修养 02:Linux 内核驱动中的指定初始化

    2.1 什么是指定初始化 在标准 C 中,当我们定义并初始化一个数组时,常用方法如下: ] = {,,,,,,,,}; 按照这种固定的顺序,我们可以依次给 a[0] 和 a[8] 赋值.因为没有对 a ...

  9. 嵌入式C语言自我修养 05:零长度数组

    5.1 什么是零长度数组 顾名思义,零长度数组就是长度为0的数组. ANSI C 标准规定:定义一个数组时,数组的长度必须是一个常数,即数组的长度在编译的时候是确定的.在ANSI C 中定义一个数组的 ...

随机推荐

  1. sequelize 学习之路

    如果你觉得Sequelize的文档有点多.杂,不方便看,可以看看这篇. 在使用NodeJS来关系型操作数据库时,为了方便,通常都会选择一个合适的ORM(Object Relationship Mode ...

  2. UML实践

    UML图一览 1.分工泳道图 使工作内容更加清晰 2.类图 更加细化了一些函数,对于之后的接口文档细节问题进行了约束 3.用例图 实现了一个玩家的整体可操作的概况 4.活动图 1)注册活动图 用于登录 ...

  3. Xposed模块开发基本方法记录

    由于某些课程实验的要求,需要通过xposed框架对某应用进行hook操作,笔者选用了开源且免费的xposed框架进行实现.虽然网上存在一些利用xposed实现特定功能的文章资源,但大多均将xposed ...

  4. cogs 2355. [HZOI 2015] 有标号的DAG计数 II

    题目分析 来自2013年王迪的论文<浅谈容斥原理> 设\(f_{n,S}\)表示n个节点,入度为0的点集恰好为S的方案数. 设\(g_{n,S}\)表示n个节点,入度为0的点集至少为S的方 ...

  5. 【[HEOI2014]大工程 】

    可能是虚树板子题了 首先先把虚树建出来,但是这里和那道虚树的入门题不一样,这里所有的询问点都得在虚树里,所以不会存在那种直接不如栈的点 之后我们考虑一下这个三个要求的东西 第一个操作我们需要统计虚树上 ...

  6. ZooKeeper学习之路 (八)ZooKeeper原理解析

    ZooKeeper中的各种角色 ZooKeeper与客户端 每个Server在工作过程中有三种状态: LOOKING:当前Server不知道leader是谁,正在搜寻 LEADING:当前Server ...

  7. Hadoop学习之路(二十八)MapReduce的API使用(五)

    求所有两两用户之间的共同好友 数据格式 A:B,C,D,F,E,O B:A,C,E,K C:F,A,D,I D:A,E,F,L E:B,C,D,M,L F:A,B,C,D,E,O,M G:A,C,D, ...

  8. 2、Web Service-术语

    1.Java中的Web Service规范 三种规范:JAXM&SAAJ.JAX-WS(JAX-RPC).JAX-RS. 三要素:soap,wsdl,uddi 1. Jaxws(掌握) JAX ...

  9. background-clip 实现字体渐变效果

    background-clip 实现字体渐变效果 (一)类似KTV字幕效果 @-webkit-keyframes loop{ 0%{background-position: -800px 0;} 10 ...

  10. SOE 部署错误 ClassFactory cannot supply requested class问题及解决方案

    一.问题描述 虽然SOE开发已经老早出来了(ArcGIS 10.1 不再支持DCOM开发,所以以往的基于AO+WebService需要转变思路),不过由于跟工作关联性不是很大,一直未系统学习过.网上下 ...