麻烦问下#ifndef的作用 #define #endif 这几个组合的意义

AVR c语言优秀编程风格
AVR单片机 c语言优秀编程风格
你现在的位置:&&&&AVR单片机 c语言优秀编程风格
AVR c语言优秀编程风格
作为一个初学者如何具有良好的程序设计风格呢?我想引用一个关于初学者请教编程大师的故事让读者自己去领悟。
有一位编程大师,他写非结构化的程序,一位初学者刻意模仿他,也写非结构化的程序。当他让大师看他的进步时,大师批评了他的非结构化程序:“ 对一位编程大师合适的东西未必对一个初学者同样合适,在超越结构化之前,你必须理解编程之道。”
我个人认为作为一个初学者应该踏踏实实的打好程序设计的基础,不要急功近利,舍本逐末。我走过不少弯路,希望大家能和我一样能牢记编程大师的忠告:“对编程大师合适的东西未必对一个初学者同样合适”。
本文所描述的优秀编程风格适合于大部分语言,文章中可能提到你不是很了解的概念,没有关系,你放心的读下去,当你使用AVR一个月之后,你什么都明白了。
AVR c语言优秀编程风格
模块化的程序应该是有一个很好的程序结构的。AVR C语言程序有两种用户文件,.c程序文件,.h头文件,程序中编写过程中需要在.c文件中包含.h头文件。初学者往往出现重复包含或者头文件包含错误的问题,我当时也时常为这种错误而发愁。下面我以我写的电机驱动例程来给大家说明一下,优秀的编程文件结构。
这个工程中有8个文件,一个说明文件,如下图:下载程序例子
我写的成型的程序的文件个数基本上都是偶数,因为每一个结构化的函数定义.c文件都会对应一个.h文件。main.c对应config.h。我们来看看各文件的包含关系。下面我们看看这些文件的包含关系与内容:[推荐的文件包含顺序与关系]
所有.c文件都包含了config.h文件。如: #include "config.h"
在config.h 中有如下代码:
#include "delay.h"
#include "device_init.h"
#include "motor.h"
这样做就不容易出现错误的包含关系,为了预防万一,我们还引入了宏定义与预编译。如下:
#ifndef _UNIT_H__
#define _UNIT_H__ 1
extern void Delay100us(uint8 n);
extern void Delay1s(uint16 n); //
n &= 6 ,when n==7, it is 1.
extern void Delay1ms(uint16 n);
第一次包含本文件的时候正确编译,并且#define _UNIT_H__ 1,第二次包含本文件#ifndef _UNIT_H__就不再成立,跳过文件。
预编译还有更多的用途,比如可以根据不同的值编译不同的语句,如下:
//#pragma REGPARMS
#if CPU_TYPE == M128
#include &iom128v.h&
#if CPU_TYPE == M64
#include &iom64v.h&
#if CPU_TYPE == M32
#include &iom32v.h&
#if CPU_TYPE == M16
#include &iom16v.h&
#if CPU_TYPE == M8
#include &iom8v.h&
#include&filename& 与 #include &filename& 的区别 :前者是包含系统目录include下 的文件,后者是包含程序目录下的文件。
变量名与函数名
变量以及函数命名应该按照尽量短,按需长,具有实际意义。可以通过下划线或者大小写结合的方法组合动词和名词组成变量函数名。下面对比好的命名方法与不好的命名方法:
好的:&&&Delay100us();
不好的:&&&Yanshi();
好的:&&&init_devices();
不好的:&&&Chengxuchushihua();
不好的:&&&
首先在模块化程序的.h文件中定义extern
//端口初始化
extern void port_init(void);
//T2初始化
void timer2_init(void);
//各种参数初始化
extern void
init_devices(void);
模块化程序的.c文件中定义函数,不要在模块化的程序中调用程序,及不要出现向timer2_init();这样函数的使用,因为你以后不知道你到底什么地方调用了函数,导致程序调试难度增加。可以在定义函数的过程中调用其他函数作为函数体。
/**************************采用timer2 产生波形***********************/
// PWM频率 = 系统时钟频率/(分频系数*2*计数器上限值))
void timer2_init(void)
TCCR2 = 0x00;
TCNT2= 0x01;
//set count
OCR2 = 0x66;
//set compare
TCCR2 = (1&&WGM20)|(1&&WGM21)|(1&&COM21)|0x06; //
start timer 快速pwm模式,匹配清零,溢出置位 256分频
//占空比=高比低为:(OCR2-0X01)/(0XFF-OCR2)
OX01++++++(OCR2)__________OXFF (+表示输出高,_表示输出低)
//即OCR2越大,输出越大
在少数几个文件中调用函数,在main.c中调用大部分函数,在interupts.c中根据不同的中断调用服务函数。
void main(void)
/******************************************************************************/
//初始工作
/******************************************************************************/
init_devices();
for_ward(0);
//默认速度运转 正
Delay1s(5);
motor_stop();
Delay1s(5);
back_ward(0);
//默认速度运转 反
Delay1s(5);
speed_add(20);
Delay1s(5);
speed_subtract(20);
Delay1s(5);
宏定义主要用于两个地方:
一是用得非常多的命令或语句,利用宏将其简化。
#ifndef TRUE
#define TRUE
#ifndef FALSE
#define FALSE 0
#ifndef NULL
#define NULL 0
#define MIN(a,b)
((a&b)?(a):(b))
#define MAX(a,b)
((a&b)?(a):(b))
#define ABS(x)
((x>)?(x):(-x))
typedef unsigned char
/* 定义可移植的无符号8位整数关键字
typedef signed
/* 定义可移植的有符号8位整数关键字
typedef unsigned int
/* 定义可移植的无符号16位整数关键字
typedef signed
/* 定义可移植的有符号16位整数关键字
typedef unsigned long
/* 定义可移植的无符号32位整数关键字
typedef signed
/* 定义可移植的有符号32位整数关键字
二是利用宏定义方便的进行硬件接口操作,再程序需要修改时,只需要修改宏定义即可,而不需要满篇去找命令行,进行修改。
//PD4,PD5 电机方向控制 如果更改管脚控制电机方向,更改PORTD |= 0x10即可。
#define moto_en1 PORTD |= 0x10
#define moto_en2 PORTD |= 0x20
#define moto_uen1 PORTD &=~ 0x10
#define moto_uen2 PORTD &=~ 0x20
//启动TC2定时比较和溢出
#define TC2_EN TIMSK |= (&&1OCIE2)|(1&&TOIE2)
//禁止TC2再定时比较和溢出
#define TC2_DIS TIMSK &=~ (1&&OCIE2)|(1&&TOIE2)
为了增加程序的可读性,方便合作者读动程序,或者程序作者在一段时间之后还能看懂程序,我们需要在程序中写 注释。
在比较特殊的函数使用或者命令调用的地方加单行注释。使用方法为:
Tbuf_putchar(c,RTbuf);
// 将数据加入到发送缓冲区并开中断
extern void Delay1s(uint16 n); //
n <= 6 ,when n==7, it is 1.
在模块化的函数中使用详细段落注释:
/***********************
** 函数名称: Com_putchar
** 功能描述: 从串行口输出一个字符c
** 输 入: c:输出字符
: 0:失败 1:成功
** 全局变量: 无
** 调用模块:
********************/
在文件头上加文件名,文件用途,作者,日期等信息。
/*********************************************************************************************************
(c) Copyright , limaokui
All Rights Reserved
**--------------文件信息--------------------------------------------------------------------------------
人: 李茂奎
**最后修改日期: 日
述: serial
**--------------历史版本信息----------------------------------------------------------------------------
** 创建人: 李茂奎
** 版 本: V1.00
** 日 期: 日
** 描 述: 原始版本
*********************************************************************************************************/
要清楚,注释是为了方便阅读,增强程序的可度性,不要本末倒置,不要给很简单大家都能看明白的程序加注释,不要让注释淹没了你的程序结构。对于函数,变量等尽量使用文件名自注释的方法,及通过文件名就可以知道意思。
本文结束了,新手教程也结束了,希望我们教程能让你轻松进入AVR的世界。
全体工作人员谢谢你对本站的支持,谢谢你光临本站。
欢迎交换链接,请与小古联系 avrvi【at】C - Pre-Processors
Advertisements
The C Preprocessor is not part of the compiler, but is a separate step in the compilation process. In simplistic terms, a C Preprocessor is just a text substitution tool. We'll refer to the C Preprocessor as the CPP.
All preprocessor lines begin with #
The unconditional directives are:
#include - Inserts a particular header from another file
#define - Defines a preprocessor macro
#undef - Undefines a preprocessor macro
The conditional directives are:
#ifdef - If this macro is defined
#ifndef - If this macro is not defined
#if - Test if a compile time condition is true
#else - The alternative for #if
#elif - #else an #if in one statement
#endif - End preprocessor conditional
Other directives include:
# - Stringization, replaces a macro parameter with a string constant
## - Token merge, creates a single token from two adjacent ones
Pre-Processors Examples:
Analyze following examples to understand various directives.
#define MAX_ARRAY_LENGTH 20
Tells the CPP to replace instances of MAX_ARRAY_LENGTH with 20. Use #define for constants to increase readability.
#include &stdio.h&
#include "myheader.h"
Tells the CPP to get stdio.h from System Libraries and add the text to this file. The next line tells CPP to get myheader.h from the local directory and add the text to the file.
#define FILE_SIZE 42
Tells the CPP to undefine FILE_SIZE and define it for 42.
#ifndef MESSAGE
#define MESSAGE "You wish!"
Tells the CPP to define MESSAGE only if MESSAGE isn't defined already.
#ifdef DEBUG
/* Your debugging statements here */
Tells the CPP to do the following statements if DEBUG is defined. This is useful if you pass the -DDEBUG flag to gcc. This will define DEBUG, so you can turn debugging on and off on the fly!
Stringize (#):
The stringize or number-sign operator ('#'), when used within a macro definition, converts a macro parameter into a string constant. This operator may be used only in a macro that has a specified argument or parameter list.
When the stringize operator immediately precedes the name of one of the macro parameters, the parameter passed to the macro is enclosed within quotation marks and is treated as a string literal. For example:
#include &stdio.h&
message_for(a, b)
printf(#a " and " #b ": We love you!\n")
int main(void)
message_for(Carole, Debra);
This will produce following result using stringization macro message_for
Carole and Debra: We love you!
Token Pasting (##):
The token-pasting operator (##) within a macro definition combines two arguments. It permits two separate tokens in the macro definition to be joined into a single token.
If the name of a macro parameter used in the macro definition is immediately preceded or followed by the token-pasting operator, the macro parameter and the token-pasting operator are replaced by the value of the passed parameter.Text that is adjacent to the token-pasting operator that is not the name of a macro parameter is not affected. For example:
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
tokenpaster(34);
This example results in the following actual output from the preprocessor:
printf ("token34 = %d", token34);
This example shows the concatenation of token##n into token34. Both the stringize and the token-pasting operators are used in this example.
Parameterized Macros:
One of the powerful functions of the CPP is the ability to simulate functions using parameterized macros. For example, we might have some code to square a number:
int square(int x) {
return x *
We can instead rewrite this using a macro:
#define square(x) ((x) * (x))
Macros with arguments must be defined using the #define directive before they can be used. The argument list is enclosed in parentheses and must immediately follow the macro name. Spaces are not allowed between and macro name and open parenthesis. For example:
#define MAX(x,y) ((x) & (y) ? (x) : (y))
Macro Caveats:
Macro definitions are not stored in the object file. They are only active for the duration of a single source file starting when they are defined and ending when they are undefined (using #undef), redefined, or when the end of the source file is found.
Macro definitions you wish to use in multiple source files may be defined in an include file which may be included in each source file where the macros are required.
When a macro with arguments is invoked, the macro processor substitutes the arguments into the macro body and then processes the results again for additional macro calls. This makes it possible, but confusing, to piece together a macro call from the macro body and from the macro arguments.
Most experienced C programmers enclose macro arguments in parentheses when they are used in the macro body. This technique prevents undesired grouping of compound expressions used as arguments and helps avoid operator precedence rules overriding the intended meaning of a macro.
While a macro may contain references to other macros, references to itself are not expanded. Self-referencing macros are a special feature of ANSI Standard C in that the self-reference is not interpreted as a macro call. This special rule also applies to indirectly self-referencing macros (or macros that reference themselves through another macro).
Advertisements
Advertisements博客访问: 200335
博文数量: 137
博客积分: 455
博客等级: 一等列兵
技术积分: 742
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: C/C++
在头文件中的作用
在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时
,就会出现大量“重定义”的错误。在头文件中实用#ifndef #define #endif能避免头文件的重定义。
方法:例如要编写头文件test.h
在头文件开头写上两行:
#ifndef _TEST_H
#define _TEST_H//一般是文件名的大写
头文件结尾写上一行:
这样一个工程文件里同时包含两个test.h时,就不会出现重定义的错误了。
分析:当第一次包含test.h时,由于没有定义_TEST_H,条件为真,这样就会包含(执行)#ifndef _TEST_H和
#endif之间的代码,当第二次包含test.h时前面一次已经定义了_TEST_H,条件为假,#ifndef _TEST_H和
#endif之间的代码也就不会再次被包含,这样就避免了重定义了。
#ifndef __74HC595_H__#define __74HC595_H__
#ifndef _optimizedvector_h // if not define _optimizedvector_h#define _optimizedvector_h // then define _optimizedvector_h
通俗点就是如果没定义_optimizedvector_h,那就定义_optimizedvector_h,防止重复编译
头文件中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编
译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般
格式是这样的:
#ifndef #define ......#endif
在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头
文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,
如:stdio.h#ifndef _STDIO_H_#define _STDIO_H_......#endif2.在#ifndef中定义变量出现的问题(一般不定义在#ifndef中)。#ifndef AAA#define AAA......#endif里面有一个变量定义在vc中链接时就出现了i重复定义的错误,而在c中成功编译。
结论:(1).当你第一个使用这个头的.cpp文件生成.obj的时候,int i 在里面定义了当另外一个使用这个的.cpp再次[单独
]生成.obj的时候,int i 又被定义然后两个obj被另外一个.cpp也include 这个头的,连接在一起,就会出现重复
定义.(2).把源程序文件扩展名改成.c后,VC按照C语言的语法对源程序进行编译,而不是C++。在C语言中,若是遇到多个
int i,则自动认为其中一个是定义,其他的是声明。(3).C语言和C++语言连接结果不同,可能(猜测)时在进行编译的时候,C++语言将全局变量默认为强符号,所以连
接出错。C语言则依照是否初始化进行强弱的判断的。(参考)解决方法:(1).把源程序文件扩展名改成.c。(2).推荐解决方案:.h中只声明在.cpp中定义#ifndef __X_H__#define __X_H__#endif //__X_H__
注意问题:(1).变量一般不要定义在.h文件中。一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就
是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条
件不满足时则编译另一组语句。条件编译命令最常见的形式为:#ifdef 标识符程序段1#else程序段2#endif它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。其中
#else部分也可以没有,即:
#ifdef程序段1#denif
这里的“程序段”可以是语句组,也可以是命令行。这种条件编译可以提高C源程序的通用性。如果一个C源程序在
不同计算机系统上系统上运行,而不同的计算机又有一定的差异。例如,我们有一个数据类型,在Windows平台中,
应该使用long类型表示,而在其他平台应该使用float表示,这样往往需要对源程序作必要的修改,这就降低了程序
的通用性。可以用以下的条件编译:
#ifdef WINDOWS#define MYTYPE long#else#define MYTYPE float#endif如果在Windows上编译程序,则可以在程序的开始加上#define WINDOWS
这样则编译下面的命令行:
#define MYTYPE long
如果在这组条件编译命令之前曾出现以下命令行: #define WINDOWS 0则预编译后程序中的MYTYPE都用float代替。这样,源程序可以不必作任何修改就可以用于不同类型的计算机系统。
当然以上介绍的只是一种简单的情况,可以根据此思路
?a href="javascript:;" onClick="tagshow(event, '&#65533;&#65533;&#65533;&#65533;');">其它的条件编译?
例如,在调试程序时,常常希望输出一些所需的信息,而在调试完成后不再输出这些信息。可以在源程序中插入以
下的条件编译段:
#ifdef DEBUGprint ("device_open(%p) ", file);#endif
如果在它的前面有以下命令行: #define DEBUG
则在程序运行时输出file指针的值,以便调试分析。调试完成后只需将这个define命令行删除即可。有人可能
觉得不用条件编译也可达此目的,即在调试时加一批printf语句,调试后一一将printf语句删除去。的确,这是可
以的。但是,当调试时加的printf语句比较多时,修改的工作量是很大的。用条件编译,则不必一一删改printf语
句,只需删除前面的一条“#define DEBUG”命令即可,这时所有的用DEBUG作标识符的条件编译段都使其中的
printf语句不起作用,即起统一控制的作用,如同一个“开关”一样。
有时也采用下面的形式:#ifndef 标识符程序段1#else程序段2#endif只是第一行与第一种形式不同:将“ifdef”改为“ifndef”。它的作用是:若标识符未被定义则编译程序段1
,否则编译程序段2。这种形式与第一种形式的作用相反。
以上两种形式用法差不多,根据需要任选一种,视方便而定。还有一种形式,就是#if后面的是一个表达式,而不是一个简单的标识符:
#if 表达式程序段1#else程序段2#endif它的作用是:当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,
使程序在不同的条件下执行不同的功能。作用范围就是当前文件啊。因为编译是以cpp或c文件位单位的嘛。还以这个为例:
//正常代码#ifdef _DEBUGTRACE("Some infomation");#else//Now is release version,so do nothing#endif//正常代码
编译时是先把所有的预编译处理展开(比如宏)再编译,所以Debug模式下,编译时的代码是://正常代码TRACE("Some infomation");//正常代码Release模式下的代码是://正常代码//正常代码&
阅读(4492) | 评论(0) | 转发(0) |
相关热门文章
给主人留下些什么吧!~~
请登录后评论。查看: 779|回复: 3
在头文件里面函数说名为什么要用条件编译#ifndef&,#define&#endif原因是什么?
主题帖子精华
初级会员, 积分 82, 距离下一级还需 118 积分
在线时间0 小时
在头文件里函数说明全局变量等定义为什么使用条件编译,我知道条件编译可以防止重复定义,使用预定义命令它的保存空间是在FLASH里面,你们这样写是不是把写好的程序保存到Flash里面吗?不然有些程序多超过了STM32SRAM 20K,而Flash有 128k容量很大,为什么要这样来定义呢?求原子大哥解答!!!!!
1、条件编译在调试程序的时候很是方便
2、不同的功能可以通过条件编译进行切换而不需要每次都修改大量的代码
3、功能比较多的时候,可以通过宏定义进行裁剪
4、缺点就是,需要考虑的东西比较多,开始编写的代码量比较大,费脑,哈哈
主题帖子精华
在线时间104 小时
1、条件编译在调试程序的时候很是方便
2、不同的功能可以通过条件编译进行切换而不需要每次都修改大量的代码
3、功能比较多的时候,可以通过宏定义进行裁剪
4、缺点就是,需要考虑的东西比较多,开始编写的代码量比较大,费脑,哈哈
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
主题帖子精华
在线时间104 小时
首先你要知道为什么要条件编译,好处在哪里,坏处在哪里
现在,程序把烂铜烂铁变得智能化了,人呢,一旦离开了这烂铜烂铁就不知道干啥了
主题帖子精华
初级会员, 积分 82, 距离下一级还需 118 积分
在线时间0 小时
回复【2楼】Badu_Space:
---------------------------------
就是不知道呀,你能指点下么
Powered by

我要回帖

更多关于 ifndef的作用 的文章

 

随机推荐