使用gcc同时编译多个文件

2011年7月28日 由 edsionte 没有评论 »

平时在写C程序的时候都是将所有代码全部写入一个.c文件中,这样写对小程序来说是适合的。但是对于比较大的项目,将所有代码写在一个文件不利于代码的维护。

最近由于项目需求,将一个服务器程序按照功能整理成了几个.c文件和.h文件。该服务器程序包含以下文件:

.
|-- config_system.c
|-- generic_function.c
|-- log_system.c
|-- server.c
`-- server.h

代码整理完毕之后,开始对源文件进行重新编译。在编译的过程中遇到了一些问题,现在总结如下。

1. 自定义头文件的使用

server.h文件包含服务器程序中所有自定义函数原型的声明和一些常量定义,将该自定义函数放在当前程序的目录下即可。自定义头文件的使用方法就和普通头文件的使用方法一样,加在程序最开始的位置即可,不过要将包含头文件的尖括号<>改成双引号“ ”。

通过这个实践,也可以更好的理解包含头文件的两种方法。对于尖括号包含的头文件,gcc在系统默认的目录中(/usr/include)查找相应的头文件;对于双引号包含的头文件,编译器首先会在当前目录下或指定的目录下(如果有指定)去查找头文件,如果当前目录下没有该头文件则去默认的头文件目录下查找。

刚才我们提到了头文件的指定目录,在大型项目中,头文件可能会更多,往往将头文件单独放在一个目录当中,此时应该使用-Idirname选项来告诉编译器到指定的目录dirname中去查找头文件。比如我们将server.h放在head目录中再进行编译:

gcc -Ihead config_system.c server.c log_system.c generic_function.c -o server

选项I代表include,该选项的作用阶段是预处理阶段。

2. 多个源文件的编译

我们可以先将每个源文件编译成目标文件,最后再生成一个可执行文件。比如:

edsionte@edsionte-desktop:~/server$ gcc -c server.c
edsionte@edsionte-desktop:~/server$ gcc -c config_system.c
edsionte@edsionte-desktop:~/server$ gcc -c log_system.c
edsionte@edsionte-desktop:~/server$ gcc -c generic_function.c
edsionte@edsionte-desktop:~/server$ gcc server.o config_system.o log_system.o generic_function.o -o server

也可以直接使用 一条命令:

edsionte@edsionte-desktop:~/server$ gcc server.c generic_function.c log_system.c config_system.c -o server

不管是那种方法都可以编译源文件,最后连接成可执行文件。在第一种方法中,单独使用-c选项只是编译源文件,并不涉及链接的过程,因此源文件是否包含main函数我们并不关心。第二种方法是将编译链接通过一条命令来完成。

其实在使用gcc编译程序时,整个编译过程分为预编译、编译、汇编和链接四个步骤。上述两种方法都自动完成了前两个步骤。

3. extern的使用

按照上面的命令进行编译程序,出现了下面的错误提示:

config_system.c:80: error: ‘config_filename’ undeclared (first use in this function)

这条错误是在说明config_filename这个变量未声明。config_filename作为一个全局变量在generic_function.c文件中已经声明并定义过,那么如何让编译器知道该变量已经定义过并且可以在config_system.c文件中使用?此时extern关键字就派上了用场。

extern的作用是声明一个变量,它的作用仅仅是声明,而不是像定义该变量时那样为其分配内存空间。一个变量在整个程序中只能定义一次,但是却可以声明多次。

4. 使用脚本编译并运行服务器程序

由于将代码按照功能分离后,每次编译都需要很长一串的命令,因此写个脚本就显得很有必要了。下面的脚本先将原有的可执行文件(如果存在的话)删除,再编译程序,最后运行可执行文件。

#!/bin/bash
test server && rm server
gcc server.c config_system.c log_system.c generic_function.c -o server
echo "compliing success!"
echo "sever is running.."
./server

通过这次“分离”代码的实践,给我最大的感受就是平时看似懂的概念只有到真正使用的时候才真的开始理解。

使用grep查找指定目录下的关键字

2011年7月24日 由 edsionte 没有评论 »

之前于童鞋问我如下问题:

在/etc目录下如何查找包含’root’关键字的所有文件?

对于一个指定的文件来说通常使用下面的方法来查找关键字:

grep 'keyword' file

如果你懂得正则表达式,那么还可以对关键字作更细致的匹配。但是利用grep查找指定目录下的所有文件我尚不清楚,通过man手册以及论坛求助基本解决了上面的那个问题。

1.如何递归查找

grep通常只能查找指定数目的文件,如果需要查找某个目录下的所有文件必须添加-R或-r选项。这个选项比较好理解,如果我们要删除一个非空的目录那么也必须对rm命令添加-r或-R选项。因此我们可以通过一下命令来完成本文一开始所提出的问题:

grep -R 'boot' /etc

这样好像已经解决了问题,不过从输出的结果来看有一些匹配并不符合我们的要求。

/etc/grub.d/README:administrator.  For example, you can add an entry to boot another OS as
/etc/grub.d/20_memtest86+:if test -e /boot/memtest86+.bin ; then
/etc/grub.d/20_memtest86+:  prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/
\t/")"

比如第三行所匹配的root并不是一个单独的词,而是包含在其他字符串中。

2.完全匹配一个词

通过上面的分析我们得知我们需要类似下面的举例那样匹配root关键字:

I am a root
root is me
I have root permission

而不是像下面这样:

rooter is me
I am rooter
I have rooter permission

因此,我们还需要增加其他参数以达到完全匹配一个词的目的。grep中使用-w选项来完全匹配一个单词。因此我们的解决方法修改如下:

grep -R -w 'boot' /etc

3.更友好的改进

通过上面的命令已经可以较好的解决本文一开始提出的问题了。不过,其输出结果很长,不方便我们查看。

如果我们需要知道该关键字位于被查找文件的哪一行,那么可以加入-n选项:

grep -R -w -n 'boot' /etc

有时候我们只需要知道该关键字包含在那个文件中,因此我们加入-l选项:

grep -R -w -l 'boot' /etc

这样以来的确方便了查看,不过由于输出结果在一个屏幕下不能完全显示,因此我们可以用more命令分屏查看,或者直接将输出结果重定位于指定的文件中:

grep -R -w -l 'boot' /etc > ./output.txt

这样就可以方便的查看输出结果了。

Hello,Python!

2011年7月10日 由 edsionte 2 条评论 »

使用Python打印一条“hello,world!”语句是再简单不过的事情了,因为Python特有的交互型解释器可以帮助你完成这项任务。如果你在Ubuntu系统下,首先在终端敲入python就可以进入python解释器,然后输入”hello, world!”即可打印出这句经典的语句。

edsionte@edsionte-laptop:~$ python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> "hello, world!"
'hello, world!'

此时,你已经成功的打印了”hello, world!”这条字符串。当你输入这条字符串后,解释器就按照字符串的解释规则去解释它,其结果就是在终端显示出hello, world这条语句。这里特别强调的是,这条语句必须被双引号或者单引号包括在内,如果在解释器中直接输入hello, world!那么将会出现语法错误,比如:

>>> hello, world!
  File "< stdin >", line 1
    hello, world!
                ^
SyntaxError: invalid syntax

其主要原因就是Python解释器此时不知道如何解释这条语句。

除了用双引号或者单引号直接告诉Python解释器”此时你应该解释一条字符串“,通过print命令也可以成功打印这条语句。

>>> print "hello, world!"
hello, world!

至此,你已经成功的使用Python打印了“hello,world!”,不过这一切都是在Python解释器里完成的,当你退出解释器是,这些语句将会消失。按照我们常规的编程习惯,我们通常会将程序以文本的形式保存起来。我们使用vim创建一个hello.py的程序,其内容是:

#!/usr/bin/python2.6
print "hello, world!"

新增的第一条语句告诉操作系统应该使用/usr/bin目录下的Python来执行这些语句。我们再修改该文件的属性,使它有执行的权限。好了,你已经创建了第一个Python脚本,现在开始执行吧:

edsionte@edsionte-laptop:~/mypython$ chmod a+x hello.py
edsionte@edsionte-laptop:~/mypython$ ./hello.py
hello, world!

成功!

adduser命令学习

2011年7月7日 由 edsionte 没有评论 »

今早试着为我的机子添加一个guest账户,以便方便其他同学的使用。因此我用useradd命令来创建一个guest账户:

edsionte@edsionte-desktop:~$ sudo useradd -d /home/test test

上述命令在系统中创建了一个test用户,并且通过-d选项为该用户指定了登录后的主目录/home/test。创建成功后,该用户的账户信息会保存在/etc/passwd文件当中。/etc/passwd文件用来保存系统中当前所有的用户信息,该文件对所有用户都可见。比如:

……
edsionte:x:1000:1000:edsionte,,,:/home/edsionte:/bin/bash
guest:x:1001:1001:guest,,,,:/home/guest:/bin/bash
test:x:1002:1002::/home/test:/bin/sh
……

在该文件中,每行信息代表一个用户。每个用户的信息由7部分组成:

用户名:加密后的用户密码:用户ID(UID):用户所在组ID(GID):用户全名以及用户信息:用户主目录:该用户登录时所用的命令解释器

从上面test用户的信息可以看到,除了我们显式指定的用户主目录,其余信息都是系统默认的。比如UID和GID相同,test用户的命令解释器选用sh。

用户创建后,还必须为test指定一个登录密码。指定和修改用户密码的命令是passwd。普通用户使用该命令只能对自己修改密码,而超级用户则可以对任何用户修改密码。我们当前的用户为edsionte,通过下面的命令即可为test指定密码:

edsionte@edsionte-desktop:~$ sudo passwd test
输入新的 UNIX 密码:
重新输入新的 UNIX 密码:
passwd:已成功更新密码

由于我们在edsionte用户下去修改test用户的密码,因此必须加sudo使用超级用户权限。

到此位置,test用户就创建完毕了,可是登录到test用户下,打开终端后却出现了以下几个问题:

1.打开终端后,提示信息仅为“$”,并不想通常我们所见到的“edsionte@edsionte-desktop:~$”;

2.在终端使用TAB键后不能自动补全命令,而是TAB健本身的效果;

原来这两个问题与我们刚才指定的命令解释器有关,通常我们使用的shell为bash,而useradd命令默认指定的shell为sh。通过usermod命令可以修改一个用户的相关信息,因此我们在test用户下通过下面的命令修改shell即可:

usermod -s /bin/bash

注销并重新登录test后就可以看到我们通常所熟悉的终端。

西邮Linux兴趣小组纳新笔试试题

2011年6月16日 由 edsionte 4 条评论 »

下面是西邮Linux小组今年纳新的笔试试题,原文在这里

1、 下面这个程序的输出结果是什么?
int main()
{ int a = (1, 2);
printf(“a = %d\n”, a);
return 0;
}

解答:
a = 2
这里利用了逗号表达式。

2、下面这个程序的输出结果是什么?
struct value {
char a[3];
short b;
};
struct value tmp;
printf(“sizeof(tmp) is %d\n”, sizeof(tmp));

解答:
sizeof(tmp) is 6
这里涉及到字节对齐,a数组后填充了1字节。

3、编写程序交换a,b的值。(使用多种方法)

4、说明 int *p[5] 和 int (*p)[5] 的区别?

解答:前者是一个指针数组,后者是一个数组指针。

5、编写函数,实现链表的创建,节点的插入和删除。

6、下面这个程序的输出结果是什么?
void f(int *p);
int main(void)
{
int *p;
f(p);
printf(“*p = %d\n”, *p);
return 0;
}
void f(int *p)
{
static int dummy = 5;
p = &dummy;
}

解答:
*p中的值不确定。
大多数人(包括我)都会认为*p的值是5,但这并不正确。因为f函数传递的是指针变量,既然是变量,那么只能通过传递地址来确保将f中的值饭或到主函数。如果f传递的是&p,那么最后的结果应该是5。

7、说明如下两段代码的区别。
char * p = “xiyou linux”;
char p[] = “xiyou linux”;

解答:
前者p是一个指针,它指向一段只读字符串。
后者p是一个数组,该数组用“xiyou linux”来初始化。

8、用C语言实现一个n!函数。(要求用递归实现)。

9、 char c;
char b[20] = “I love xiyou linux”;
c=’l’与c=”l”有区别么?若有,区别是什么?字符串b在内存中占几个字节?

解答:
c=’l'是一个字符,而c=”l”是字符串。
从对b数组的定义可以得知b在内存中占有20个字节,这与对它进行初始化的字符串长度无关。
如果有如下定义:
char b[] = “I love xiyou linux”;
那么此时b的大小为19。

10、实现自己的strcat()。

11、char str[20] ;
scanf(“%s”, str);
printf(”%s”,str);
如果输入:I love linux!<回车>
输出结果是什么?为什么?

解答:
输出结果为I。因为使用scanf输入字符串时不能含有空格,否则字符串会从第一个空格处被截断。

12、下面这段程序的输出结果是什么?为什么?
#define MAX(x,y) x > y ? x : y
int main()
{
int a = 10, b = 20;
int c = 3*MAX(a , b);
printf (“%d\n”, c);
return 0;
}

解答:
经过宏替换后,c = 3*a>b?a:b; 则c的值为10。

13、已知两个整型数组均为升序排列,将两个数组合并,且合并后仍按升序排列。

14、下面两条语句哪条更合理?
if(array[which] == 5 && which < SIZE)…..
if(which < SIZE && array[which] == 5)….

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key