18-1C语言复习
按位与& :只要有0 结果即为0 按位或| :只要要有1 结果即为1 按位取反~ :对每一位取反0->1,1->0 左(右)移<<(>>) :可以直接按照字面意思理解 最高位移出去最低位补0 按位异或 ^ : 异:代表不一样,不一样时按照或运算结果为1;则剩下两者一样的情况时就不按照或运算,规定结果为0.
“#define 标识符字符串”:用于宏定义例:#define SYSCLK_FREQ_72MHz 72000000 定义标识符SYSCLK_FREQ_72MHz的值为72000000,之后出现的SYSCLK_FREQ_72MHz将被替代为所定义的标记72000000。 这样做的好处是在源码中不用写很多看不懂的数字,从而让程序可读性更高,而且更重要的一点当数值需要更改时,我们只需要改变宏定义中的数值,程序中的数值随之更改不需要一一更改所有的数值。 条件编译是根据实际定义宏(某类条件)进行代码静态编译的手段。可根据表达式的值或某个特定宏是否被定义来确定编译条件。 #define 定义一个预处理宏
#undef 取消宏的定义
#if 编译预处理中的条件命令,相当于C语法中的if语句
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
defined 与#if, #elif配合使用,判断某个宏是否被定义
最常见的条件编译是防止重复包含头文件的宏,形式跟下面代码类似:
[mw_shl_code=c,true]#ifndef ABCD_H
2 #define ABCD_H
3
4 // ... some declaration codes
5
6 #endif // #ifndef ABCD_H
[/mw_shl_code]
# ifdef #ifndef 等用法(转) 头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。 还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
[mw_shl_code=applescript,true]#ifndef <标识>
#define <标识>
......
......
#endif
[/mw_shl_code]
<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h [mw_shl_code=applescript,true]#ifndef _STDIO_H_
#define _STDIO_H_
......
#endif
[/mw_shl_code]
我在打开了几个源码之后发现了确实是这样的写的,而且至少main.c与key.c中都有#include "sys.h",但是当我把按键输入实验中ifndef与define以及endif都注释掉以后程序仍然可以编译,当时认为是否是因为没有使用这个sys.h中的函数, 结果在sys.h源码中找到了定义io口映射的PXin(n)位操作语句,在思考了好久之后我才发现应该是我理解错意思了,上面说两个.c文件都include同一个头文件的意思应该是include你所写的这个文件所形成的.h,并不是你写的头文件的其中引用的文件。 然而当我以为我解决了问题之后,突然看到了main.c与led.c都包含了led.h文件,我又试着把led.h的文件中几行条件编译语句注释掉,发现竟然同样可通过编译,这个问题直到最后我也没有想明白。 extern变量声明 C语言中extern可以置于变量或者函数前,以表变量或函数的定义在别的文件中。提示编译器遇到此变量和函数时在其他模块中寻找其定义,视频中例子调用test()函数由于在test.c中需要用到id但没有定义, 并且C规定了不可以多次定义。所以在test.c中加入extern.(串口实验)
typedef类型别名 还是相当于顶层头文件为你取好了一个简单的名字,用时直接使用即可。
结构体 结构体的好处就是程序看起来规整,并减少调用函数的入口参数。例如调用GPIO_Init只需要提前将结构体设置好mode,pin,speed。至于为什么设置完结构体需要调用GPIO_Init这里我理解为每次设置好结构体都要调用初始化, 让我们设置的东西起到作用。并且结构体扩展性很强,指的是如果我们不使用结构体不仅仅我们要修改函数内部还要修改函数的定义与声明,并且在所有调用该函数的地方都进行相应的修改,工作量非常大。相反的如果使用结构体, 外部加入多少参数我们只需要修改所调用函数内部与之相对应即可。GPIO_InitTypeDef就是系统写好的一个结构体go to definition可以看到里面包含了pin,mode,speed。
|