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;
其实,预处理之后跟第一种方法是一样的