C_如何组织C语言的源文件结构/头文件内容组织/链接编译多个文件/引用外部其他源文件中的对象的方法#include & extern/extern的主要作用
文章目录
C_引用外部其他源文件中的对象的方法include & extern/extern的主要作用
概要
- #include将被include的文件导入到使用#include的文件中
- 尽管如此,如果使用了预编译处理(preprocessor),引入的全部内容可能只有一部分会被编译(如果在本次编译中的多个c源文件中有超过一个源文件include过相同的头文件)
- extern:extern对于源文件中定义的对象的做作用域和static恰好相反
试验代码(链接编译多个文件)
使用extern,允许你在不通过include的情况下使用其他文件中定义的对象
-
注意,在编译的时候,需要连同main文件(即,定义main())的源文件)以及定义了被引用对象的源文件一起告诉
gcc
-
gcc m.c b.c multiply.c -o mbm
-
本试验指定了主程序源文件
m.c
- 主程序中,通过
#include
将头文件cxxu.h
导入
- 主程序中,通过
-
两个提供函数和外部变量的源文件
b.c
&multiply.c
-
cxxu.h(该头文件中包含了一些调试宏)
// 数值调试宏 #ifndef CXXU #define CXXU 1 #define dprint(expr) printf(#expr " = %d\n", expr) #define gprint(expr) printf(#expr " = %g\n", expr) #define fprint(expr) printf(#expr " = %f\n", expr) #define sprint(expr) printf(#expr " = %s\n", expr) #endif -
multiply.c
-
#ifndef MULTIPLY #define MULTIPLY #include <stdio.h> int multiply(int a, int b) { return a * b; } #endif
-
-
b.c
-
#include <stdio.h> const int num = 5; const char *str_multiplier="multiplier"; void func() { printf("fun in a.c\n"); }
-
-
m.c(定义程序入口(main()函数))
-
#include <stdio.h> #include "cxxu.h" int main() { //这里将extern声明写在了m.c文件中,当然,也可以将他们放到导入的头文件中(编译语句命令行不变) extern void func(); extern int multiply(int a, int b); extern char *str_multiplier; // 调用func()打印出实际定义函数体的源文件(b.c 文件中) func(); // multiply()定义在multiply.c文件中. int product = multiply(1, 5); // 打印调用结果(乘积) dprint(product); // 打印外部字符串 sprint(str_multiplier); return 0; }
-
-
运行程序
./mbm
-
结果
-
┌─[cxxu@cxxuAli] - [~/cppCodes] - [2022-04-23 09:12:22] └─[0] <git:(master dc0fc40✗✱✈) > gcc m.c b.c multiply.c -o mbm ┌─[cxxu@cxxuAli] - [~/cppCodes] - [2022-04-23 09:17:37] └─[0] <git:(master dc0fc40✗✱✈) > ./mbm fun in a.c product = 5 str_multiplier = multiplier
-
-
当然,一般将
extern
所声明的内容写在某个头文件中
,可以保持main.c的整洁,以及提高方便复用 -
使用extern,可以只把定义对象的文件中的指定对象(通过声明&名称)引入到本程序的编译,而不会访问过多的内容,导致异常(例如对象冲突),在大程序中尤为如此
-
-
-
如果既没有用
#include
将提供被引用对象的定义的源文件导入,又没有使用extern 在主程序中显示声明,那么即使在编译文件中提到所有文件,还是无法通过编译(找不到被引用的对象)
组织C语言的源文件结构/头文件内容组织
以下简单提一下基本的组织原则,具体的可以参考tlpi(The Linux Programming Interface)源代码中的组织方式
- 下载地址:Source code for “The Linux Programming Interface” (man7.org)
- 或者到github/gitee搜索源代码
The C(K&R)中也简单提到过C程序源码的组织方式
头文件.h
只在其中编写最公用的内容(#include
,#define
,extern 函数原型的声明
)(而不要在头文件中编写函数的实现,函数(函数家族)的实现应当创建相应的.c
文件作为库(lib文件),(使用这些库文件(中的函数)的方式不一)- 编写头文件的时候,建议用预编译处理来包裹全部的头文件内容,放置重复
#include
示例
get_num.h
/*************************************************************************\ * Copyright (C) Michael Kerrisk, 2022. * * * * This program is free software. You may use, modify, and redistribute it * * under the terms of the GNU Lesser General Public License as published * * by the Free Software Foundation, either version 3 or (at your option) * * any later version. This program is distributed without any warranty. * * See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. * \*************************************************************************/ /* Listing 3-5 */ /* get_num.h Header file for get_num.c. */ #ifndef GET_NUM_H #define GET_NUM_H #define GN_NONNEG 01 /* Value must be >= 0 */ #define GN_GT_0 02 /* Value must be > 0 */ /* By default, integers are decimal */ #define GN_ANY_BASE 0100 /* Can use any base - like strtol(3) */ #define GN_BASE_8 0200 /* Value is expressed in octal */ #define GN_BASE_16 0400 /* Value is expressed in hexadecimal */ long getLong(const char *arg, int flags, const char *name); int getInt(const char *arg, int flags, const char *name); #endif
get_num.c
/*************************************************************************\ * Copyright (C) Michael Kerrisk, 2022. * * * * This program is free software. You may use, modify, and redistribute it * * under the terms of the GNU Lesser General Public License as published * * by the Free Software Foundation, either version 3 or (at your option) * * any later version. This program is distributed without any warranty. * * See the files COPYING.lgpl-v3 and COPYING.gpl-v3 for details. * \*************************************************************************/ /* Listing 3-6 */ /* get_num.c Functions to process numeric command-line arguments. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <errno.h> #include "get_num.h" /* Print a diagnostic message that contains a function name ('fname'), the value of a command-line argument ('arg'), the name of that command-line argument ('name'), and a diagnostic error message ('msg'). */ static void gnFail(const char *fname, const char *msg, const char *arg, const char *name) { fprintf(stderr, "%s error", fname); if (name != NULL) fprintf(stderr, " (in %s)", name); fprintf(stderr, ": %s\n", msg); if (arg != NULL && *arg != '\0')a fprintf(stderr, " offending text: %s\n", arg); exit(EXIT_FAILURE); } /* Convert a numeric command-line argument ('arg') into a long integer, returned as the function result. 'flags' is a bit mask of flags controlling how the conversion is done and what diagnostic checks are performed on the numeric result; see get_num.h for details. 'fname' is the name of our caller, and 'name' is the name associated with the command-line argument 'arg'. 'fname' and 'name' are used to print a diagnostic message in case an error is detected when processing 'arg'. */ static long getNum(const char *fname, const char *arg, int flags, const char *name) { long res; char *endptr; int base; if (arg == NULL || *arg == '\0') gnFail(fname, "null or empty string", arg, name); base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8 : (flags & GN_BASE_16) ? 16 : 10; errno = 0; res = strtol(arg, &endptr, base); if (errno != 0) gnFail(fname, "strtol() failed", arg, name); if (*endptr != '\0') gnFail(fname, "nonnumeric characters", arg, name); if ((flags & GN_NONNEG) && res < 0) gnFail(fname, "negative value not allowed", arg, name); if ((flags & GN_GT_0) && res <= 0) gnFail(fname, "value must be > 0", arg, name); return res; } /* Convert a numeric command-line argument string to a long integer. See the comments for getNum() for a description of the arguments to this function. */ long getLong(const char *arg, int flags, const char *name) { return getNum("getLong", arg, flags, name); } /* Convert a numeric command-line argument string to an integer. See the comments for getNum() for a description of the arguments to this function. */ int getInt(const char *arg, int flags, const char *name) { long res; res = getNum("getInt", arg, flags, name); if (res > INT_MAX || res < INT_MIN) gnFail("getInt", "integer out of range", arg, name); return res; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2021-04-23 android studio4.+_修改项目使用的sdk版本/sdk列表sdk下载