数据库规范化,又称数据库或资料库的正规化、标准化,是数据库设计中的一系列原理和技术,以减少数据库中数据冗余,增进数据的一致性。关系模型的发明者埃德加·科德最早提出这一概念,并于1970年代初定义了第一范式、第二范式和第三范式的概念,还与Raymond F. Boyce于1974年共同定义了第三范式的改进范式——BC范式。

除外还包括针对多值依赖的第四范式,连接依赖的第五范式,DK范式和第六范式。

现在数据库设计最多满足3NF,普遍认为范式过高,虽然具有对数据关系更好的约束性,但也导致数据关系表增加而令数据库IO更易繁忙,原来交由数据库处理的关系约束现更多在数据库使用程序中完成。

范式

对于任何给定的数据库都有很多种表示方法,从完全的范式话到完全的反范式花,以及两者折中。在范式化的数据库中,每个事实数据只会出现一次,相反,在反范式花中,信息是冗余的。范式化就是消除数据的冗余,消除数据依赖。

一个常见的例子,“雇员、部门、部门精灵”的例子:

Employee department leader
Jones Accounting Jones
Smith Engineering Smith
Brown Accounting Jones
Green Engineering Smith

对这样一个schema ,显然其中有些数据是重复的,修改数据时可能发生不一致。加入 Brown 接任了 Accounting 部门的领导,那么需要修改很多行来反映这个变化,这很麻烦而且很容易出错,并且如果Smith这一行的 leader 和 Green 这一行的 leader 不一样,那就不知道到底谁是对的了,这就像 “一个人有两块手表就永远不知道正确时间了”。这样设计的一张表还存在很多问题,比如部门不存在雇员就没法表示一个部门,针对这些问题我们要对这张表进行范式化

将表拆分是范式化的手段,将信息冗余的列从原表中拆分出来做一个新表,然后通过关联表建立联系。

Employee department
Jones Accounting
Smith Engineering
Brown Accounting
Green Engineering
department leader
Accounting Jones
Engineering Smith

拆分成两张表之后上面的问题就都解决了(第二范式),我们来看下范式存在的优缺点。

范式的优缺点

在解决数据库性能问题时,经常会被建议对 schema 进行范式化建议,尤其是写密集的场景。这通常是个好建议,范式话能带来这些好处:

  • 范式化的数据更新操作通常比反范式化要快
  • 当数据较好地范式化时,就只有很少或者没有重复数据,所以只需要修改更少的数据
  • 范式化的表通常更小,可以更好地放在内存里,所以执行操作会很快
  • 很少有多余的数据意味着检索列表数据时更少需要distinct 或者 group by 语句,在前面的例子中,需要使用 distinct 或者 group by 才能获得一份唯一的部门列表,如果部门是一种单独的表则只需要简单查询这张表就可以了

当然范式化的设计并不是只带来好处,范式化的schema带来的缺点是需要关联,稍微复杂一些的查询语句在符合范式的schema上都可能需要至少一次关联,甚至多次关联。关联的代价是很昂贵的,也可能使一些索引策略无效。例如范式化可能将列存在不同的表中,而这些列如果在同一表中本可以属于同一个索引。

反范式的优缺点

反范式就是在通过增加冗余数据或数据分组来提高数据库读性能的过程。有时候反范式能掩盖关系型数据库软件的低效。反范式的schema因为数据都在一张表中,可以很好的避免关联。

如果不需要关联表,则对大部分查询最差的情况–即使表没有使用索引–是全表扫描,当数据比内存还大的时候可能比关联表要快得多,因为这样避免了随机IO(扫描全表基本上是顺序IO)。使用反范式的表能使用更加有效的索引策略。

混合使用范式化和反范式化

完全的范式化和完全的反范式化 schema 都是实验室才有的东西:在真实世界不会有这么极端的使用,在实际应用中经常需要混用,可能使用部分范式化的schema、缓存表,以及其他技巧。

最常见的反范式化数据的方法是复制或者缓存,在不同的表中,存储相同的特定列。但是反范式化使得更新数据的代价变大了,需要考虑更新的频率以及更新的时常,并和执行select查询的频率进行比较。所以实际使用中,是否使用反范式化还是要根据具体需求确定,如果查询多余更新,可以反范式化多一些,如果需要经常更新数据,那么过多的反范式化列使得整体的性能反而降低了。

一般来说,在范式化达到一定的满意水平并且所需要的约束和规则都已经建立起来才进行反范式化。

从性能来说,范式化有更好的写性能,反范式化有更好的读性能。

参考:

Git默认的配置会将中文文件名显示为一堆数字(quote值),之前用也觉得没什么,文件管理的比较粗糙,乱码对应哪个文件都能猜出来,今天偶然看到这个配置,就水一篇好了。

其实只需要添加一个配置就可以了,

git config --global core.quotepath false

core.quotepath设为false的话,就不会对0×80以上的字符进行quote。中文显示正常。

水是水了点,确实还是有用的,尤其是对中文用户。

另外加一个配置Git自动管理Linux 和 Windows 的换行差别:

git config --global core.autocrlf true

这个配置在安装git windows 时是可以配置的,如果忘了配置可以用这个命令配置。

还可以配置下面的选项让git提交文件时不把换行符 warning 回显出来:

git config --global core.safecrlf false

设置socks5代理

git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'

bw: bandwidth
cloudlet
CIS: cloud information service,执行资源的分配与注册, CloudSim 初始化时会生成该类的实例,不需要自己处理
PE: Processing Element, CPU unit
vm: virtual machine, runs inside a Host, sharing hostList with other VMs. process cloudlets.process according to CloudletScheduler’s(timeShared, spaceShared) defined policy. VmSchedulerTimeShared is property of Host.
datacenterBroker: 代理仁,代表用户执行操作,对用户隐藏虚拟机管理(创建,提交任务到虚拟机,虚拟机销毁)。
Event:

可以有多个datacenter, 一个datacenter有多个host,一个host有多个PE,

一个仿真流程

  1. 设置用户数量(broker count)
  2. 设定通用的变量
  3. 创建CIS(自动创建)
  4. 创建Datacenter:包括 host + characteristics(Pe, Ram, Bw, price)
  5. 创建DatacenterBroker
  6. 创建Vm instances
  7. 提交Vm list 到 broker
  8. 创建 Cloudlet(tasks) List
  9. 提交(submit) Cloudlet lists到broker
  10. start simulation
  11. stop simulation
  12. 输出仿真的状态

CloudSim Events the heartbeat of simulation

非常重要的类: SimEvent


前面已近有一篇系统级IO,那是 CSAPP 中的一章,讲了Unix IO的内容,这一次是Unix环境高级编程的一章,也是讲Unix/Linux下的文件IO,可能这里讲编程的内容多一些。

我们知道通过 low level 的 open, creat 方法调用可以获得一个 file description ,它是一个整数,对应到进程文件描述符表的一项,使用这个整数就可以标识这个打开的文件。

文件信息

对于一个打开的文件的 fd, 我们已经知道有一个 stat 函数可以获得该文件的信息,其实有另外好几个类似的函数(在64位系统还是有stat64的一套函数),其函数声明如下:

#include <sys/stat.h>
int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
int fstatat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);

它们的作用都是获得某个文件的相关信息,文件信息被保存在一个结构体中,上面的函数调用会填充这个函数体的成员字段。stat和fstat比较好理解,只是第一个参数不同(不考虑参数的修饰符),stat使用一个文件名做参数,fstat使用一个fd做参数,实际的效果是一样的,不过stat的buf参数使用了restrict修饰符,这个修饰符在之前的文章中已经解释过了,它提示编译器保证该指针指向的对象不被当前指针以外的指针修改。

阅读全文 »


尽管现在很多的高级语言,脚本语言提供了非常非常方便实用的数组,队列(list),字典(map)等数据结构,但是理解C语言的数组和指针是掌握这些高级数据结构的基础。即使不经常写底层的数组,也应该熟练掌握。

初始化

数组的初始化比较简单不罗嗦了,这里介绍一下数组的指定项目初始化方法,这是c99添加的特性,个人觉得很有意思。

// 普通的初始化方法
int year[6] = {31, 28, 31, 30, 31, 30};
// 可以在初始化时指定要初始化的项及其值如:
int year[6] = { [1] = 28, [3] = 30, 31, 30, [1] = 29};
// 初始化结果为: [0, 29, 0, 30, 31, 30]

看起来还算好理解,就是指定下标对其赋值。某个指定赋值后的为指定项目用来对后续的元素初始化,未初始化的项会初始化为0(只要对数组进行了初始化就会把剩余的部分初始化为0,这是一般行为)。甚至可以对一项多次指定赋值,以最后一次为准。

再提一次,花括号列表的形式只能用于初始化,不能用于对数组赋值。

对数组指针使用sizeof的时候会计算到第一个 \0 处,常用 sizeof arr / sizeof arr[0] 计算数组的大小。但是实际上这样做可能会导致问题。

阅读全文 »

人啊,就还得要多学习一个,要不然想的东西啊,总是 too simple。
–来自长者的教诲

从实习离职回学校“搞论文”已经快三个月了。实在的,论文没怎么搞,没搞出什么东西,已经对科研非常的没兴趣了,不适合做科研。不过这三个月看些书,就觉得啊,还是要学习一个,要不然搞得那些东西啊,一点都不excited,都 too simple,跑的一点都不快,没什么搞头。

在学校做工程的人多容易飘飘然,这是我的感受。虽然我见的一点也不多,但是我作为一个年轻人,我还是要说一个,现在做项目的啊,都太low。现在在学校做项目的主要是几个:老师的项目,工程性的项目,老师和外面公司,机构谈好,回来交给学生做,然后老师基本不管;学生自己的项目,几个人组一个团队,拉一些活自己写,能力好的能做的不错,听说不少都不能按时完成。

做项目的好处是能实践一些东西,现在做创业的那么多,随便加入一个创业团队,从后台导前台,从开发到运维到推广都能体验一番,但是呢,现在做创业的都太浮躁了,看起来从前到后,从低到高全栈了一把,实际上,现在99%的创业团队里,技术都不是重点,用php,python,ruby搞个模板一趟的网站出来就可以搞个大新闻了。这种看起来厉害的全栈,真的非常危险,尤其是对我们这样的学生来说。

当然这只是我自己的感觉。曾经我也认为自己全栈的一逼,虽然我CSS渣的可以,但是前端的人都偶尔找我调CSS;虽然JS一直没弄懂闭包,不过真正能写JS的人实在很少,我也能顶一下;后台那些框架,哪个我没用过(胡),Restful API, MVC,MVVC,一套一套的。这些名词堆砌的多了,就自己真的都会相信自己是全栈了。

但是真正想想自己做过的那些东西,有什么技术吗?所谓的技术,其实就是你知不知道这个东西,有没有这个思路,知道这个东西的名字,查查资料,翻翻教程,一般的一周就能上手了。现在所谓的互联网创业,大多数都是这样的技术支撑着,就能拿大笔投资,真是想不明白,这样的都能技术创业,要是还都成功了,简直没天理了。

那应该学习一个,要学什么呢?我觉得还是计算机理论的基础。但是又要结合工程的实际,不是科研的那种;组成原理,计算机网络,编译,操作系统,这些才应该是985计算机专业学生的内力,才是和其他人比较时有优势的地方。当然主流的语言,框架,数据库技术都很重要,这就需要花更多的时间去学习,毕竟虽然说它们比较简单,但是要掌握真的还是要花费时间精力的。

不过这中的调和又很矛盾,我自己是理论基础学的不好的,所以我认为其他人应该理论都学的比我好(什么逻辑),但是我看的有的人的Linux水平,实在不能说是一个计算机专业毕业的学生。虽然Linux并不是必须要掌握的技术,但是技能树上没有这一枝,实在太可惜了。

那些网上卖水果的,卖各种乱七八糟的P2P行业,忽悠了好几年,实在没看出来他们创造的价值在哪里,根本没有解决什么问题。绝大部分的P2P就是忽悠,尤其是P2P理财,水分泡沫太大了,基本全是泡沫所以看起来很好。

未完


C可以对变量的个别位进行操作,这在流行的其他高级语言中不太常见了,尤其是脚本语言很少有这样的能力了。位操作最常见的情景是设备控制的驱动程序,每一位都有特定的含义;数据的压缩与解压缩,数据加密解密等等。

基础:二进制数、位、字节

这部分是计算机理论基础,只提一个有意思的点:一个字节通常包括8个位,不过C使用术语字节(byte)表示用于存放系统字符集的空间大小,所以一个C字节可能为8,9,16位或者其他的值。然而描述存储器芯片和数据传输率时使用的字节就是指8位字节。

关于字符编码的原码,反码,补码的内容,不再赘述。

位运算符

C提供位的逻辑运算符移位运算符

位逻辑运算符

4个位运算符用于整型数据,位逻辑运算符与常规的逻辑运算符(&&, ||, !)不一样,使用时不要把它们混淆了。常规的逻辑运算符是对整个操作数进行的,而位逻辑运算是针对数的某一位操作,而不影响其他位。

  • 取二进制反码,或者按位取反: ~ 将一个数的二进制表示形式按位取反。注意,~是一个操作符, ~val 并不改变val的值,就像 3*val 不会改变val保存的值,该操作会返回一个操作的结果,用于赋值语句的右边。
  • 位与 & 两个操作数按位与产生一个新值。两个操作数对应位都为1时,结果的那一位也为1,否则为0.
  • 位或 | 和 &很像
  • 按位异或 ^ 对每一个位,如果操作数中的对应位有且只有一个为1,结果对应位也为1,都则结果对应位为0

这四个操作符和 数值运算(+, -, *, /)一样可以与赋值符号(=)结合成一个赋值运算符(&=, |=, ^=)。其中取反常写为 val = ~val;

掩码、打开位

掩码常用在如子网掩码的名称中,掩码是某些位设为1而某些位设为0的位组合。使用掩码与&操作符的目的是将特定位之外的所有位都设置为0.

与使用掩码相对的是打开某个为,使用 | 操作符与掩码将某些位保持不变而使某些位都设置位1。

其他还有关闭位( f &= ~mask),转置位(f ^= mask)都是很常用的。

查看某一位的值

用 & 实现查看变量某一个位的值.

int i = 123;
int mask = 0b01000000; // 01000000
if ( (i & mask ) == mask)
puts("bit 6 of i is 1");

这种方法中,为避免信息漏过边界,位掩码至少应该与其所屏蔽的值具有相同的宽度。

移位运算

移位运算包括左移和右移两种。

左移运算 << 将左侧操作数的值的每一位都向左移动,移动的位数由右侧操作数指定。空出的位用 0 填充,并且丢弃移出做操作数末端的未。移位操作返回一个新值而不改变其操作数。如 i << 2 返回i左移2位的结果,i的值不变。

右移运算 >> 将做操作数的每位向右移动。丢弃移出左侧操作数右端的位。对于无符号类型(unsigned),使用0填充左端空出的位,对于有符号数,结果依赖于机器,可能用0填充,可能用符号位填充(常用符号位填充)。

移位运算是高校的对2的幂的乘法和除法,在编译器的优化中常用移位运算和加法运算组合来优化乘法运算,比如 i*33 可以被优化为 i << 5 + i 这样子。这些优化取决于编译器实现,对程序员是透明的。

移位运算对于按位存储数据的变量很有用有用,比如颜色常保存为 0xff00ff00 这样子,使用移位与 & 操作符可以方便的取得 rgba 对应的数值。

位字段

位字段在之前的一片介绍链接的部分,ELF的结构体中使用过。这种技术在网络协议中也经常会使用。

位字段是一个singed int 或者 unsigned int 中一组相邻的位。位字段由一个结构声明建立,该结构声明为每一个字段提供标签,并决定字段的宽度

struct {
unsigned int autfd: 1;
unsigned int bldfc: 1;
unsigned int undln: 1;
unsigned int itals: 1;
} prnt

prnt.autfd = 1;
prnt.itals = 0;

该定义使prnt包含4个1位的字段,可以像使用普通结构成员运算符一样将值赋给单独的字段。变量prnt被存储在一个int大小的存储单元中,且只有4位被使用。

带有位字段的结构允许在单个单元中存储多项设置。但是要确保设置的值没有超出字段的容量。

如果声明的结构中总的字段数超过了一个unsigned int 大小,那么将会使用下一个unsigned int存储位置。不允许一个字段跨越两个存储位置的边界,如果有这种情况编译器将自动地移位字段定义,使字段按照unsigned int边界对齐。这样会在前一个存储位置留下一个未命名的洞。

可以使用未命名字段填充未命名的洞。使用一个宽度为0的未命名字段迫使下一个字段与下一个存储位置对齐。也就是说位字段结构成员的大小没有什么限制,只要注意边界对齐就可以了。

struct {
unsigned int field1 : 3;
unsigned int : 1; // leave a hole
unsigned int field2 : 3;
unsigned int : 0; // hole for align
unsigned int field : 5;
} staff;

一个可能的问题是字段在int中的顺序依赖于机器实现,有的机器上从左到右存储,在另一些机器则是从右向左,所以位字段往往难以移植,典型地,把他们用于不可移植的用途,例如按照某个特定硬件设备所使用的确切格式来存放数据。

位字段和位运算符是对于同类编程问题的两种可选择的方法,可以使用其中任何一种,通常来说位字段的方法比较好理解,使用也更加方便。

完?

来源: http://www.unixguide.net/Linux/linuxshortcuts.shtml 个人觉得非常好的一个Linux入门资料。待我慢慢翻译整理出来。

前面的部分很值得看看,后面的很多管理工具都已经过时了,看不看影响不大了。

介绍

This is a practical selection of the commands we use most often. Press to see the listing of all available command (on your PATH). On my small home system, it says there are 2595 executables on my PATH. Many of these “commands” can be accessed from your favourite GUI front-end (probably KDE or Gnome) by clicking on the right menu or button. They can all be run from the command line. Programs that require GUI have to be run from a terminal opened under a GUI.

图例:

  • <> = 键盘上的单个特殊按键或者功能键. 比如用 表示 “control” 键.
  • italic = 斜体字用于文件名,或者需要你自己替换的变量.
  • fixed width = 等宽字体表示Linux命令和文件.

对Unix新手的提示:

阅读全文 »

文件

一个文件可以理解为磁盘上的一段命名的存储区。对于操作系统来说,文件更加复杂,比如大文件可能存储在分散的区段中,或者还会包含一些使操作系统确定文件类型的附加数据等。但是对于C程序员来说不需要考虑底层文件系统的实现,C将文件看出是连续的字节序列,每一个字节都可以单独地存取。 ANSI C提供了文件的两种试图:文本视图和二进制视图。

文本试图和二进制试图是指使用C打开文件的模式,与Windows中的文本文件和二进制文件是两个不同的概念(对C程序来说文本文件和二进制文件没有区别)。使用文本模式打开文件时,会将本地环境表示法映射为C试图,比如 windows 和 Linux有不同的换行模式,但是是用文本模式打开文件时,这两个换行方式都会被映射到 \n ,同样一个C中以 \n 换行的字符串写到文本模式打开的文件时,会自动转换到 \r\n 或者 macintosh 的 \r。而使用二进制模式打开文件则不会有这样的映射。通常使用文本模式处理二进制文件会效果很糟糕,但是也是可以使用的。

标准输入、输出、错误流是三个特殊的文件,这个已经提到过多次了,它们的fd分贝是0,1,2。

标准IO

之前提到过一篇系统级IO。系统级IO(Unix IO)一般称为低级IO(low level io),使用操作系统提供的基本IO服务,标准IO则是标准C提供的保准所有操作系统都可用的IO模型,ANSI C建立的标准IO模型具有很好的可移植性,我们讨论的标准IO就是它。

除了可以执行之外,标准IO相对于系统级IO还有两个优势:

  1. 标准IO包含了很多专用的函数,可以方便地处理不同的IO问题。
  2. 对于输入和输出进行了缓冲,也就是可以大块地转移信息,而不是每次一个字节地转移。

对于标准IO的学习主要就是一些标准IO函数的学习。这些函数本身不是很复杂,只需要大概记住它们的函数声明就可以使用了。这里不一一列举了,写一些比较常用的。

#include <stdio.h>

// 打开一个文件,返回一个新的流,
FILE * fopen(const char * restrict __filename, const char * resttrict __modes);
// 打开一个文件,替换一个已存在的流
FILE *reopen(const char * restrict __filename, const char * restrict __modes, FILE * restrict __stream)
`

第二个参数的打开模式使用一个字符串表示,常用r, w, a, r+, w+, a+,注意使用了w的模式都会删除原有文件的内容。这几个模式都可以再加一个 b 表示使用二进制模式而非文本模式打开文件。不过对于Unix和Linux这样只有一种文件类型的系统,带b和不带b的模式是相同的。

可以看出fopen和系统级IO的open返回的不一样,fopen返回一个方便操作的文件指针fp,而open是返回一个int类型的fd。指针fp指向一个关于文件信息的数据包,而不是指向实际的文件。

int fgetc(FILE *);
int getc(FILE *);
int fputc(int, FILE *);

char * fgets(char * restrict __s, int __n, FILE * restrict __stream);
int fputs(const char * restrict, FILE * restrict);

int fclose(FILE *); // 关闭成功返回0
int fflush(FILE *); // 刷新文件

// code snippet
while ((ch = getc(fp) != EOF) {
...
}

fgetc 和 getc 的主要区别是 getc是使用宏实现的,而fgetc是一个函数,这意味着一些区别:

  1. getc的参数不能是一个有副作用的表达式(虽然没有语法错误,但是会很危险)
  2. 可以获得fgetc的地址(函数指针)
  3. 理论上来说,getc 速度更快,对于大量使用的情况,推荐使用 getc

文件的格式化输入输出:

int fprintf(FILE * restrict __stream, const char * restrict __format, ...);
int fscanf(FILE * restrict __stream, const char * restrict __format, ...);

文件指针操作:

int fseek(FILE * __stream, long int __off, int __whence);	// 设置位置指针为指定的值
long int ftell(FILE * __stream); // 获取当前文件位置

int fgetpos(FILE * restrict __stream, fpos_t * restrict __pos);
int fsetpos(FILE * __stream, const fpos_t * __pos);

上面的fseek设定指针的位置是很常见的,其中第二的参数 offset 是一个可正可负可为零(为0用于测试该流是否允许设置指针位置)的long int 类型,第三个参数说明offset 的起始点。 ANSI 中定义了如下起始点常量:

  • SEEK_SET 。 文件开始
  • SEEK_CUR 。 当前指针
  • SEEK_END 。 文件结尾

结合起始点与偏移量可以方便的设置到任意的文件位置。

fgetpos 和 fsetpos 使用一个 fpos_t 类型的指针作为设置值,可以用于更大的文件(超过long int范围长度的文件)。

标准IO内幕

哈哈哈

其他标准IO函数

列出来如下:

  • int ungetc(int c, FILE *fp) 将指定字符c放回输入流
  • int feof(FILE *fp) 判断是否到达文件结尾
  • int ferror(FILE *fp) 测试错误指示器
  • void clearerr(FILE *) 清楚文件结尾和错误指示器
int setvbuf(FILE * fp, char * buf, int mode, size_t size);
size_t fread(const void * ptr, size_t size, size_t nmemb, FILE * fp);
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE * fp);

servbuf可以建立一个供标准IO函数使用的替换缓冲区。(cpp 368)

参考:

今天学习C的库函数,到可变参数部分,使用gcc编译一个简单的程序发现编译报错,代码如下:

#include <stdargs.h>
#include <stdio.h>

double sum(int lim, ...);
int main(void) {
double s, t;
s = sum(3, 1.0, 2.02, 3.123);
t = sum(8, 2.9, 9.1, 2.2, 4.1, 99.2, 43.2, 54.1, 9.0);

printf("sum of s is %g\nsum of t is %g\n", s, t);
}
double sum(int lim, ...) {
va_list ap;
double sum = 0;
int i;
va_start(ap, lim);
for (i=0; i<lim; i++)
sum += va_arg(ap, double);
va_end(ap);
return sum;
}

错误如下:fatal error: stdarg.h: No such file or directory,头文件不存在,很奇怪,去找了一下 /usr/include 目录确实没有这个文件,用find查找竟然也没发现这个文件,难道系统没有这个文件?不可能的啊。(是find 的时候打错名字了。。。真是见鬼,闹腾到今天才发现)

实际上系统是有这个文件的,不过不在 include 目录下,在 /usr/lib/gcc/x86_64-redhat-Linux/4.8.2/include/ 这个目录下是有这个文件的。这是我的CentOS下的路径,可能其他发行版的路径名略有不同。

为了让gcc找到这个头文件,我们可以拷贝一份到 /usr/include 下,但是更好的方法是做一个软连接:

sudo ln -s /usr/lib/gcc/x86_64-redhat-Linux/4.8.2/include/stdargs.h /usr/include/stdargs.h

在编译就没问题了。

软连接在这种情况下很有用的工具,很多编译器找不到共享目标啊,找不到头文件啊,都给个软连接可以解决。

== added

前面讲到的find 查找文件,因为centos 7没有了locate命令(locate真的很方便,只是要维护一个db确实比较不讨人喜欢就被砍了吧),查找文件都是用find,常用的方法如下:

find -L /usr -name "*file-to-find*" -type f

-L 表示 follow symbol link, -type f表示输出文件,不输出目录,也可以 -type d 只输出目录。

查找的名字,可以用双引号括起来,加通配符。