标准库与C中用户定义的头文件(.h)及其实现文件(.c)有何不同? [英] How a standard library differ from user defined header file (.h) and its implementation file (.c) in C?

查看:79
本文介绍了标准库与C中用户定义的头文件(.h)及其实现文件(.c)有何不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在main.c中使用#include <stdio.h>包含的标准库(如libc.a(静态库))与main.c中包含的用户定义头文件(cube.h)及其实现文件(多维数据集)有何不同. c)在C中?

我的意思是两个都是头文件,但是一个人的实现是一个静态库(.a),另一个是源文件(.c).

您将在例如cube.c中拥有定义(实现)

#include "cube.h"

int cube( int x ) {
   return x * x * x;
}

然后,我们将函数声明放入另一个文件中.按照惯例,这是在头文件cube.h中完成的.

int cube( int x );

我们现在可以使用#include指令(它是C预处理程序的一部分)从其他地方(例如main.c)调用该函数.

#include "cube.h"
#include <stdio.h>


int main() {
  int c = cube( 10 );
  printf("%d", c);
  ...
}

此外,如果我在cube.h中包括了包括卫兵,那么当我在main.c和cube.c中都包括cube.h时会发生什么.它将包含在何处?

解决方案

一种编程语言与其实现方式不同.

编程语言是一种规范(写在纸上;您应该阅读翻译单元.

您的 GCC 编译器进程 #include 和其他指令并扩展宏).并且gcc不仅运行适当的编译器(某些cc1),而且运行汇编器as 链接器和加载器 预订更多).

出于充分的原因,您的头文件cube.h实际上应该以包括防护措施开头.在您的简单例子中,它们可能没有用(但是您应该养成这种习惯).

实际上,您几乎应该始终使用gcc -Wall -Wextra -g(以获取所有警告和调试信息).阅读有关调用GCC 的章节.

您也可以将-v传递给gcc,以了解实际运行的程序(例如cc1ldas).

您可以将-H传递给gcc,以了解在预处理阶段包含哪些源文件.您还可以获取cube.c的预处理形式,作为通过gcc -C -E cube.c > cube.i获得的cube.i文件,然后使用某些编辑器或寻呼机查看该cube.i文件.

您-或gcc-将需要(在您的示例中)将cube.c(该文件及其每个头文件提供的翻译单元#include -ing)编译为cube.o 目标文件(假设是Linux系统).您还可以将main.c编译为main.o.最后,gcc会链接cube.omain.o和一些启动文件(有关 crt0 )和libc.so共享库(实现POSIX C标准库规范等)来生成可重定位对象文件,共享 build自动化工具,例如 GNU make .

如果我在cube.h中包括了加入保护,那么当我在main.c和cube.c中都包括cube.h时会发生什么?

这些应该是两个不同的翻译单元.您将分几步来编译它们.首先,您使用main.c编译为main.o

 gcc -Wall -Wextra -g -c main.c

,上面的命令正在生成一个main.o对象文件(借助于cc1as)

然后使用

gcc -Wall -Wextra -g -c cube.c

编译(另一个翻译单元)

gcc -Wall -Wextra -g -c cube.c

因此获得cube.o

(请注意,在您的cube.h中添加include防护不会更改将其读取两次的事实,一次是在编译cube.c时一次,另一次是在编译main.c时一次)

最后,使用

将两个目标文件链接到yourprog可执行文件中.

gcc -Wall -Wextra -g cube.o main.o -o yourprog

(我邀请您尝试所有这些命令,并使用gcc -v而不是上面的gcc尝试它们.)

请注意,gcc -Wall -Wextra -g cube.c main.c -o yourprog正在运行以上所有步骤(请检查gcc -v).您确实应该编写一个 Makefile 以避免键入所有这些命令(只需使用make,甚至更好的make -j以并行运行编译.)

最后,您可以使用./yourprog运行可执行文件(但请阅读有关 PATH ),但您应该学习如何使用gdb并尝试gdb ./yourprog.

cube.h将包含在哪里?

它将同时包含在两个翻译部门中;运行gcc -Wall -Wextra -g -c main.c时一次,运行gcc -Wall -Wextra -g -c cube.c时另一次.请注意,目标文件(cube.omain.o)不包含附带的头.他们的调试信息(格式为 DWARF 格式)保留了该包含项(例如,包含的路径,而不是内容)头文件).

顺便说一句,研究现有的免费软件项目(并研究其中的一些源代码,至少是为了灵感).您可以查看GNU glibc Linux内核提供和实现.例如,printf最终有时有时会使用 write(2),但它是缓冲(请参见模块这样的编程语言(例如Ocaml,Go等). C不是一个.

How a standard library like libc.a (static library) which is included using #include <stdio.h> in our main.c differ from user defined header file (cube.h) included in main.c with its implementation file (cube.c) in C ?

I mean both are header files but one's implementation is a static library (.a) and others is source file (.c) .

You would have the definition (implementation) in, say, cube.c

#include "cube.h"

int cube( int x ) {
   return x * x * x;
}

Then we'll put the function declaration in another file. By convention, this is done in a header file, cube.h in this case.

int cube( int x );

We can now call the function from somewhere else, main.c for instance, by using the #include directive (which is part of the C preprocessor) .

#include "cube.h"
#include <stdio.h>


int main() {
  int c = cube( 10 );
  printf("%d", c);
  ...
}

Also if I included include guards in cube.h what would happen when I include cube.h in both main.c and cube.c . Where it will get included?

解决方案

A programming language is not the same as its implementation.

A programming language is a specification (written on paper; you should read n1570, which practically is the C11 standard), it is not a software. The C standard specifies a C standard library and defines the headers to be #include-d.

(you could run your C program with a bunch of human slaves and without any computers; that would be very unethical; you could also use some interpreter like Ch and avoid any compiler or object or executable files)

How a standard library like libc.a (static library) which is included using #include <stdio.h> ... differs from a user file cube.c

The above sentence is utterly wrong (and makes no sense). libc.a does not #include -or is not included by- the <stdio.h> header (i.e. file /usr/include/stdio.h and other internal headers e.g. /usr/include/bits/stdio2.h). That inclusion happens when you compile your main.c or cube.c.

In principle, <stdio.h> might not be any file on your computer (e.g. #include <stdio.h> could trigger some magic in your compiler). In practice, the compiler is parsing /usr/include/stdio.h (and other included files) when you #include <stdio.h>.

Some standard headers (notably <setjmp.h>, <stdreturn.h>, <stdarg.h>, ....) are specified by the standard but are implemented with the help of special builtins or attributes (that is "magic" things) of the GCC compiler.

The C standard knows about translation units.

Your GCC compiler processes source files (grossly speaking, implementing translation units) and starts with a preprocessing phase (processing #include and other directives and expanding macros). And gcc runs not only the compiler proper (some cc1) but also the assembler as and the linker ld (read Levine's Linkers and Loaders book for more).

For good reasons, your header file cube.h should practically start with include guards. In your simplistic example they are probably useless (but you should get that habit).

You practically should almost always use gcc -Wall -Wextra -g (to get all warnings and debug info). Read the chapter about Invoking GCC.

You may pass also -v to gcc to understand what programs (e.g. cc1, ld, as) are actually run.

You may pass -H to gcc to understand what source files are included during preprocessing phase. You can also get the preprocessed form of cube.c as the cube.i file obtained with gcc -C -E cube.c > cube.i and later look into that cube.i file with some editor or pager.

You -or gcc- would need (in your example) to compile cube.c (the translation unit given by that file and every header files it is #include-ing) into the cube.o object file (assuming a Linux system). You would also compile main.c into main.o. At last gcc would link cube.o, main.o, some startup files (read about crt0) and the libc.so shared library (implementing the POSIX C standard library specification and a bit more) to produce an executable. Relocatable object files, shared libraries (and static libraries, if you use some) and executables use the ELF file format on Linux.

If you code a C program with several source files (and translation units) you practically should use a build automation tool like GNU make.

If I included include guards in cube.h what would happen when I include cube.h in both main.c and cube.c ?

These should be two different translation units. And you would compile them in several steps. First you compile main.c into main.o using

 gcc -Wall -Wextra -g -c main.c

and the above command is producing a main.o object file (with the help of cc1 and as)

Then you compile (another translation unit) cube.c using

gcc -Wall -Wextra -g -c cube.c

hence obtaining cube.o

(notice that adding include guards in your cube.h don't change the fact that it would be read twice, once when compiling cube.c and the other time when compiling main.c)

At last you link both object files into yourprog executable using

gcc -Wall -Wextra -g cube.o main.o -o yourprog

(I invite you to try all these commands, and also to try them with gcc -v instead of gcc above).

Notice that gcc -Wall -Wextra -g cube.c main.c -o yourprog is running all the steps above (check with gcc -v). You really should write a Makefile to avoid typing all these commands (and just compile using make, or even better make -j to run compilation in parallel).

Finally you can run your executable using ./yourprog (but read about PATH), but you should learn how to use gdb and try gdb ./yourprog.

Where it cube.h will get included?

It will get included at both translation units; once when running gcc -Wall -Wextra -g -c main.c and another time when running gcc -Wall -Wextra -g -c cube.c. Notice that object files (cube.o and main.o) don't contain included headers. Their debug information (in DWARF format) retains that inclusion (e.g. the included path, not the content of the header file).

BTW, look into existing free software projects (and study some of their source code, at least for inspiration). You might look into GNU glibc or musl-libc to understand what a C standard library really contains on Linux (it is built above system calls, listed in syscalls(2), provided and implemented by the Linux kernel). For example printf would ultimately sometimes use write(2) but it is buffering (see fflush(3)).

PS. Perhaps you dream of programming languages (like Ocaml, Go, ...) knowing about modules. C is not one.

这篇关于标准库与C中用户定义的头文件(.h)及其实现文件(.c)有何不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆