满目星河不及你什么意思,远不及你 怎么回答这句话怎么回答

C++中的文件输入/输出(1):

首先我將给出一段代码接着再逐行进行解释。我们的第一个程序将建立一个文件并写入一些字符:

这个程序将在当前运行目录下建立一个名為cpp-home.txt的文件,并向它写入“Hello World!”

#include —— 你需要包含此文件以使用C++的文件输入/输出函数。注意:一旦包含了这个文件你不再需要(为了使用cout/cin)包含iostream.h,因为fstream.h已经自动包含了它

1)ofstream即“output file stream(输出文件流)”。它将建立一个句柄(handle)以便我们以后能以一个文件流的形式写入文件。

2)SaveFile —— 这是文件句柄的名字当然,你还可以换用任何一个你想要的名称

3)(“cpp-home.txt”); —— 打开名为cpp-home.txt的文件。如果程序运行的当前目录已经存在这樣一个文件则它将被替换掉;万一不存在,程序也会为你创建一个为文件你不必为此而担心。

ofstream是一个类因此ofstream SaveFile(“cpp-home.txt”);这一语句将创建一個该类的对象;而我们在括号中所传递的参数实际上将传给构造函数:在这里我们将我们要建立的文件的名称作为实际参数传递给了该类嘚构造函数。当然我们还可以传递其它的一些信息,不过我以后再对其进行讲解

“<<”这是一个预定义好的运算符。这行语句所做的昰将上面的那段文本写入文件。SaveFile是一个文件句柄它关联一个打开的流式文件。所以我们只须输入句柄名,再跟着输入“<<”然后接着寫下一串用引号括起来的文本,就可以实现对文件的写入如果我们想写入的是某个变量的值而不是带引号的文本,也只须像通常使用cout << 一樣将变量传递给句柄对象像这样:

SaveFile.close(); —— 既然我们打开了一个流文件,那么当我们用完它之后就必须关闭它。SaveFile是ofstream类的一个对象而该类(ofstream)囿一个用于关闭文件的成员函数,即close() 函数因此,我们只要依次输入文件句柄名点号和close(),就可以关闭该文件!

注意:一旦你关闭文件茬你重新打开它以前,就再不能对它进行访问

以上就是一个可以写文件的最简单程序。的确很容易!不过正如你即将在以后部分的教程中所看到的,还有更多的东西要学呢!

C++中的文件输入/输出(2):读取文件

你已经看到了应该如何写文件现在,当我们已经得到cpp-home.txt文件时我们将要读取它,并且将内容打印在屏幕上

首先,我要指出的是有很多种方法可以读取文件。以后我会向你们介绍所有的方法(就峩所知的)此刻,我先向你展示最佳的方法(我认为的)

正如你已经熟悉的——我将首先给出一段程序代码,然后我会详细地对它進行解释说明:

你想必已经了解首行的意义所在,而剩下的部分将由我为你解释

stream(输出文件流)”。前一节的程序是进行文件的写操作这僦是它用“output(输出)”来表示的原因。而本节的程序则是读取一个文件这就是它用“input(输入)”来表示的原因。这一行剩下的代码于你而言应当昰熟悉的了:OpenFile是ifstream类的一个对象它将关联一个输入文件流;而用引号括住的内容,就是将要打开的文件的名称

请注意:这里没有对要打開的文件是否存在进行测试!以后我将向你指出如何进行检测。

while(!OpenFile.eof()) —— 如果已经到达文件末尾eof( )函数将返回一个非零值。因此我们所设计的這个循环将一直持续直至我们的文件操作到达文件末尾。这样我们就可以遍历整个文件以便对它进行读取。

OpenFile.get(ch); —— OpenFile是类ifstream的一个对象该類声明了一个名为get( )的成员函数。只要我们拥有该对象我们自然就可以调用这个函数。get( )函数从相应的流文件中读出一个字符并将其返回給变量。在本例中get( )函数只带一个参数——用于存储所读取的字符的变量。所以调用OpenFile.get(ch)后程序将会从OpenFile流中读取一个字符并存入变量ch中。

注意:如果你再次调用该函数它将读取下一个字符,而不是原来的那一个!你过后将理解为什么会这样

这就是我们要不断反复循环直至讀操作到达文件尾的原因。每循环一次我们将读出一个字符并将它保存在ch中。

File.close(); —— 我们打开了一个流式文件就需要关闭它。使用close()函数即可将它关闭这和前一节的一样!

注意:一旦你关闭了一个文件,在你重新打开它之前你不能再对它进行访问。

大功告成了!我希望伱能明白我的解释当你编译并运行这个程序的时候,它应当会输出:

C++中的文件输入/输出(3):掌握输入/输出流

在这一章里我会提及一些有用的函数。我将为你演示如何打开一个可以同时进行读、写操作的文件;此外我还将为你介绍其它打开文件的方法,以及如何判断咑开操作是否成功因此,请接着往下读!

到目前为止我已为你所展示的只是单一的打开文件的途径:要么为读取而打开,要么为写入洏打开但文件还可以以其它方式打开。迄今你应当已经认识了下面的方法:

噢,这可不是唯一的方法!正如以前所提到的以上的代碼创建一个类ifstream的对象,并将文件的名字传递给它的构造函数但实际上,还存在有不少的重载的构造函数它们可以接受不止一个的参数。同时还有一个open()函数可以做同样的事情。下面是一个以上代码的示例但它使用了open()函数:

你会问:它们之间有什么区别吗?哦我曾做叻不少测试,结论是没有区别!只不过如果你要创建一个文件句柄但不想立刻给它指定一个文件名那么你可以使用 open()函数过后进行指定。順便再给出一个要使用open()函数的例子:如果你打开一个文件然后关闭了它,又打算用同一个文件句柄打开另一个文件这样一来,你将需偠使用open()函数


据此,只要file1.txt和file2.txt并存储了文本内容你将看到这些内容。

现在该向你演示的是,文件名并不是你唯一可以向open()函数或者构造函數(其实都一样)传递的参数下面是一个函数原型:

你应当知道filename表示文件的名称(一个字符串),而新出现的则是open_mode(打开模式)open_mode的值鼡来定义以怎样的方式打开文件。下面是打开模式的列表:

你写入的所有数据将被追加到文件的末尾此方式使用ios::out

你写入的所有数据将被縋加到文件的末尾,此方式不使用ios::out

删除文件原来已存在的内容(清空文件)

如果要打开的文件并不存在那么以此参数调用open()函数将无法进荇。

如果要打开的文件已存在试图用open()函数打开时将返回一个错误。

以二进制的形式打开一个文件

实际上,以上的值都属于一个枚举类型的int常量但为了让你的编程生涯不至于太痛苦,你可以像上表所见的那样使用那些名称

下面是一个关于如何使用打开模式的例子:

正洳你在表中所看到的:使用ios::ate将会从文件的末尾开始执行写入。如果我没有使用它原来的文件内容将会被重新写入的内容覆盖掉。不过既嘫我已经使用了它那么我只会在原文件的末尾进行添加。所以如果file1.txt原有的内容是这样:

那么执行上面的代码后,程序将会为它添上“That’s new!”因此它看起来将变成这样:

假如你打算设置不止一个的打开模式标志,只须使用OR操作符或者是 | 像这样:

我希望现在你已经明白“咑开模式”是什么意思了!

现在,是时候向你展示一些真正有用的东西了!我敢打赌你现在还不知道应当怎样打开一个可以同时进行读取囷写入操作的文件!下面就是实现的方法:

实际上这只是一个声明语句。我将在下面数行之后给你一个代码示例但此时我首先想提及┅些你应当知道的内容。

上面的代码创建了一个名为“File”的流式文件的句柄如你所知,它是fstream类的一个对象当使用fstream时,你应当指定ios:: in和ios::out作為文件的打开模式这样,你就可以同时对文件进行读、写而无须创建新的文件句柄。噢当然,你也可以只进行读或者写的操作那樣的话,相应地你应当只使用ios::in或者只使用ios::out —— 要思考的问题是:如果你打算这么做为什么你不分别用ifstream及ofstream来实现呢?

下面就先给出示例代碼:

// 此函数将在后面解释

OK这儿又有一些新东西,所以我将逐行进行解释:

static char str[10]; —— 这将创建一个容量为10的字符数组我猜static对你而言或者有些陌生,如果这样就忽略它这只不过会在创建数组的同时对其进行初始化。

File.seekg(ios::beg); —— OK我要让你明白它究竟会做些什么,因此我将以一些有点兒离题、但挺重要的内容开始我的解释

你是不是曾经很想知道那背后真正执行了什么操作?不管是或不是我都将为你解释。这是一个while型循环它会一直反复,直至程序的操作到达文件的尾端但这个循环如何知道是否已经到了文件末尾?嗯当你读文件的时候,会有一個类似于“内置指针(inside-pointer)”的东西它表明你读取(写入也一样)已经到了文件的哪个位置,就像记事本中的光标而每当你调用OpenFile.get(ch)的时候,它会返回当前位置的字符存储在ch 变量中,并将这一内置指针向前移动一个字符因此下次该函数再被调用时,它将会返回下一个字符而这一过程将不断反复,直到读取到达文件尾所以,让我们回到那行代码:函数seekg()将把内置指针定位到指定的位置(依你决定)你可鉯使用:

ios::beg —— 可将它移动到文件首端

ios::end —— 可将它移动到文件末端

或者,你可以设定向前或向后跳转的字符数例如,如果你要向定位到当湔位置的5个字符以前你应当写:

如果你想向后跳过40个字符,则应当写:

同时我必须指出,函数seekg()是被重载的它也可以带两个参数。另┅个版本是这样子的:

“.bat”、“.sh”等后缀

每当命令运荇完后,make 会检测每个命令的返回码如果命令返回成功,那么 make 会执


行下一条命令当规则中所有的命令成功返回后,这个规则就算是成功唍成了如果一个规
则中的某个命令出错了(命令退出码非零),那么 make 就会终止执行当前规则这将有可能

有些时候,命令的出错并不表礻就是错误的例如 mkdir 命令,我们一定需要建立一个目


录如果目录不存在,那么 mkdir 就成功执行万事大吉,如果目录存在那么就出错了。
峩们之所以使用 mkdir 的意思就是一定要有这样的一个目录于是我们就不希望 mkdir 出

为了做到这一点,忽略命令的出错我们可以在 Makefile 的命令行前加┅个减号“-”(在


Tab 键之后),标记为不管命令出不出错都认为是成功的如:

还有一个全局的办法是,给 make 加上“-i”或是“--ignore-errors”参数那么,


Makefile Φ所有命令都会忽略错误而如果一个规则是以“.IGNORE”作为目标的,那么
这个规则中的所有命令将会忽略错误这些是不同级别的防止命令絀错的方法,你可以根据

还有一个要提一下的 make 的参数的是“-k”或是“--keep-going”这个参数的意思是,


如果某规则中的命令出错了那么就终目该規则的执行,但继续执行其它规则

四、嵌套执行 make

在一些大的工程中,我们会把我们不同模块或是不同功能的源文件放在不同的目录中峩们


可以在每个目录中都书写一个该目录的 Makefile,这有利于让我们的 Makefile 变得更加
地简洁而不至于把所有的东西全部写在一个 Makefile 中,这样会很难维護我们的
Makefile这个技术对于我们模块编译和分段编译有着非常大的好处。

例如我们有一个子目录叫 subdir,这个目录下有个 Makefile 文件来指明了这个目录


下文件的编译规则。那么我们总控的 Makefile 可以这样书写:

定义$(MAKE)宏变量的意思是也许我们的 make 需要一些参数,所以定义成一个变量比较


利于維护这两个例子的意思都是先进入“subdir”目录,然后执行 make 命令
Makefile 中(如果你显示的声明),但是不会覆盖下层的 Makefile 中所定义的变量除
非指萣了“-e”参数。

如果你要传递变量到下级 Makefile 中那么你可以使用这样的声明:

如果你不想让某些变量传递到下级 Makefile 中,那么你可以这样声明:

洳果你要传递所有的变量那么,只要一个 export 就行了后面什么也不用跟,表示传递

需要注意的是有两个变量,一个是 SHELL一个是 MAKEFLAGS,这两个變量不管你是否


的参数信息如果我们执行“总控 Makefile”时有 make 参数或是在上层 Makefile 中定
义了这个变量,那么 MAKEFILES 变量将会是这些参数并会传递到下层 Makefile Φ,这
是一个系统级的环境变量

但是 make 命令中的有几个参数并不往下传递,它们是“-C”,“-f”,“-h”“-o”和“-W”


(有关 Makefile 参数的细节将在后面说奣)如果你不想往下层传递参数,那么你可以

如果你定义了环境变量 MAKEFLAGS,那么你得确信其中的选项是大家都会用到的如果其


中有“-t”,“-n”,和“-q”参数,那么将会有让你意想不到的结果或许会让你异常地

还有一个在“嵌套执行”中比较有用的参数,“-w”或是“--print-directory”会在 make


的過程中输出一些信息让你看到目前的工作目录。比如如果我们的下级 make 目录是

而在完成下层 make 后离开目录时,我们会看到:

当你使用“-C”參数来指定 make 下层 Makefile 时“-w”会被自动打开的。如果参数中

如果 Makefile 中出现一些相同命令序列那么我们可以为这些相同的命令序列定义一个变


量。定义这种命令序列的语法以“define”开始以“endef”结束,如:

这里“run-yacc”是这个命令包的名字,其不要和 Makefile 中的变量重名在“define”


和“endef”中的兩行就是命令序列。这个命令包中的第一个命令是运行 Yacc 程序因为
Yacc 程序总是生成“y.tab.c”的文件,所以第二行的命令就是把这个文件改改名字还
是把这个命令包放到一个示例中来看看吧。

我们可以看见要使用这个命令包,我们就好像使用变量一样在这个命令包的使用中,命


令包“run-yacc”中的“$^”就是“foo.y”“$@”就是“foo.c”(有关这种以“$”开
头的特殊变量,我们会在后面介绍)make 在执行命令包时,命令包中的每個命令会被依次

在 Makefile 中的定义的变量就像是 C/C++语言中的宏一样,他代表了一个文本字串


在 Makefile 中执行的时候其会自动原模原样地展开在所使用嘚地方。其与 C/C++所不同
的是你可以在 Makefile 中改变其值。在 Makefile 中变量可以使用在“目标”,“依
赖目标”“命令”或是 Makefile 的其它部分中。

变量的命名字可以包含字符、数字下划线(可以是数字开头),但不应该含有“:”、“#”、


“=”或是空字符(空格、回车等)变量是大小写敏感的,“foo”、“Foo”和“FOO”是三
个不同的变量名传统的 Makefile 的变量名是全大写的命名方式,但我推荐使用大小写搭
配的变量名如:MakeFlags。这样鈳以避免和系统的变量冲突而发生意外的事情。

有一些变量是很奇怪字串如“$<”、“$@”等,这些是自动化变量我会在后面介绍。

变量在声明时需要给予初值而在使用时,需要给在变量名前加上“$”符号但最好用小


括号“()”或是大括号“{}”把变量给包括起来。洳果你要使用真实的“$”字符那么你
需要用“$$”来表示。

变量可以使用在许多地方如规则中的“目标”、“依赖”、“命令”以及新嘚变量中。先看一

变量会在使用它的地方精确地展开就像 C/C++中的宏一样,例如:

当然千万不要在你的 Makefile 中这样干,这里只是举个例子来表奣 Makefile 中的变


量在使用处展开的真实样子可见其就是一个“替代”的原理。

另外给变量加上括号完全是为了更加安全地使用这个变量,在仩面的例子中如果你不想


给变量加上括号,那也可以但我还是强烈建议你给变量加上括号。

在定义变量的值时我们可以使用其它变量来构造变量的值,在 Makefile 中有两种方式来


在用变量定义变量的值

先看第一种方式,也就是简单的使用“=”号在“=”左侧是变量,右侧是變量的值右侧


变量的值可以定义在文件的任何一处,也就是说右侧中的变量不一定非要是已定义好的值,
其也可以使用后面定义的值如:
$(bar)的值是$(ugh),$(ugh)的值是“Huh?”)可见变量是可以使用后面的变量来定

这个功能有好的地方,也有不好的地方好的地方是,我们可以把变量的真实值推到后面来

当“CFLAGS”在命令中被展开时会是“-Ifoo -Ibar -O”。但这种形式也有不好的地


方那就是递归定义,如:

这会让 make 陷入无限的变量展开过程中去当然,我们的 make 是有能力检测这样的定义


并会报错。还有就是如果在变量中使用函数那么,这种方式会让我们的 make 运行时非常
慢更糟糕的是,他会使用得两个 make 的函数“wildcard”和“shell”发生不可预知
的错误因为你不会知道这两个函数会被调用多少次。

为了避免上媔的这种方法我们可以使用 make 中的另一种用变量来定义变量的方法。这种


方法使用的是“:=”操作符如:

值得一提的是,这种方法前面嘚变量不能使用后面的变量,只能使用前面已定义好了的变

那么y 的值是“bar”,而不是“foo bar”

上面都是一些比较简单的变量使用了,让我們来看一个复杂的例子其中包括了 make 的函


数、条件表达式和一个系统变量“MAKELEVEL”的使用:

关于条件表达式和函数,我们在后面再说对于系統变量“MAKELEVEL”,其意思是如果


我们的 make 有一个嵌套执行的动作(参见前面的“嵌套使用 make”),那么这个变量会
记录了我们的当前 Makefile 的调用层數。

下面再介绍两个定义变量时我们需要知道的请先看一个例子,如果我们要定义一个变量


其值是一个空格,那么我们可以这样来:

nullstring 昰一个 Empty 变量其中什么也没有,而我们的 space 的值是一个空格因


为在操作符的右边是很难描述一个空格的,这里采用的技术很管用先用一個 Empty 变量
来标明变量的值开始了,而后面采用“#”注释符来表示变量定义的终止这样,我们可以
定义出其值是一个空格的变量请注意这裏关于“#”的使用,注释符“#”的这种特性值得
我们注意如果我们这样定义一个变量:

dir 这个变量的值是“/foo/bar”,后面还跟了 4 个空格如果峩们这样使用这样变量来指


定别的目录——“$(dir)/file”那么就完蛋了。

还有一个比较有用的操作符是“?=”先看示例:

其含义是,如果 FOO 没有被定義过那么变量 FOO 的值就是“bar”,如果 FOO 先前被定义


过那么这条语将什么也不做,其等价于:

这里介绍两种变量的高级使用方法第一种是變量值的替换。

我们可以替换变量中的共有的部分其格式是“$(var:a=b)”或是“${var:a=b}”,其


意思是把变量“var”中所有以“a”字串“结尾”的“a”替換成“b”字串。这里的“结
尾”意思是“空格”或是“结束符”

这个示例中,我们先定义了一个“$(foo)”变量而第二行的意思是把“$(foo)”中所有


以“.o”字串“结尾”全部替换成“.c”,所以我们的“$(bar)”的值就是“a.c b.c c.c”

另外一种变量替换的技术是以“静态模式”(参见前面章节)萣义的,如:

这依赖于被替换字串中的有相同的模式模式中必须包含一个“%”字符,这个例子同样让

第二种高级用法是——“把变量的徝再当成变量”先看一个例子:

在这个例子中,$(x)的值是“y”所以$($(x))就是$(y),于是$(a)的值就是“z”(注


意,是“x=y”而不是“x=$(y)”)

我们还可鉯使用更多的层次:

这里的$(a)的值是“u”,相关的推导留给读者自己去做吧

让我们再复杂一点,使用上“在变量定义中使用变量”的第一個方式来看一个例子:


也就是“Hello”。

再复杂一点我们再加上函数:


字串替换成“2”字串,于是“variable1”变成“variable2”,再取其值所以,最終

在这种方式中,或要可以使用多个变量来组成一个变量的名字然后再取其值:

再来看看结合第一种技术的例子:

再来看一个这种技術和“函数”与“条件语句”一同使用的例子:

当然,“把变量的值再当成变量”这种技术同样可以用在操作符的左边:

我们可以使用“+=”操作符给变量追加值,如:


使用“+=”操作符可以模拟为下面的这种例子:

所不同的是,用“+=”更为简洁


如果变量之前没有定义过,那么“+=”会自动变成“=”,如果前面有变量定义那么“+=”
会继承于前次操作的赋值符。如果前一次的是“:=”那么“+=”会以“:=”作為其赋值符,

由于前次的赋值符是“=”所以“+=”也会以“=”来做为赋值,那么岂不会发生变量的递

补归定义这是很不好的,所以 make 会自動为我们解决这个问题我们不必担心这个问题。

如果有变量是通常 make 的命令行参数设置的那么 Makefile 中对这个变量的赋值会被忽


略。如果你想茬 Makefile 中设置这类参数的值那么,你可以使用“override”指示符
对于多行的变量定义,我们用 define 指示符在 define 指示符前,也同样可以使用

还有一种设置变量值的方法是使用 define 关键字使用 define 关键字设置变量的值可以


有换行,这有利于定义一系列的命令(前面我们讲过“命令包”的技术就是利用这个关键字)
define 指示符后面跟的是变量的名字,而重起一行定义变量的值定义是以 endef 关键字
结束。其工作方式和“=”操作符一样变量的值可以包含函数、命令、文字,或是其它变
量因为命令需要以[Tab]键开头,所以如果你用 define 定义的命令变量中没有以[Tab]
键开头那么 make 就不会紦其认为是命令。
下面的这个示例展示了 define 的用法:

make 运行时的系统环境变量可以在 make 开始运行时被载入到 Makefile 文件中但是如


果 Makefile 中已定义了这个变量,或是这个变量由 make 命令行带入那么系统的环境变
量的值将被覆盖。(如果 make 指定了“-e”参数那么,系统环境变量将覆盖 Makefile
Makefile 中使用这个变量了这对于我们使用统一的编译参数有比较大的好处。如果
用系统环境变量的值一个共性和个性的统一,很像“全局变量”和“局部變量”的特性

当 make 嵌套调用时(参见前面的“嵌套调用”章节),上层 Makefile 中定义的变量会以


系统环境变量的方式传递到下层的 Makefile 中当然,默認情况下只有通过命令行设置
的变量会被传递。而定义在文件中的变量如果要向下层 Makefile 传递,则需要使用
exprot 关键字来声明(参见前面章節)

当然,我并不推荐把许多的变量都定义在系统环境中这样,在我们执行不用的 Makefile


时拥有的是同一套系统变量,这可能会带来更多的麻烦

前面我们所讲的在 Makefile 中定义的变量都是“全局变量”,在整个文件我们都可以访


问这些变量。当然“自动化变量”除外,如“$<”等这种类量的自动化变量就属于“规则
型变量”这种变量的值依赖于规则的目标和依赖目标的定义。

当然我样同样可以为某个目标设置局部变量,这种变量被称为“Target-specific


Variable”它可以和“全局变量”同名,因为它的作用范围只在这条规则以及连带规则中
所以其值也只在作用范围内有效。而不会影响规则链以外的全局变量的值
是“?=”第二个语法是针对于 make 命令行带入的变量,或是系统环境变量

这个特性非常的有用,当我们设置了这样一个变量这个变量会作用到由这个目标所引发的

在这个示例中,不管全局的$(CFLAGS)的值是什么在 prog 目标,以及其所引发的所有规


标变量中我们知道,变量可以定义在某个目标上模式变量的好处就是,我们可以给定一
种“模式”可以把变量定義在符合这种模式的所有目标上。

我们知道make 的“模式”一般是至少含有一个“%”的,所以我们可以以如下方式给所


有以[.o]结尾的目标定義目标变量:

同样,模式变量的语法和“目标变量”一样:

override 同样是针对于系统环境传入的变量或是 make 命令行指定的变量。

使用条件判断鈳以让 make 根据运行时的不同情况选择不同的执行分支。条件表达式可以


是比较变量的值或是变量和常量的值。

下面的例子判断$(CC)变量是否“gcc”,如果是的话则使用 GNU 函数编译目标。

可见在上面示例的这个规则中,目标“foo”可以根据变量“$(CC)”值来选取不同的函

我们可以从上媔的示例中看到三个关键字:ifeq、else 和 endififeq 的意思表示条件


语句的开始,并指定一个条件表达式表达式包含两个参数,以逗号分隔表达式以圓括号
括起。else 表示条件表达式为假的情况endif 表示一个条件语句的结束,任何一个条件
表达式都应该以 endif 结束

当我们的变量$(CC)值是“gcc”时,目標 foo 的规则是:

而当我们的变量$(CC)值不是“gcc”时(比如“cc”)目标 foo 的规则是:

当然,我们还可以把上面的那个例子写得更简洁一些:

第一个昰我们前面所见过的“ifeq”

比较参数“arg1”和“arg2”的值是否相同当然,参数中我们还可以使用 make 的函数

第二个条件关键字是“ifneq”。语法是:

其比较参数“arg1”和“arg2”的值是否相同如果不同,则为真和“ifeq”类似。

第三个条件关键字是“ifdef”语法是:


<variable-name>同样可以是一个函数的返回徝。注意ifdef 只是测试一个变量是否有
值,其并不会把变量扩展到当前位置还是来看两个例子:

第一个例子中,“$(frobozz)”值是“yes”第二个则昰“no”。


第四个条件关键字是“ifndef”其语法是:
这个我就不多说了,和“ifdef”是相反的意思
做为开始(不然就被认为是命令)。而注释符“#”同样也是安全的“else”和“endif”
也一样,只要不是以[Tab]键开始就行了
特别注意的是,make 是在读取 Makefile 时就计算条件表达式的值并根据条件表達式的
值来选择语句,所以你最好不要把自动化变量(如“$@”等)放入条件表达式中,因为自
动化变量是在运行时才有的
而且,为了避免混乱make 不允许把整个条件语句分成两部分放在不同的文件中。

在 Makefile 中可以使用函数来处理变量从而让我们的命令或是规则更为的灵活囷具有智


能。make 所支持的函数也不算很多不过已经足够我们的操作了。函数调用后函数的返
回值可以当做变量来使用。

函数调用很像變量的使用,也是以“$”来标识的其语法如下:


数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔函数调用以“$”开头,以圓

括号或花括号把函数名和参数括起感觉很像一个变量,是不是函数中的参数可以使用变


量,为了风格的统一函数和变量的括号最恏一样,如使用“$(subst a,b,$(x))”这样
的形式而不是“$(subst a,b,${x})”的形式。因为统一会更清楚也会减少一些不必

在这个示例中,$(comma)的值是一个逗号$(space)使用了$(empty)定義了一个空格,


$(foo)的值是“a b c”$(bar)的定义用,调用了函数“subst”这是一个替换函数,
这个函数有三个参数第一个参数是被替换字串,第二个參数是替换字串第三个参数是替
换操作作用的字串。这个函数也就是把$(foo)中的空格替换成逗号所以$(bar)的值是

名称:字符串替换函数——subst。

洺称:模式字符串替换函数——patsubst


功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否
以包括通配符“%”,表示任意长度的字串如果<replacement>中也包含“%”,那么
来转义,以“\%”来表示真实含义的“%”字符)

返回:函数返回被替换过后的字符串

这和我們前面“变量章节”说过的相关知识有点相似。如:

名称:去空格函数——strip


功能:去掉<string>字串中开头和结尾的空字符。
返回:返回被去掉涳格的字符串值

把字串“a b c ”去到开头和结尾的空格,结果是“a b c”

名称:查找字符串函数——findstring。


返回:如果找到那么返回<find>,否则返回涳字符串

第一个函数返回“a”字符串,第二个返回“”字符串(空字符串)

名称:过滤函数——filter

名称:反过滤函数——filter-out。

名称:排序函数——sort

名称:取单词函数——word。

名称:取单词串函数——wordlist

名称:单词个数统计函数——words。

名称:首单词函数——firstword

以上,是所有的芓符串操作函数如果搭配混合使用,可以完成比较复杂的功能这里,举


一个现实中应用的例子我们知道,make 使用“VPATH”变量来指定“依賴文件”的搜索
路径于是,我们可以利用这个搜索路径来指定编译器对头文件的搜索路径参数 CFLAGS

下面我们要介绍的函数主要是处理文件洺的。每个函数的参数字符串都会被当做一个或是一

名称:取目录函数——dir


功能:从文件名序列<names>中取出目录部分。目录部分是指最后一個反斜杠(“/”)
之前的部分如果没有反斜杠,那么返回“./”
返回:返回文件名序列<names>的目录部分。

名称:取文件函数——notdir


功能:从攵件名序列<names>中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)
返回:返回文件名序列<names>的非目录部分

名称:取后缀函数——suffix。

洺称:取前缀函数——basename

名称:加后缀函数——addsuffix。

名称:加前缀函数——addprefix

名称:连接函数——join。

返回:返回连接过后的字符串

foreach 函数和別的函数非常的不一样。因为这个函数是用来做循环用的Makefile 中

这个函数的意思是,把参数<list>中的单词逐一取出放到参数<var>所指定的变量中然


嘚所返回的每个字符串会以空格分隔,最后当整个循环结束时<text>所返回的每个字符
串所组成的整个字符串(以空格分隔)将会是 foreach 函数的返囙值。
这个参数来依次枚举<list>中的单词举个例子:

上面的例子中,$(name)中的单词会被挨个取出并存到变量“n”中,“$(n).o”每次根


据“$(n)”计算出┅个值这些值以空格分隔,最后作为 foreach 函数的返回所以,

if 函数很像 GNU 的 make 所支持的条件语句——ifeq(参见前面所述的章节)if 函数的

可见,if 函數可以包含“else”部分或是不含。即 if 函数的参数可以是两个也可以

call 函数是唯一一个可以用来创建新的参数化的函数。你可以写一个非常複杂的表达式


这个表达式中,你可以定义许多参数然后你可以用 call 函数来向这个表达式传递参数。

那么foo 的值就是“a b”。当然参数的佽序是可以自定义的,不一定是顺序的如:

此时的 foo 的值就是“b a”。

origin 函数不像其它的函数他并不操作变量的值,他只是告诉你你的这个變量是哪里来


“$”字符Origin 函数会以其返回值来告诉你这个变量的“出生情况”,下面是 origin
如果<variable>是一个默认的定义,比如“CC”这个变量这種变量我们将在后面
如果<variable>是一个命令运行中的自动化变量。关于自动化变量将在后面讲述

这些信息对于我们编写 Makefile 是非常有用的,例如假设我们有一个 Makefile 其包


了一个定义文件 Make.def,在 Make.def 中定义了一个变量“bletch”而我们的环境中
也有一个环境变量“bletch”,此时我们想判断一下,如果變量来源于环境那么我们就
把之重定义了,如果来源于 Make.def 或是命令行等非环境的那么我们就不重新定义它。

我要回帖

更多关于 满目星河不及你什么意思 的文章

 

随机推荐