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