include的含义(C/C++摘记)
最近开始工作了,涉及的是开源C项目,现在正是一个学习过程。本以为信心满满,以为自己C/C++的基础相当扎实,步入工作才发现,菜得不行。
这种菜,是在阅读项目源代码的过程体现出来的。因为实在是看到了太多的陌生的变量声明或函数声明的方式,看得我真的沉迷了,同时也自卑了。
同时也复习了一遍指针,感觉上理解的层次更高了一层,额,应该说往更底层走了,涉及汇编内容了,还有编译一个程序的过程有了新的认识,对于内存的分配有也了更深刻的理解。
虽然让同事说我指针没学好,呵呵,还是虚心接受吧,还好,遇到的是好同事,它们愿意给我讲解,我很高兴。还有一点,我发现,在使用课本上的一些术语与同事进行沟通的时候,发现会互相误解。。。到底是课本太肤浅还是我学得太肤浅导致我的表达不准确呢?随他去吧。
近日,搜罗了很多自己遇到的疑问的回答,并已进行mark,在这里能够进行一些摘抄的笔记,加油!
下面转自一帖子,原址不便放出,希望对大家有所帮助,对于重要话语,我进行摘录,对一些重要句子进行高亮处理。
#include用于在源代码中引用其他的源代码文件
#include有两种写法:
#include <stdio.h> // 使用<>时,编译器仅在系统目录下搜索被引用的文件
#include “stdio.h” // 使用”"时,编译器除了在系统目录下搜索,还会在当前目录下搜索引用的文件
系统目录包括哪些?
一个是编译器内定的目录,这些目录是内建在编译器内部的,不可改变(如果要追究的话,可以看这个文件gcc/gcc/collect2.c),一般为:
/usr/include
/usr/local/include
/usr/lib/gcc-lib/i386-linux/2.95.2/include
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../include/g++-3
/usr/lib/gcc-lib/i386-linux/2.95.2/../../../../i386-linux/include
可以增加
-nostdinc参数阻止gcc搜索内建路径。
还有一个就是通过-I参数指定的路径
#include的工作原理非常简单,就是将引用文件的内容在#include处展开。
举个例子,有两个文件test.h和main.c
/* test.h */
#ifndef TEST_H
#define TEST_H
#define TEST_STR “This is a test string”
#endif
/* main.c */
#include “test.h”
extern int printf (const char*, …);
int main (int argc, char* argv[])
{
printf(“%s”, TEST_STR);
return 0;
}
那么在编译main.c的时候,#include “test.h”语句被编译器展开,编译器最终看到的main.c应该是这个样子的:
#ifndef TEST_H
#define TEST_H
#define TEST_STR “This is a test string”
#endif
extern int printf (const char*, …);
int main (int argc, char* argv[])
{
printf(“%s”, TEST_STR);
return 0;
}
OK,到这里#include的基本工作原理就清楚了,那么我们为什么要使用#include?
编译器是个傻子,它在碰到不认识的符号的时候就会抱怨,然后罢工。因此,如果我们在源代码中引用了一个外部的对象的时候,会在引用之前增加一个该对象的声明,让编译器先和这个符号熟悉一下,就像extern int printf (const char*, …);
于是,在每个引用到了printf的C文件中,我们都需要加上这么一个声明。一个函数就凑合了,多了怎么办?
对,把这些函数声明都放到stdio.h文件中,这样我只需在每个C文件中写一行#include <stdio.h>,就等同于声明了n个函数。这个偷懒的方法真不错!
由于include可以让我们少写很多代码,这样大家都喜欢include,不光在c文件中include,还在h文件中include。include是可以递归的。这样有时貌似只引用了一个文件,但其实引用了很多个文件。编译器貌似在编译一个十几行代码的C文件,但由于C文件中的头文件被展开,编译器实际上是在编译一个几千行的源文件,编译器真累的不行。所以大家在写源代码的时候务必也照顾一下编译器的感受,不需要的头文件就不要引用了。
同时,由于include可以递归,我们在包含一个头文件的时候往往不能判断出到底包含了那些文件,文件的重复包含无法避免。重复包含头文件是有害的,有时候编译器会向你抱怨某个数据类型或数据结构被重复定义。为了避免头文件被重复包含,我们往往使用下面的技巧:
#ifndef TEST_H
#define TEST_H
/* your file goes here */
#endif
我们看看它的工作原理,如果代码如下
#include “test.h”
#include “test.h”
extern int printf (const char*, …);
int main (int argc, char* argv[])
{
printf(“%s”, TEST_STR);
return 0;
}
那么编译时会被展开为:
#ifndef TEST_H /* 这里TEST_H还没有被define,条件为true,下面的代码会被编译到 */
#define TEST_H /* 这里define了,TEST_H */
#define TEST_STR “This is a test string”
#endif /* #ifndef TEST_H 条件结束 */
#ifndef TEST_H /* 这里TEST_H在上面被define,条件为false,下面的代码会被跳过 */
#define TEST_H /* 这里不会被编译 */
#define TEST_STR “This is a test string” /* 这里不会被编译 */
#endif /* 这里不会被编译, #ifndef TEST_H 条件结束 */
extern int printf (const char*, …);
int main (int argc, char* argv[])
{
printf(“%s”, TEST_STR);
return 0;
}
OK,最后我们来说说c文件和h文件。
对编译器来说,它并不区分c文件和h文件,编译器会一视同仁的认为是源代码文件,即包含源代码的文本文件。
程序员们则约定,将编译器编译的对象命名为.c文件,将上面说的,用来偷懒给别人引用的文件命名为.h文件。
由于h文件是用来被引用的,且往往不能确定被多少人引用,因此,我们在h文件中放的是不会参数数据实体的,比如声明和宏。而产生数据实体的定义则被放在c文件中。试想,如果我们在一个h文件中定义一个变量a,那么凡是引用了这个h文件的c文件中都会定义一个a,编译器到无所谓,为每个c编译出来的o文件分配一个a,而链接器却会向你抱怨a太多了,都不知道用哪个好。
牛逼
文章很好,很实用,感谢1
上面两条评论可以删了
приемлемые расценки сантехнические работы только тут