再学C语言,复习&笔记。


系列顺序:
Linux C语言编程基本原理与实践
Linux C语言指针与内存
Linux C语言结构体

更多Linux知识:
一天一点linux


常用指令

sudo管理员权限
sudo apt-get update 更新资源(安装软件前最好先更新资源)
sudo apt-get install vim(软件名称) 安装某软件
cd ~ 进入home目录
pwd 显示当前目录路径
ls 显示当前文件夹下所有文件夹及文件
ls -l 进入当前目录的文件夹目录后查看当前文件夹包含的文件/文件夹的类型、创建时间、用户权限、用户和用户组
(最前面是“d”为文件夹;“-”为普通类型的文件)
touch + 文件名 新建文件
rm + 文件名 删除文件
mkdir + 目录名 新建目录
vi(vim) + 文件名 使用vim编辑文件
clear 清洁屏幕
cc -v 检查编译器
gcc -v 检查编译器

VIM命令模式:
dd 删除整行
i 在光标前面插入
a 在光标后面插入
x 删除单个字符
Shift + a 行尾插入
Shift + i 行首插入
o 下一行插入
Shift + o 上行插入
:$ 跳到代码末尾

第一个Linux C程序

编译文件:

1
cc a.c

会得到a.out
编译文件后查看文件权限:
rw-:可读、可写
rwx:可读、可写、可执行
三个rwx 1创建用户 2与创建用户在同一个用户组 3任意用户
运行文件:
“.” 当前路径
“./” 当前路径下的文件
执行文件:./a.out

多文件操作

sp max.c 新建max.c文件(窗口打开多个文件)
ctrl + w +下箭头 跳转到下一个编辑框
ctrl + w +下箭头 跳转到上一个编辑框
对应行数+d+d 剪切光标下相应行数的程序
9dd(代码占九行,剪切代码)
p粘贴在剪切板的程序
set nu 打开行号
:wqa 保存所有文件并退出

1
2
3
gcc hello.c -o hello.out  指定编译文件(-o 给编译后的文件重命名)
cp a.c b.c 拷贝文件:将a.c拷贝到b.c
cat a.c 查看源代码程序

分开编译:

1
gcc -c 文件名.c -o 文件名.o

函数可以先编译成.o文件,然后再和主文件一起编译成可执行文件。在源代码多的时候可以提高效率。
例如:

1
2
3
4
5
6
7
8
gcc -c max.c -o max.o
gcc max.o hello.c
./a.out
cp max.c min.c
rm a.out
gcc-c min.c -o min.o
gcc gcc max.o min.o hello.c
./a.out

gcc编译流程分为4步:
预处理(Pre-Processing) -> 编译(Compling) -> 汇编(Assembling) -> 连接(Linking)
预处理:处理#include、#define、#ifdef 等宏命令
编译:把预处理完的文件编译为汇编程序.s
汇编:把汇编程序.s编译为.o二进制文件
链接:把多个二进制文件.o集合(链接)成一个可执行文件

使用别人的静态库.o文件(机器码,无法查看原代码)可以创建.h文件,然后在源程序中#include<文件名.h>来引用。

hello.c

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include "max.h"
#include "min.h"

int main()
{
int a1 = 33;
int a2 = 21;
int maxNum = max(a1, a2);
int minNum = min(a1, a2);
printf("%d,%d\n", maxNum, minNum);
return 0;
}

max.c

1
2
3
4
5
6
7
8
int max(int a, int b)
{
if(a > b){
return a;
}else{
return b;
}
}

min.c

1
2
3
4
5
6
7
8
int min(int a, int b)
{
if(a < b){
return a;
}else{
return b;
}
}

max.h

1
int max(int a, int b);

min.h

1
int min(int a, int b);

makeFile的编写与使用

make工具可以将大型的开发项目分成若干个模块。
make工具可以很清晰和很快捷的编译和整理源文件。

rm *.o 表示删除所有.o文件
make -v 检查make版本
vi Makefile 编译修改过的部分
输出文件:源文件
一个tab gcc命令
从上到下逐层求精
eg:

1
2
3
4
5
6
hello.out:max.o min.o hello.c
gcc max.o min.o hello -o hello.out
max.o:max.c
gcc -c max.c
min.o:min.c
gcc -c min.c

最后用make命令执行

重复使用make时,中间生成过的文件不需要再生成,没有修改过的文件不用再编译,会直接跳过该段代码,所以更加节省编译时间。

gcc-c 参数的意义:
-c 参数是将源代码编译成“目标文件 .o”,不进行连接
后面多个目标文件可通过-o链接成可执行文件。
不使用这个参数时gcc会直接进行编译链接,生成可执行文件。

main函数详解

1
2
3
4
5
6
# main函数的完整形式
int main(int argc,char* argv[])
{
printf("hello world\n");
return 0;
}

gcc main.c -o main.out && ./ main.out
echo \$? 执行成功则返回0

参数

1
2
3
4
5
6
7
8
9
int main(int argc,char* argv[])
{
printf("argc is %d\n",argc);
int i;
for(i=0;i<argv;i++){
printf("argv[%d] is %s\n",i,argv[i]);
}
return 0;
}

./out.c -l -s asd qwe

输出为:

1
2
3
4
5
6
argc is 5
argv[0] is ./m3.out
argv[1] is -l
argv[2] is -a
argv[3] is asd
argv[4] is qwe

标准输入流输出流以及错误流

cio.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h> //标准输入输出
/*
stdin
stdout
stderr
*/
int main()
{
printf("hello world!\n"); //输出功能由标准输出流完成,printf是系统封装后的
int a;
scanf("%d", &a); //标准输入流,错误流是系统出错的时候的
printf("input value is : %d\n", a);
return 0;
}

在输入a.out时,程序执行,系统给程序提供了一个进程,当程序启动时,系统也提供给程序一系列的指针。linux将所有的外设(摄像头,打印机等),都视为文件。当启动应用程序时候,会产生三个文件(stdin,stdout,stderr,即标准输入,输出,错误流)。
默认情况下,输入流就是键盘,stdout默认为显示器,可切换为网卡或者打印机。

1
2
printf("Please input the value a:\n");
fprintf(stdout, "please input the value a:\n");

因为stdout默认是显示器,上两句就是等价的,printf其实就是fprintf封装后得到的。
同理,scanf和fscanf等价:

1
2
scanf("%d", &a);
fscanf(stdin, "%d", &a);

错误流:

1
2
3
4
5
if(a < 0){
fprintf(stderr, "the value must > 0\n");
return 1;
}
return 0;

通过返回值1和0,让程序知道出错了。再次编译cc cio.c -o a2.out,我们运行a2.out,输入2则正常执行,当输入-1,则有对应的提示,即错误流发挥作用。
其实Linux这个很大的系统,就是由这些类似的小工具完成的。当等于0是为正确,错的话为其他值。
标准的输入流,输出流,以及错误流还可以重定向。

Linux几乎可以用于任何领域,这里我们不得不提出linux的通道,管道起到了很重要的作用,不同应用程序之间要配合使用,就需要用到管道。
先说输入流,输出流和错误流的重定向机制:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main()
{
printf("input the int value i:\n"); \\printf其实对fprintf的封装,是从标准输出流(即stdout)来输出这个过程
scanf("%d", &i); //默认输入流是键盘
printf("input the int value j:\n");
scanf("%d", &j);
printf("i+j=%d\n", i+j);
}

cc main.c
得到a.out,运行a.out,我们分别输入3和5输入到终端。我们标准输出流是1,输出入是0。
>> 标准输出流重新定向符

1
2
3
./a.out 1>> a.txt   输出到a.txt(默认为1,可不写1
./a.out >> a.txt 内容追加下方
./a.out > a.txt 内容覆盖

执行命令后,分别输入3回车后再输入5。再使用命令cat a.txt,可以看到已经输出到文件里的内容。
错误流重定向:1>标准输出流重定向 2>标准错误流重定向
./a.out 1>t.txt 2>f.txt 将正确的输出流重定向输出到t.txt,错误的输出流重定向输出到f.txt

<< 标准输入流重新定向符

1
./a.out < input.txt 将input.txt的内容作为输入流传递给程序a.out

综合使用标准输出流、标准错误流、标准输入流

1
./a.out 1>t.txt 2>f.txt <input.txt

管道原理及应用

ls / 查看根目录
ls /etc/ 查看Linux默认配置目录
ls /etc/ | grep 关键字符 将etc文件输出到一个管道
|管道
eg. 要查找某个目录下有多少个文件名包含“ab”,可以写

1
ls /etc/ | grep ab

将前一个程序输出流重定向到grep,通过管道得到需要的内容。
ps -e | grep ssh
ps查看当前的进程,当前操作系统是否开启ssh进程

管道使用demo

程序avg.c,求任意个数的平均值:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main()
{
int sum, n;
scanf("%d,%d", &sum, &n);
float v = sum / n;
printf("v = %f\n", v);
return 0;
}

cc avg.c -o avg.out

再写一个统计输入的程序input.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main()
{
int flag = 1;
int i;
int count = 0;
int sum = 0;
while(flag){
scanf("%d", &i);
if(0==i){
break;
}
count++;
sum+=i;
}
printf("%d,%d\n",sum,count);
return 0;
}

cc input.c -o input.out
结合使用以上两个程序,将所有数据进行统计,执行input.out,之后通过管道经过avg.out计算平均值。
./input.out | ./avg.out
以上就是通过管道,将两个小程序连接起来得到更复杂的程序的过程。