gcc 如何处理本地包含的文件?
How does gcc handle local included files?
这真的不应该是我必须要问的问题,但不知怎的,我通过搜索来回答我的问题却一无所获。
为了再问一个问题,我做了三个文件:main.c、sub.c和sub.h。 main.c 具有 'main' 函数,而 sub.c 仅包含函数定义。
最初,main.c 将“#include "sub.h"”作为其唯一的包含语句。
尝试 'gcc main.c -O3 -o test' 导致错误,表明函数 f()(在 [=37= 中声明]、在 sub.c 中定义并在 main.c 中引用)是未引用。
尝试 'gcc main.c sub.c -O3 -o test' 导致了预期的行为。
然后我修改了 test.c,删除了 #include 和对 f 的引用。 'gcc main.c -O3 -o test2' 按预期工作。
然后我重新添加了对 f 的引用,忘记重新添加 #include。尽管如此,'gcc main.c sub.c -O3 -o test3' 仍按预期工作。
我注意到这个错误,并故意将包含重新添加为“#include sub.c”。 'gcc main.c sub.c -O3 -o test4' 导致错误,说 f() 被定义了多次。 'gcc main.c -O3 -o test4 恢复正常工作。
我唯一能得出的结论是,就本地文件而言,如果文件是源代码文件,则包含它,不要将其添加到命令中,否则将其源添加到命令,不要费心包含它,因为显然包含与否并不重要。我猜?
基本上我的问题是,上面的行为是不是有意的,如果是的话,它被记录在案,在哪里,所以我可以阅读它并在未来如何处理我包含的文件方面做出更明智的决定。
如果你编译一个程序,那么 main 函数应该在那里。如果你编译一个没有main的程序,那么你只能生成目标文件。在您的情况下,看起来 main.c 包含 main() 并且 sub.c 包含一些函数定义。另一件重要的事情是当你定义一个头文件时,确保你有预处理器指令来防止多次包含头文件。
示例如下:
http://www.tutorialspoint.com/cprogramming/c_header_files.htm
I then re-added the references to f, and forgot to re-add the #include. Despite this, 'gcc main.c sub.c -O3 -o test3' worked as expected.
对于"worked"适当宽松的定义;我敢打赌 f()
return 是 int
,并且 gcc
默认为 C89 模式。
在 C99 之前,如果编译器在看到函数定义或声明之前遇到函数 call,它假定被调用的函数 returned 一个 int
。因此,只要 f()
实际上 return 是 int
,您的代码就会编译并 运行 成功。如果 f()
不 return 和 int
代码仍然可以编译,但是你会遇到 运行 时间问题。链接器只关心符号是否存在;它不关心类型不匹配。
C99 取消了隐式 int
类型,因此在 C99 编译器下,如果您在范围内没有 f()
的声明(通过包含 sub.h
或手动添加声明)。
The only conclusion I can draw from this is that as far as local files are concerned, if the file is a source code file then include it and don't add it to the command, else add its source to the command and don't bother including it, because apparently it doesn't matter whether you include it or not. I guess?
这是完全错误的结论。您不 想将一个 .c
文件包含在另一个 .c
文件中作为常规做法,因为它会导致各种混乱。 main.c
中的所有内容都对 sub.c
可见,反之亦然,从而导致潜在的命名空间冲突 - 例如,两个文件都可以定义一个名为 foo()
的 "local" 辅助函数。通常这样的 "local" 函数在源文件之外是不可见的,但是通过将一个源文件包含在另一个源文件中,foo()
的两个版本都是可见的并且相互冲突。另一个问题是,如果 .c
文件包含另一个 .c
文件,而另一个 .c
文件包含另一个 .c
文件等,您可能会得到一个编译器无法处理的翻译单元。每次在不需要的地方更改一个或另一个文件时,您将最终重新编译 both 个文件。一直以来都是不好的做法。
正确的做法是分别编译 main.c
和 sub.c
并确保 sub.h
包含在 both 中(你想在 sub.c
中包含 sub.h
以确保您的声明符合您的定义;如果不符合,编译器将在翻译 sub.c
时出错)。
编辑
在评论中回答以下问题:
When you say to compile main.c and sub.c separately, I'm assuming you mean to make object files out of them each individually and then link them (3 commands total)? Is there any way to do that with a single command?
命令gcc -o test main.c sub.c
做同样的事情,只是不将相应的目标文件保存到磁盘。您还可以创建一个简单的 Makefile,如下所示:
CC=gcc
CFLAGS=-O3 -std=c99 -pedantic -Wall -Werror
SRCS=main.c sub.c
OBJS=$(SRCS:.c=.o)
test: $(OBJS)
$(CC) -o $@ $(CFLAGS) $(OBJS)
clean:
rm -rf test $(OBJS)
然后你需要做的就是输入make test
:
[fbgo448@n9dvap997]~/prototypes/simplemake: make test
gcc -O3 -std=c99 -pedantic -Wall -Werror -c -o main.o main.c
gcc -O3 -std=c99 -pedantic -Wall -Werror -c -o sub.o sub.c
gcc -o test -O3 -std=c99 -pedantic -Wall -Werror main.o sub.o
从 .c 文件构建目标文件有隐式规则,因此您不需要在 Makefile 中包含这些规则。您需要做的就是指定目标和先决条件。
您可能需要删除 -pedantic
标志才能使用某些特定于平台的实用程序,并且您可能还需要指定不同的标准(c89、gnu89 等)。不过,您肯定会希望保留 -Wall -Werror
标志 - 它们将启用所有警告并将所有警告视为错误;他们会强制 你处理警告。
这真的不应该是我必须要问的问题,但不知怎的,我通过搜索来回答我的问题却一无所获。
为了再问一个问题,我做了三个文件:main.c、sub.c和sub.h。 main.c 具有 'main' 函数,而 sub.c 仅包含函数定义。
最初,main.c 将“#include "sub.h"”作为其唯一的包含语句。
尝试 'gcc main.c -O3 -o test' 导致错误,表明函数 f()(在 [=37= 中声明]、在 sub.c 中定义并在 main.c 中引用)是未引用。 尝试 'gcc main.c sub.c -O3 -o test' 导致了预期的行为。
然后我修改了 test.c,删除了 #include 和对 f 的引用。 'gcc main.c -O3 -o test2' 按预期工作。
然后我重新添加了对 f 的引用,忘记重新添加 #include。尽管如此,'gcc main.c sub.c -O3 -o test3' 仍按预期工作。
我注意到这个错误,并故意将包含重新添加为“#include sub.c”。 'gcc main.c sub.c -O3 -o test4' 导致错误,说 f() 被定义了多次。 'gcc main.c -O3 -o test4 恢复正常工作。
我唯一能得出的结论是,就本地文件而言,如果文件是源代码文件,则包含它,不要将其添加到命令中,否则将其源添加到命令,不要费心包含它,因为显然包含与否并不重要。我猜?
基本上我的问题是,上面的行为是不是有意的,如果是的话,它被记录在案,在哪里,所以我可以阅读它并在未来如何处理我包含的文件方面做出更明智的决定。
如果你编译一个程序,那么 main 函数应该在那里。如果你编译一个没有main的程序,那么你只能生成目标文件。在您的情况下,看起来 main.c 包含 main() 并且 sub.c 包含一些函数定义。另一件重要的事情是当你定义一个头文件时,确保你有预处理器指令来防止多次包含头文件。 示例如下:
http://www.tutorialspoint.com/cprogramming/c_header_files.htm
I then re-added the references to f, and forgot to re-add the #include. Despite this, 'gcc main.c sub.c -O3 -o test3' worked as expected.
对于"worked"适当宽松的定义;我敢打赌 f()
return 是 int
,并且 gcc
默认为 C89 模式。
在 C99 之前,如果编译器在看到函数定义或声明之前遇到函数 call,它假定被调用的函数 returned 一个 int
。因此,只要 f()
实际上 return 是 int
,您的代码就会编译并 运行 成功。如果 f()
不 return 和 int
代码仍然可以编译,但是你会遇到 运行 时间问题。链接器只关心符号是否存在;它不关心类型不匹配。
C99 取消了隐式 int
类型,因此在 C99 编译器下,如果您在范围内没有 f()
的声明(通过包含 sub.h
或手动添加声明)。
The only conclusion I can draw from this is that as far as local files are concerned, if the file is a source code file then include it and don't add it to the command, else add its source to the command and don't bother including it, because apparently it doesn't matter whether you include it or not. I guess?
这是完全错误的结论。您不 想将一个 .c
文件包含在另一个 .c
文件中作为常规做法,因为它会导致各种混乱。 main.c
中的所有内容都对 sub.c
可见,反之亦然,从而导致潜在的命名空间冲突 - 例如,两个文件都可以定义一个名为 foo()
的 "local" 辅助函数。通常这样的 "local" 函数在源文件之外是不可见的,但是通过将一个源文件包含在另一个源文件中,foo()
的两个版本都是可见的并且相互冲突。另一个问题是,如果 .c
文件包含另一个 .c
文件,而另一个 .c
文件包含另一个 .c
文件等,您可能会得到一个编译器无法处理的翻译单元。每次在不需要的地方更改一个或另一个文件时,您将最终重新编译 both 个文件。一直以来都是不好的做法。
正确的做法是分别编译 main.c
和 sub.c
并确保 sub.h
包含在 both 中(你想在 sub.c
中包含 sub.h
以确保您的声明符合您的定义;如果不符合,编译器将在翻译 sub.c
时出错)。
编辑
在评论中回答以下问题:
When you say to compile main.c and sub.c separately, I'm assuming you mean to make object files out of them each individually and then link them (3 commands total)? Is there any way to do that with a single command?
命令gcc -o test main.c sub.c
做同样的事情,只是不将相应的目标文件保存到磁盘。您还可以创建一个简单的 Makefile,如下所示:
CC=gcc
CFLAGS=-O3 -std=c99 -pedantic -Wall -Werror
SRCS=main.c sub.c
OBJS=$(SRCS:.c=.o)
test: $(OBJS)
$(CC) -o $@ $(CFLAGS) $(OBJS)
clean:
rm -rf test $(OBJS)
然后你需要做的就是输入make test
:
[fbgo448@n9dvap997]~/prototypes/simplemake: make test
gcc -O3 -std=c99 -pedantic -Wall -Werror -c -o main.o main.c
gcc -O3 -std=c99 -pedantic -Wall -Werror -c -o sub.o sub.c
gcc -o test -O3 -std=c99 -pedantic -Wall -Werror main.o sub.o
从 .c 文件构建目标文件有隐式规则,因此您不需要在 Makefile 中包含这些规则。您需要做的就是指定目标和先决条件。
您可能需要删除 -pedantic
标志才能使用某些特定于平台的实用程序,并且您可能还需要指定不同的标准(c89、gnu89 等)。不过,您肯定会希望保留 -Wall -Werror
标志 - 它们将启用所有警告并将所有警告视为错误;他们会强制 你处理警告。