一、gcc 的编译步骤
(源码 ==> 可执行程序)
机器里面跑的是可执行程序
编译:gcc xxx.c
警告信息和错误信息
a.out
gcc hello.c -o hello
./hello <=> ./a.out
源码 ==>可执行程序(四步)
(0 1)机器语言 ==> 汇编语言 ==> 高级语言
硬件+ 机器语言 + 汇编语言+ 高级语言;
1)预处理
处理与#号相关的代码(展开头文件)
gcc -E xxx.c -o xxx.i (预处理为 .i 文件 -o指定生成文件名)
2)编译
检查语法错误;
如果语法有误则报错,如果语法没错则编译成汇编语言
gcc -S xxx.i -o xxx.s (再编译为汇编文件)
3)汇编
将汇编语言解析成二进制文件(并非纯粹的二进制文件)
gcc -c xxx.s -o xxx.o (汇编为二进制的 .o 文件)
4)链接
将目标文件链接库文件生成机器能够识别的二进制文件
gcc xxx.o -o xxx
.o文件:.o文件是源码编译出的目标文件(二进制文件)。每个文件经过编译都会形成一个目标文件(二进制文件),多个目标文件链接后才能形成可执行文件。
gcc的选项:
-c,只编译,不链接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,
通常用于编译不包含主程序的子程序文件。
-o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。
如果不给出这个选项,gcc就给出预设的可执行文件a.out。
-g,产生符号调试工具(GNU的gdb)所必要的符号资讯,
要想对源代码进行调试,我们就必须加入这个选项。
-O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、
连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,
但是,编译、连接的速度就相应地要慢一些。
-O2,比-O更好的优化编译、连接,当然整个编译、连接过程会更慢。
-Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。
-Ldirname,将dirname所指出的目录加入到程序函数档案库文件的目录列表中,
分文件
整个工程包含:
main.c ==> main
xxxx.c ==> 多个.c(不同功能函数接口)
单独的文件中,只要调用(使用),就需要声明。
.h ==> .c -.h
.h: 头文件 (每个c文件都跟着一个h文件,h文件的作用是放着c文件中函数的声明,结构体的定义等。)
包含内容: 1.包含其他头文件
2.函数声明
3.构造数据类型
4.宏定义
5.重定义名
6.全局变量
引用头文件:
库里头文件(系统定义头文件):
#include <stdio.h>
自定义头文件:
#include “add.h”
C语言中include <> 和include" "的区别
1. include <> :引用的是编译器的类库路径里面的头文件,用于引用系统头文件。
2. include " ":引用的是你程序目录的相对路径中的头文件,如果在程序目录没有找到引用的
头文件则到编译器的类库路径的目录下找该头文件,用于引用用户头文件。
3.所以:引用系统头文件,两种形式都会可以,include<>效率高,引用用户头文件,只能使
用include " "
二、条件编译
编译器根据条件的真假决定是否编译相关的代码
1、根据宏是否定义,其语法如下:
#ifdef <macro>
……
#else
……
#endif
2、根据宏的值是否为真,其语法如下:
#if <macro>
…
#else
…
#endif
3、根据宏是否定义,作用:防止头文件重复包含
#ifndef <macro>
#define <macro>
....
#endif
三、make工具
1.工程管理器,顾名思义,是指管理较多的文件
Make工程管理器也就是个“自动编译管理器”,这里的“自动”
是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大量的编译工作。
Makefile会把规则中的第一个目标作为终极目标
2.make工具的作用
当项目中包含多个c文件,但只对其中1个文件进行了修改,
那用gcc编译会将所有的文件从头编译一遍,这样效率会非常低;
所以通过make工具,可以查找到修改过的文件(根据文件时间戳),
只对修改过的文件进行编译,这样大大减少了编译时间,提高编译效率
(一般来说,我们编写的代码可能永远在一个文件中,是需要多个文件,其中包括:main函数的主文件+若干个子文件+若干个头文件。如果我们每次处理以上若干文件,不管文件有没有修改过,只要gcc处理,都会将全部文件进行编译,浪费编译时间与系统进程。
使用make工程管理工具,只对修改过的文件进行编译,对其他未修改文件不做处理,最后将所有文件一起进行链接。)
3. Makefile是Make读入的唯一配置文件
4. Makefile的编写格式
格式:
目标:依赖
<tab> 命令
add:add.o main.o
gcc add.o main.o -o add
add.o:add.c
gcc -c add.c -o add.o
main.o
目标实现,需要通过依赖文件实现
可以只有目标没有依赖。
-c ,只编译,不链接成为可执行文件,编译器只是 由输入的.c等源代码文件 生成 .o 为后缀的目标文件, 通常用于编译不包含主程序的子程序文件。
-o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。
如果不给出这个选项,gcc就给出预设的可执行文件a.out。
1> 有目标没有依赖
make执行Makefile文件内的指令,
执行"make"默认执行的是第一个目标
对应的命令。若想执行剩余的目标,需要 "make 目标"执行。
.PHONY:clean ==> .PHONY
声明一个伪命令。若之前的目标中有生成可执行文件名为clean。
执行“make clean”时,make会认为clean目标已实现。不执行clean
目标对应的指令,用.PHONY声明后,make认为clean是一个指令,
“make clean”时会执行clean指令对应的命令。
2>有目标有依赖
目标:依赖
<tab>命令
(第一个目标:可执行文件名 )
一般是多个文件实现一项工程。对多个.c文件的管理。
四、变量
$HOME ($:表示取值)
HOME=/home/hq
1)预定义变量
CC 默认值为cc,与gcc同
RM 默认值为rm -f
CFLAGS 无默认值,一般为c编译器的选项
OBJS 一般为目标文件xx.o
1)机器上面运行的是源码?还是可执行文件?
可执行文件
2)在虚拟机上面编写代码,在虚拟机上面执行代码
(编写代码和执行代码在同一个架构上)
3)架构:
win:X86
公司的产品:ARM架构;
4)在X86架构上编写和编译代码,在ARM架构上执行代码
交叉开发(编写代码、编译代码和执行代码的架构不一样)
编译代码的时候要使用交叉编译工具链去编译代码
2)自动变量:
$< 第一个依赖文件的名称
$@ 目标文件的完整名称
$^ 所有不重复的目标依赖文件,以空格分开
%.o:%.c:表示所有的.o文件依赖于所有的.c文件
以下不属于makefile的范畴(不更新时间戳)
五、shell脚本编程
shell ==》解析器:sh ksh csh bash
shell脚本:本质 ==》shell命令的有序集合
shell脚本编程:
将shell命令按照一定的逻辑顺序实现指定功能,
放到一个文件中文件叫脚本文件,后缀.sh,可以直接执行,不用编译。
shell脚本语言 ==》解释型语言
写一个shell脚本文件步骤:
1. 创建一个脚本文件
touch xxx.sh
2. 将脚本文件权限修改为可执行
chmod 777 xxx.sh
3. 编辑脚本内容
vi xxx.sh
4. 执行脚本文件
./xxx.sh 或者 bash xxx.sh
六、shell变量
1.shell中允许建立变量存储数据,但是不支持数据类型,
(如:整型、字符、浮点类型),
所有赋值给变量的值都解释为一串字符。
2.变量的定义格式:
变量名=值
例:val=10
注:等号两边都不能有空格。
取shell变量的值:$变量名
3.shell变量的分类
1)环境变量 printenv 或 env
HOME=/home/hq
2)用户自定义变量
在shell编程中通常定义的变量名用大写,
变量的调用:$变量名
Linux Shell/bash从右向左赋值
如:Y=yy
X=$Y
echo $X //输出yy
使用unset命令删除变量的赋值
如: X=xx
unset X
echo $X //没有任何输出
3)位置变量或命令行参数
$0 执行的脚本名
$1-$9、${10}-${n} 命令行空格传的参数
$# 命令行参数个数,除$0
$@ $* 遍历输出命令行参数内容
4)预定义变量
$? 获取的是上一句命令是否正确执行的结果
0:真 非0:为假
$$ 获取进程pid
七、shell中的语句
1)说明性语句
以#号开始到该行结束,不被解释执行(注释)
#!/bin/bash 告诉操作系统使用哪种类型的shell执行此脚本文件
2)功能性语句
任意的shell命令、用户程序或其它shell程序。
3)结构性语句
条件测试语句、多路分支语句、循环语句、循环控制语句等
八、功能性语句
1.read (类似c中scanf)
从终端获取值赋值给变量。
格式:read 变量名1 变量名2 ...
加提示语句:read -p "提示字符串" 变量名1 变量名2 ...
注:把终端读入空格隔开的 第一个单词赋值给第一个变量,第二个单词赋值给第二个变量,依次类推赋值,剩余所有单词赋值给最后一个变量。
2. expr
算术运算命令expr主要用于进行简单的整数运算,
包括加(+)、减(-)、乘(\*)、整除(/)和求模(%)等操作
如:expr \( 12 + 3 \) \* 2
NUM=`expr 12 + 3` :将运算的结果赋值给变量
注意:
1)运算符左右两侧必须有空格
2)*和()必须加转义字符,\* 、 \( \)
3)expr语句可以直接输出运算结果
3.let
在运算中不能有空格
运算结果需要赋给一个变量
变量参与运算的过程不用加$取值、不用加 \
如:
let r=(1+3)*2
let r+=1 // ((r++))
练习:计算表达式值a*b+c-d/b;
要求:
1)从终端读入4个数a,b,c,d;a=2,b=3,c=15,d=18
2)进行运算
3)打印结果输出到终端 (15)
4.test
test语句可测试三种对象:
字符串 整数 文件属性
每种测试对象都有若干测试操作符
1)字符串的测试:(等号两边加空格)
s1 = s2 测试两个字符串的内容是否完全一样
真,假
echo $? 输出上一条命令是否正确执行的结果 (0正确 非0 不正确)
s1 != s2 测试两个字符串的内容是否有差异
-z s1 测试s1 字符串的长度是否为0
-n s1 测试s1 字符串的长度是否不为0
2)整数的测试:
a -eq b 测试a 与b 是否相等
a -ne b 测试a 与b 是否不相等
a -gt b 测试a 是否大于b
a -ge b 测试a 是否大于等于b
a -lt b 测试a 是否小于b
a -le b 测试a 是否小于等于b
3)文件属性的测试;
-d name 测试name 是否为一个目录
-f name 测试name 是否为普通文件
-e name 测试文件是否存在
九、结构性语句:
1.if…then…fi
1)基本结构:
if 表达式
then
命令表
fi
如果表达式为真, 则执行命令表中的命令;
否则退出if语句, 即执行fi后面的语句。
if和fi是条件语句的语句括号, 必须成对使用;
命令表中的命令可以是一条, 也可以是若干条。
例如:
#!/bin/bash
if [ -e $1 ]
then
echo "$1 exist"
exit; //相当于结束程序
fi
下面两种方式等价
//
if test -f $filename
then
fi//
//if [ -f $filename ]
then
fi//
2)分层结构1:
if 表达式
then
命令表1
else
命令表2
fi
分层结构2:
if 表达式1
then
命令表1
elif 表达式2
then
命令表2
elif 表达式3
then
命令表3
else
命令表4
fi
3)嵌套结构:
if 表达式
then
命令表1 (或表达式)
else
if 表达式
then
命令表
fi
fi
注意:
如果表达式为真, 则执行命令表中的命令; 否则退出if语句, 即执行fi后面的语句。
if和fi是条件语句的语句括号, 必须成对使用;命令表中的命令可以是一条, 也可以是若干条。
练习1:利用if语句实现找出三个数中最大的数
#!/bin/bash
read a b c
if [ $a -gt $b ]
then
if [ $a -gt $c ]
then
max=$a
else
max=$c
fi
else
if [ $b -gt $c ]
then
max=$b
else
max=$c
fi
fi
echo $max
通过逻辑与实现:
#! /bin/bash
read -p "please input:" a b c
if [ $a -gt $b ] && [ $a -gt $c ]
then
echo $a
elif [ $b -gt $a ] && [ $b -gt $c ]
then
echo $b
else
echo $c
fi
case...esac
case 字符串变量 in
模式1)
命令表1
;;(相当于C语言中的break)
模式2)
命令表2
;;
……
*)
命令表n
;;
esac
注意:
1)case语句只能检测字符串变量
2)命令表以单独的双分号行结束,退出case语句
3)模式 n常写为字符* 表示所有其它模式
4)case匹配项中可以存在多个模式,每种模式之间用 | 隔开
例子:
逻辑运算符的书写格式:
|| :逻辑或
[ 表达式1 ] || [ 表达式2 ]
[ 表达式1 -o 表达式2 ]
&& :逻辑与
[ 表达式1 ] && [ 表达式2 ]
[ 表达式1 -a 表达式2 ]
! :逻辑非
[ ! 表达式 ]
练习:学生成绩管理系统,用shell中的case实现
90-100:A
80-89:B
70-79:C
60-69:D
<60:不及格
#!/bin/bash
read -p "please input score:" score
if [ $score -ge 0 -a $score -le 100 ]
then
let num=score/10
case $num in
9|10)
echo "A"
;;
8)
echo "B"
;;
7)
echo "C"
;;
6)
echo "D"
;;
*)
echo "不及格"
esac
else
echo "input error!"
fi
循环语句
for..do..done
格式:
for 变量名 in 单词表
do
命令表
done
变量依次取单词表中的各个单词,每取一次单词, 就执行一次循环体中的命令.
循环次数由单词表中的单词数确定.
命令表中的命令可以是一条, 也可以是由分号或换行符分开的多条。
for语句的几种书写格式:
1)for i in 1 2 3 4 do....done :
变量i从单词表中取值
例如:
for I in 1 3 5 7 9 // for I in `seq 1 2 10`
(1是起始数字,10是终止数字,2是增加的数字,及两个数之间差值为2)
do
echo "$I"
done
这样就打印出了1 3 5 7 9
2)for i do...done:
变量i从命令行取值,可以省略in 单词表
3)for i in {1..10} do...done:
(大括号和数字之间不加空格)
变量i从1~10
4)for ((i = 0; i < 10; i++)) do...done
do
echo "I=$I"
done
练习:求1到100的和。
#!/bin/bash
for ((i = 1; i <= 100; i++))
do
let sum=$sum+$i
done
echo "sum = $sum"
shell脚本的死循环
(1) for((;;))
do
echo "for"
done
(2) while true
do
echo "while"
done
while..do..done:
格式:
while 命令或表达式
do
命令表
done
while语句首先测试其后的命令或表达式的值,
如果为真,就执行一次,然后再测试该命令或表达式的值,
执行循环体,直到该命令或表达式为假时退出循环。
写while的表达式时候,表达式与中括号之间加空格
#!/bin/bash
I=0
while [ $I -lt 5 ]
do
I='expr $I + 1'
echo "$I"
done
while [$I -lt 5]//改成while true 就是死循环了。
练习二:1-10求和运算,用while写
#!/bin/bash
SUM=0
I=1
while [ $I -le 10 ]
do
let SUM=$SUM+$I
let I=I+1
done
echo $SUM