您的位置:首页 > 其它

防止全局变量、头文件重复包含与 extern 的使用

2012-08-17 21:25 399 查看
C语言中经常会出现文件重复包含而导致全局变量重复定义,下面以一个例子说明

-----Makefile

CC = gcc
OBJS = main.o errhandle.o strcopy.o
CFLAGS = -Wall -std=c99
main: ${OBJS}	# or $(OBJS), ${OBJS} is shell style
${CC} ${CFLAGS} -o $@ ${OBJS}
main.o: main.c errhandle.h strcopy.h
${CC} ${CFLAGS} -c main.c
errhandle.o: errhandle.c errhandle.h
${CC} ${CFLAGS} -c errhandle.c
strcopy.o: strcopy.c strcopy.h errhandle.h
${CC} ${CFLAGS} -c strcopy.c
clean:
rm -f main *.o


-----main.c

#include <stdio.h>
#include "errhandle.h"
#include "strcopy.h"

#define DESTSIZE	10

int main(int argc, char *argv[])
{
char dest[DESTSIZE];
char *src = "1234567890";
if(strcopy(dest, DESTSIZE, src) != NULL)
{
printf("%s\n", dest);
}
else
{
printerr();
}

if(strcopy(dest, DESTSIZE, NULL) != NULL)
{
printf("%s\n", dest);
}
else
{
printerr();
}

return 0;
}
-----errhandle.h

/*
* error handle
*/
#ifndef _ERRHANDLE_H
#define _ERRHANDLE_H	// to avoid duplicate(multiple) inclusion or declare(definition) of the header file

#ifdef __cplusplus
extern "C" {
#endif

extern int errcode;

#define ERR_SHORT			1	/* The dest string is too short */
#define ERR_SRCNULL			2	/* The src string pointer is NULL */
#define ERR_DESTNULL		3	/* The dest string pointer is NULL */

extern void printerr();

#ifdef __cplusplus	/* extern "C" */
}
#endif

#endif	/* _ERRHANDLE_H */
-----errhandle.c

#include <stdio.h>
#include "errhandle.h"

int errcode;

void printerr()
{
if(ERR_SHORT == errcode)
{
printf("The dest string is too short\n");
}
else if(ERR_SRCNULL == errcode)
{
printf("The src string pointer is NULL\n");
}
else if(ERR_DESTNULL == errcode)
{
printf("The dest string pointer is NULL\n");
}
}
-----strcopy.h

#ifndef _STRCOPY_H
#define _STRCOPY_H	// to avoid duplicate(multiple) inclusion or declare(definition) of the header file

#ifdef __cplusplus
extern "C" {
#endif

extern char *strcopy(char *dest, unsigned int dest_len, const char *src);

#ifdef __cplusplus	/* extern "C" */
}
#endif

#endif	/* _STRCOPY_H */
-----strcopy.c

#include <string.h>
#include "errhandle.h"
#include "strcopy.h"

char *strcopy(char *dest, unsigned int dest_len, const char *src)
{
char *dest_p = NULL;
int i;
if(dest == NULL)
{
errcode = ERR_DESTNULL;
return NULL;
}
if(src == NULL)
{
errcode = ERR_SRCNULL;
return NULL;
}
if(dest_len < strlen(src) + 1)
{
errcode = ERR_SHORT;
return NULL;
}
for(i = 0, dest_p = dest; src[i] != '\0'; i++, dest_p++)
{
*dest_p = src[i];
}
*dest_p = '\0';

return dest;
}
这个例子中 strcopy.c 和 errhandle.c 都要用到 errcode 这个全局变量,其它依赖关系见上面的代码

【说明】

1、以下这种方式可以防止头文件重复包含或定义:

#ifndef _ERRHANDLE_H
#define _ERRHANDLE_H	// to avoid duplicate(multiple) inclusion or declare(definition) of the header file
...
#endif	/* _ERRHANDLE_H */
2、关于extern "C" 的说明可以见这里:关于extern "C",http://effective-c.googlecode.com/files/effective-c.pdf

#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus	/* extern "C" */
}
#endif

3、在 errhandle.h 中声明 extern int errcode;而在 errhandle.c 中声明int
errcode;这样即可解决 errcode 因重复包含导致的 "multiple definition"。这种用法在标准C库的 errno.h 和 errno.c 中能见到。

--------------------------------------------------------------------------------------------------------------------------------------

下面介绍另一种方法,参考文章http://www.4ucode.com/Study/Topic/951741

只修改 errhandle.h 和 errhandle.c 2个文件

-----errhandle.h

/*
* error handle
*/
#ifndef _ERRHANDLE_H
#define _ERRHANDLE_H	// to avoid duplicate(multiple) inclusion or declare(definition) of the header file

#ifdef __cplusplus
extern "C" {
#endif

#ifdef ERRHANDLE_GLOBALS
#define ERRHANDLE_EXT
#else
#define ERRHANDLE_EXT	extern
#endif

ERRHANDLE_EXT int errcode;

#define ERR_SHORT			1	/* The dest string is too short */
#define ERR_SRCNULL			2	/* The src string pointer is NULL */
#define ERR_DESTNULL		3	/* The dest string pointer is NULL */

extern void printerr();

#ifdef __cplusplus	/* extern "C" */
}
#endif

#endif	/* _ERRHANDLE_H */


-----errhandle.c

#define ERRHANDLE_GLOBALS	/* 这条语句必须位于 #include "errhandle.h" 的前面 */
#include <stdio.h>
#include "errhandle.h"

void printerr()
{
if(ERR_SHORT == errcode)
{
printf("The dest string is too short\n");
}
else if(ERR_SRCNULL == errcode)
{
printf("The src string pointer is NULL\n");
}
else if(ERR_DESTNULL == errcode)
{
printf("The dest string pointer is NULL\n");
}
}
【说明】

errhandle.h 中改变的内容:

#ifdef ERRHANDLE_GLOBALS
#define ERRHANDLE_EXT
#else
#define ERRHANDLE_EXT	extern
#endif

ERRHANDLE_EXT int errcode;
errhandle.c 中去掉了int errcode;  的声明,增加了:

#define ERRHANDLE_GLOBALS	/* 这条语句必须位于 #include "errhandle.h" 的前面 */
这种方法的原理很简单:

定义过 ERRHANDLE_GLOBALS 的文件: ERRHANDLE_EXT int errcode; == int errcode; 

未定义过 ERRHANDLE_GLOBALS 的文件: ERRHANDLE_EXT int errcode; == extern int errcode; 

这样,就只有一份 int errcode; 的声明在 errhandle.c 中,其它文件均为 extern int errcode; 

其实,预处理之后跟第一种方法是一样的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  string null header file ext