现在的位置: 首页 > 综合 > 正文

举例——防止全局变量、头文件重复包含

2013年06月27日 ⁄ 综合 ⁄ 共 4056字 ⁄ 字号 评论关闭

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.cerrhandle.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; 

其实,预处理之后跟第一种方法是一样的

抱歉!评论已关闭.