Linux
下的
C
初探(
FC4
)
第一个
c
程序:
了解
bash
、
vi
、
gcc
、
gdb
、
g++
、
make
、
cvs
,我们可以通过
man
命令获得帮助,使用
which
命令查看是否可用,如
which bash
如果回应
/bin/bash
,则
ok
了。
man printf
获得
c
中
printf
函数的帮助。
使用
vi
编辑第一个
c
程序,在当前目录下,
vi hello.c
,按
a
键,进入编辑模式:
#
include <stdio.h>
int main(void){
printf(“hello world!/n”);
return 0;
}
依次
Esc :wq
键退出
vi
gcc –o hello hello.c
或者
gcc hello.c –o hello
会产生一个名为
hello
的可执行文件。
./hello
命令执行这个文件,屏幕会显示
hello world!
我们的第一个
c
程序就完成了,这样我们建立了学
linux c
的信心。
gcc
的使用:
接下来了解一下
gcc
的常用命令,以上面的程序为例子
gcc hello.c
默认会产生一个
a.out
的可执行文件,使用
ls –l
命令查看,
gcc –o hello hello.c/gcc hello.c –o hello
产生一个
hello
的可执行文件
gcc –S hello.c
产生一个
hello.S
文件
,我们用
ls –l
查看知道它是可读但不可执行,还可以知道它是个汇编语言文件。
gcc
编译所调用的
.h
头文件是在
/usr/include
目录下面,而二进制库文件在
/usr/lib
下面。关于
gcc
的根多功能我们可以通过
man gcc
查看。
Vi
的使用:
vi
编辑器的功能非常强大,有三种
mode
分别是
command mode
、
Insert mode
和
last line mode
,
vi hello.c
默认是
command mode
所以你不能编辑,按“
i
”键可以进入
insert mode
插入工作,按“
Esc
”切换到
command mode
下,
an
“:”键进入
last line mode
下,按“
:w(
文件名
)
”保存(文件),按“
:wq
”保存并退出,按“
:q!
”退出但不保存,在
command mode
下可以按“
i
”、“
a
”、“
o
”进入
insert mode
,“
i
”是从光标当前位置开始输入文件,“
a
”
从目前光标所在位置的下一个位置开始输入文字,而“
o
”是插入新的一行,从行首开始输入文字。“
set nu
”可以设置行号。
光标移动:可以直接使用键盘的上下左右键来控制,但我们一般使用“
k
”“
j
”“
h
”“
l
”代替上下左右,按“
ctrl+b
”:屏幕往
“
后
”
移动一页,“
ctrl+f
”:往
“
前
”
移动一页。
“
ctrl
+
u
”:往
“
后
”
移动半页,“
ctrl+d
”
往
“
前
”
移动半页。
“
0
”:移到文章的开头。“
G
”:移动到文章的最后。
“
$
”:移动到光标所在行的
“
行尾
”
。
“
^
”:移动到光标所在行的
“
行首
”
。
“ w ”:光标跳到下个字的开头,“ e ”:光标跳到下个字的字尾,“ b ” :光标回到上个字的开头,“ #l ”:光标移到该行的第 # 个位置,如: 56l 。
“ w ”:光标跳到下个字的开头,“ e ”:光标跳到下个字的字尾,“ b ” :光标回到上个字的开头,“ #l ”:光标移到该行的第 # 个位置,如: 56l 。
删除文字:“
x
”:删除光标所在位置的
“
后面
”
一个字符,“
#x
”:例如,「
6x
」表示删除光标所在位置的
“
后面
”6
个字符。
“
X
”:删除光标所在位置的
“
前面
”
一个字符。“
#X
”:例如,「
20X
」表示删除光标所在位置的
“
前面
”20
个字符。
“ dd ”:删除光标所在行。“ #dd ”:从光标所在行开始删除 # 行。
“ dd ”:删除光标所在行。“ #dd ”:从光标所在行开始删除 # 行。
复制和替换:“
yw
”:将光标所在之处到字尾的字符复制到缓冲区中,“
#yw
”:复制
#
个字到缓冲区
。
“ yy ”:复制光标所在行到缓冲区,“ #yy ”:例如,「 6yy 」表示拷贝从光标所在的该行 “ 往下数 ”6 行文字。
“ p ”:将缓冲区内的字符贴到光标所在位置。所有与 “y” 有关的复制命令与 “p” 配合能完成复制与粘贴功能。
“ yy ”:复制光标所在行到缓冲区,“ #yy ”:例如,「 6yy 」表示拷贝从光标所在的该行 “ 往下数 ”6 行文字。
“ p ”:将缓冲区内的字符贴到光标所在位置。所有与 “y” 有关的复制命令与 “p” 配合能完成复制与粘贴功能。
“
r
”:替换光标所在处的字符。“
R
”:替换光标所到之处的字符,直到按下“
Esc
”键为止。
gdb
的使用:
gdb
支持很多的命令使你能实现不同的功能。
这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令
,
命令描述
:
file 装入想要调试的可执行文件。
kill 终止正在调试的程序。
list 执行一行源代码但不进入函数内部。
next 执行一行源代码但不进入函数内部。
step 执行一行源代码而且进入函数内部。
run 执行当前被调试的程序
quit 终止 gdb
watch 使你能监视一个变量的值而不管它何时被改变。
break 在代码里设置断点 , 这将使程序执行到这里时被挂起。
make 使你能不退出 gdb 就可以重新产生可执行文件。
shell 使你能不离开 gdb 就执行 UNIX shell 命令。
gdb 支持很多与 UNIX shell 程序一样的命令编辑特征。 你能象在 bash 或 tcsh 里那样按 Tab 键让 gdb 帮你补齐一个唯一的命令 , 如果不唯一的话 gdb 会列出所有匹配的命令。 你也能用光标键上下翻动历史命令。
file 装入想要调试的可执行文件。
kill 终止正在调试的程序。
list 执行一行源代码但不进入函数内部。
next 执行一行源代码但不进入函数内部。
step 执行一行源代码而且进入函数内部。
run 执行当前被调试的程序
quit 终止 gdb
watch 使你能监视一个变量的值而不管它何时被改变。
break 在代码里设置断点 , 这将使程序执行到这里时被挂起。
make 使你能不退出 gdb 就可以重新产生可执行文件。
shell 使你能不离开 gdb 就执行 UNIX shell 命令。
gdb 支持很多与 UNIX shell 程序一样的命令编辑特征。 你能象在 bash 或 tcsh 里那样按 Tab 键让 gdb 帮你补齐一个唯一的命令 , 如果不唯一的话 gdb 会列出所有匹配的命令。 你也能用光标键上下翻动历史命令。
gdb
使得调试程序十分有用,比如设置断点,
make
的使用:
如果就上面的一个
hello.c
程序,我们完全没有必要使用
make
功能,我们手工编译就可以了,但当我们遇到很多的模块,那么手工来编译就显得不可行了,这是我们就借用
make
,但好处不仅仅在此,他只更新修改过的文件,并且
make
命令不会漏掉一个需要更新的文件。
文件和文件间或模块或模块间有可能存在倚赖关系,
make
命令也是依据这种依赖关系来进行维护的,我们把依赖关系写到
makefile
文件里,熟悉
java
语言编程的就会想到
ant
,我们可以用“
make –f
文件名”指定
makfile
文件,但一般就把文件取名为
makefile
。
Make
命令本身可带有四种参数:标志、宏定义、描述文件名和目标文件名。其标准形式为:
Make [flags] [macro definitions] [targets]
Makfile
:
makfile
有自己的语法规则,在
makfile
中一般说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。
比如,我们有个项目:其中由两个
c
源文件
aa.c
、
bb.c
以及库文件
LS
编译生成,这两个文件还分别包含自己的头文件
a.h
、
b.h
。通常情况下,
C
编译器将会输出两个目标文件
aa.o
、
bb.o
。假设
aa.c
声明用到一个名为
abc
的文件,但
bb.c
不用。即在
aa.c
里有这样的声明:
#include <abc>
,那么建立下面的
makfile
文档描述这些文件之间的相互联系
:
1 #It is a example for describing makefile
2 program : aa.o bb.o
3 cc aa.o bb.o -LS -o program
4 aa.o : aa.c a.h abc
5 cc -c aa.c
6 bb.o : bb.c b.h
7 cc -c bb.c
第一行有 # 的行为注释行。第行指定 program 由两个目标文件 aa.o 、 bb.o 链接生成。第三行描述如何从 program 所依赖的文件建立可执行文件。接下来的 4 、 6 行分别指定两个目标文件,以及它们所依赖的 .c 和 .h 文件和 abc 文件( bb.c 没有)。而 5 、 7 行则指定了如何从目标所依赖的文件建立目标。
而当 aa.c 或 a.h 文件在编译之后又被修改,则 make 工具可自动重新编译 aa.o ,如果在前后两次编译之间, aa.c 和 a.h 均没有被修改,而且 .o 还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义, make 工具可避免许多不必要的编译工作。利用 Shell 脚本也可以达到自动编译的效果,但是, Shell 脚本将全部编译任何源文件,包括哪些不必要重新编译的源文件,而 make 工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。
shell 脚本和 windows 下的 .bat 文件相当。
Makefile 文件一般包含以下内容 :
1 )宏定义, 2 )源文件之间的相互依赖关系, 3 )可执行的命令。
Makefile 中允许使用简单的宏指代源文件及其相关编译信息,在 linux 中也称宏为变量。在引用宏时只需在变量前加 $ 符号,但值得注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号()。宏的定义为编写 makefile 文件带来很大的方便。
1 #It is a example for describing makefile
2 program : aa.o bb.o
3 cc aa.o bb.o -LS -o program
4 aa.o : aa.c a.h abc
5 cc -c aa.c
6 bb.o : bb.c b.h
7 cc -c bb.c
第一行有 # 的行为注释行。第行指定 program 由两个目标文件 aa.o 、 bb.o 链接生成。第三行描述如何从 program 所依赖的文件建立可执行文件。接下来的 4 、 6 行分别指定两个目标文件,以及它们所依赖的 .c 和 .h 文件和 abc 文件( bb.c 没有)。而 5 、 7 行则指定了如何从目标所依赖的文件建立目标。
而当 aa.c 或 a.h 文件在编译之后又被修改,则 make 工具可自动重新编译 aa.o ,如果在前后两次编译之间, aa.c 和 a.h 均没有被修改,而且 .o 还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义, make 工具可避免许多不必要的编译工作。利用 Shell 脚本也可以达到自动编译的效果,但是, Shell 脚本将全部编译任何源文件,包括哪些不必要重新编译的源文件,而 make 工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。
shell 脚本和 windows 下的 .bat 文件相当。
Makefile 文件一般包含以下内容 :
1 )宏定义, 2 )源文件之间的相互依赖关系, 3 )可执行的命令。
Makefile 中允许使用简单的宏指代源文件及其相关编译信息,在 linux 中也称宏为变量。在引用宏时只需在变量前加 $ 符号,但值得注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号()。宏的定义为编写 makefile 文件带来很大的方便。
cvs:
如果进行团队开发版本控制和维护是必要的
语法随笔:
x = n++; // 先使用n的值,再使n+1;如果n==2,则x == 2,n==3
x = ++n; //先使n+1,再使用n的值;如果n==2,则x == 3,n==3
++对n的值的作用是一样的,但用到表达式中,作用是不一样的。
&:可以实现屏蔽某些位或将某些位置1,c=n&0177;//保留低七位;
<<:<<2;表示左移2位末端空出两位以0填之;即作乘4;
Printf(“%d %d /n”,++n,power(2,n));//错误,取决于机器,不同会产生不同结果,取决于n是调用power之前还是后,
A[i]=i+1;//都取决于编译器
for(a; b; c) 语句;等价与
a;
while(b){语句;
c;
}
Do {语句;
}
While();//先执行语句再判断条件
Continue只能用在循环中不能用在switch中,开始下一轮迭代,
函数之间的通讯通过参数,返回值和全局变量实现;
C语言调用 者调用一函数时,用变量取代参数,传给函数的是变量的值而不是变量的地址;被调用函数不能影响调用者的原变量;但如果是数组名出现再函数的 参数中,则是将数组的起始地址传给参数的,可以改变数组元素的值,数组是传地址的,
“定义”和“说明”:int sp; double val[MAX];为定义,且分配空间,extern int sp; extern double val[]; 说明,不分配空间,只知道他们的类型;外部变量只定义一此,而其它地方用extern 说明它;数组定义时确定大小;可以把变量的extern 说明集中在一个文件中,使用时用#include ;
变量:内部,外部,内部变量的值不保留随调用函数退出消失
静态变量:内部静态变量,外部静态变量;内部静态变量的值保留不随调用函数退出消失,即私有,永久存储,外部静态变量只属于从它的定义处开始到文件结束,其它文件不能访问他们,提供了一种名字隐藏的方法,则可以用static 使得函数只让它的文件知道,其它文件不用知道,c语言中的“static”不仅意味永久性还有某程度的私有性,外部命名一样的变量不会有冲突;
寄存器(register)变量:告知编译器该变量经常用到,只能用作自动变量和函数的形式参数。每个函数只能有很少的变量可以放在寄存器中,而且仅有某些类型的变量可以是寄存器变量,寄存器变量的地址是不能取道的,
函数内不能定义函数;
外部变量和静态变量在无明显的赋初值时保证他们的初始化为零,初始化只能进行依次,理论上是在编译时进行。寄存器变量具有不确定值,
自动变量和寄存器变量在每次调用时或进入分程序时进行初始化,初值不局限于产量,还可以包括已经确定的表达式或函数调用,
预处理:#include ,包含的文件变了,则所有与其有关的文件都要重新编译,
*++p:先将p加1单位后再取*p中的内容
*--p :先将p减1单位再取*p中的内容
malloc():返回整数*mallo():返回指针
[]优先与* int *day[10]:指向整数的指针组成的数组,int (*day)[10]:指向13个整数组成的数组的指针,
near(16位) , far(32位), huge(32位):近,远,特大,指针
int a[10][10]:int *b[10]:a[i][j],b[i][j]都合法,但a是真正的数组,分配了100个单位,b仅仅分配了10个指针,每个指针指向一个整型数组,如果每个整数数组是10个元素,则100个单位放整数,还得再加上10个单元放指针,有额外的开销,但也有好处:1.访问元素是用指针间接访问而不是像数组那样通过乘法和加法运算,即速度快,2.指针数组的每行长度不一定一样,可以不一样。
结构(struct)的地址和指向结构的指针:对结构只能实现两种运算即取结构的地址“&”&访问结构的成员“.”。不能整体利用,但指向结构的指针却不受限制,所以结构可以和函数结合在一起了。自动的结构和自动的数组一样不能初始化,只有外部的或静态结构可以初始化。
结构数组:struct key keytab[NKEYS]:两个一样的结构可以互相赋值。
Typedef:
exit(0):表示程序正常结束,非零表示异常结束。stderr和exit出错处理
fgets(line, MAXLINE, fp):行输入,从文件fp中读下个输入行,存入字符数组line。最多可读MAXLINE-1个字符,存入的行以“/0
”结束 。正常结束fgets回送line,如果文件结束则回送NULL
fputs(line, fp):输出,把一字符串(不需包含新行符)到文件上,直接从标准I/O拷贝来的
system(“data”):系统调用,强制执行程序data输出当天 日期&时间。
read,write ::低级I/O read(fd, buf, n);
open :回送 的是文件的描述子,而不是文件指针(用fopen)
creat(name, mode):建立文件,mode:读写权限
Lseek:lseek(fd, offset, origin):随机访问