[C++系列] UNIX 高级编程 day01 概述 操作系统 发展
in C/C++ with 0 comment

[C++系列] UNIX 高级编程 day01 概述 操作系统 发展

in C/C++ with 0 comment

产生尽可能多的警告
分文件声明、定义和调用函数
头文件的附加搜索路径
静态库的构建和使用
共享库的构建和使用
1 Hello, World !
1.1 问题

使用vi编写HelloWorld.c程序,运行后,在控制台输出“Hello World!”
1.2 步骤

实现此案例需要按照如下步骤进行。
步骤一:打开vi,编辑源程序

代码如下:

include <stdio.h>

int main()
{

 printf("Hello World!");
 return 0;

}
上述代码中,以下代码:

 printf("Hello World!");

是在控制台上输出字符串“Hello World!”,其中printf是一个函数,用于将参数字符串输出到控制台上。
步骤二:预编译

在控制台使用gcc -E HelloWorld.c -o HelloWorld.i命令进行编译预处理,完成编译前的预处理工作,将源文件HelloWorld.c处理成HelloWorld.i文件。
步骤三:编译

在控制台使用gcc -S HelloWorld.i命令将与处理后的高级语言文件HelloWorld.i翻译成汇编语言,得到汇编文件HelloWorld.s。
步骤四:汇编

在控制台使用gcc -c HelloWorld.s命令将汇编文件HelloWorld.s翻译成机器指令,得到目标文件HelloWorld.o。
步骤五:链接

在控制台使用gcc HelloWorld.o -o HelloWorld命令将目标文件HelloWorld.o和标准库链接,得到可执行程序HelloWorld。
步骤六:运行

在控制台敲入可执行程序名HelloWorld,然后回车即可执行该文件。
1.3 完整代码

本案例的完整代码如下所示:

include <stdio.h>

int main()
{

 printf("Hello World!");
 return 0;

}
2 产生尽可能多的警告
2.1 问题

gcc是gnu旗舰产品,它的功能非常强大,有多达上千个选项用于完成各种功能。本案例介绍如何产生尽可能多的警告。
2.2 步骤

实现此案例需要按照如下步骤进行。
步骤一:使用Wall选项产生尽可能多的警告

在控制台使用gcc -Wall HelloWorld.c命令一次性编译链接源文件HelloWorld.c时,如果源文件有问题,则会产生尽可能多的警告,以提示程序的问题所在。
3 分文件声明、定义和调用函数
3.1 问题

当需要编写较大型的程序时,往往把与这方面相关的函数集中到某一个文件中,把那方面相关的函数集中到另一个文件中。这时就需要将函数的声明放在扩展名为.h的头文件中,将函数的定义放在扩展名为.c的文件中,而在其他的扩展名为.c的文件中调用它们。
3.2 步骤

实现此案例需要按照如下步骤进行。
步骤一:定义头文件print.h

代码如下所示:

ifndef exam_print_h

define exam_print_h

void printArea(char* str, double area);

endif

上述代码中,以下代码:

ifndef exam_print_h

define exam_print_h

和代码:

endif

在一起的作用是防止该头文件中其间的内容被多次包含,而造成的重复定义错误。
上述代码中,以下代码:
void printArea(char* str, double area);
在头文件中声明了一个函数。
步骤二:在print.c文件中定义头文件print.h中声明的函数体

代码如下所示:

include <stdio.h>

include "print.h"

void printArea(char* str, double area)
{
printf("图形%s的面积是%lfn", str, area);
}
注意:需要使用#include将头文件包含进来。
上述代码中,以下代码:
void printArea(char* str, double area)
{
printf("图形%s的面积是%lfn", str, area);
}
是头文件print.h中声明的函数的定义。
步骤三:在main.c文件中调用头文件print.h中声明的函数

代码如下所示:

include "print.h"

int main(int argc, const char * argv[])
{
// insert code here...
printArea("圆", 100);
return 0;
}
注意:需要使用#include将头文件包含进来。
上述代码中,以下代码:
printArea("圆", 100);
是头文件print.h中声明的函数的调用。
步骤四:编译链接

在控制台使用gcc main.c print.c –I <指定路径>命令一次性编译链接源文件main.c和print.c,由于没有指定生成的文件名,所以生成可执行文件a.out。
上述命令中,选项-I的作用是先找-I指定的目录,再找当前目录,最后找系统目录。
3.3 完整代码

本案例中的完整代码如下所示:
文件print.h

ifndef exam_print_h

define exam_print_h

void printArea(char* str, double area);

endif

文件print.c

include <stdio.h>

include "print.h"

void printArea(char* str, double area)
{
printf("图形%s的面积是%lfn", str, area);
}
文件main.c

include "print.h"

int main(int argc, const char * argv[])
{
// insert code here...
printArea("圆", 100);
return 0;
}
4 头文件的附加搜索路径
4.1 问题

unix是一个多用户的操作系统,意味着每个用户登录系统之后,都有自己专用的运行环境。这个运行环境是由一组变量所定义,这组变量被称为环境变量。用户可以对自己的环境变量进行修改以达到对环境的要求。
本案例介绍如何添加头文件的附加搜索路径。
4.2 步骤

实现此案例需要按照如下步骤进行。
步骤一:头文件的附加搜索路径

在控制台上输入如下命令:
export C_INCLUDE_FILE=/Users/tarena/desktop
即可将C头文件的附加搜索路径设置为/Users/tarena/desktop,相当于gcc的-I选项的功能。
5 静态库的构建和使用
5.1 问题

静态库的本质就是将多个目标文件打包成一个文件。在使用时链接静态库就是将库中被调用的代码复制到调用模块中。优点是使用静态库的代码在运行时无需依赖库,且执行效率高,缺点是静态库占用空间大,库中代码一旦修改必须重新链接。
5.2 步骤

实现此案例需要按照如下步骤进行。
步骤一:构建静态库

文件math.h代码如下所示:

ifndef math_h

define math_h

double add(double x, double y);
void show(double result);

endif

文件calc.c代码如下所示:

include <stdio.h>

include "math.h"

double add(double x, double y)
{

return x + y;

}
文件calc.c代码如下所示:

include <stdio.h>

include "math.h"

void show(double result)
{

printf("%lf\n", result);

}
使用如下命令,将C源文件编译成目标文件:
gcc –c calc.c
gcc –c show.c
使用如下命令,将目标文件打包成静态库文件
ar –r libmath.a calc.o show.o
步骤二:使用静态库

文件main.c代码如下所示:

include <stdio.h>

include "math.h"

int main()
{

printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return 0;

}
使用如下命令,与静态库文件一同生成可执行文件:
gcc main.c libmath.a
也可以使用如下命令,先指定静态库文件的路径,再生成可执行文件:
export LIBRARY_PATH=静态库文件所在路径
gcc main.c –lmath //选项-l用于指定静态库名
还可以使用如下命令,生成可执行文件:
gcc main.c –lmath –L静态库文件所在路径
5.3 完整代码

本案例中的完整代码如下所示:
文件math.h代码如下所示:

ifndef math_h

define math_h

double add(double x, double y);
void show(double result);

endif

文件calc.c代码如下所示:

include <stdio.h>

include "math.h"

double add(double x, double y)
{

return x + y;

}
文件calc.c代码如下所示:

include <stdio.h>

include "math.h"

void show(double result)
{

printf("%lf\n", result);

}
文件main.c代码如下所示:

include <stdio.h>

include "math.h"

int main()
{

printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return 0;

}
6 共享库的构建和使用
6.1 问题

共享库和静态库最大的不同就是,链接共享库并不需要将库中被调用的代码复制到调用模块中,相反被嵌入到调用模块中的仅仅是被调用代码在共享库中的相对地址。如果共享库中的代码同时为多个进程所用,共享库的实例在整个内存空间中仅需一份,这正是共享的意义所在,共享库的优点是占用空间小,即使修改了库中的代码,只要接口保持不变,无需重新链接,缺点是使用共享库的代码在运行时需要依赖库,执行效率略低。
6.2 步骤

实现此案例需要按照如下步骤进行。
步骤一:共享库的构建

文件math.h代码如下所示:

ifndef math_h

define math_h

double add(double x, double y);
void show(double result);

endif

文件calc.c代码如下所示:

include <stdio.h>

include "math.h"

double add(double x, double y)
{

return x + y;

}
文件calc.c代码如下所示:

include <stdio.h>

include "math.h"

void show(double result)
{

printf("%lf\n", result);

}
使用如下命令,将C源文件编译成目标文件:
gcc –c –fpic calc.c
gcc –c –fpic show.c //选项fpic是指生成位置无关代码,即调用代码通过相对地址标识被调用代码的位置,模块中的指令与该模块被加载到内存中的位置无关。
使用如下命令,将目标文件打包成共享库文件
gcc –shared calc.o show.o –o libmath.so
编译和链接也可以合并为一步完成
gcc -shared -fpic calc.c show.c -o libmath.so
步骤二:共享库的使用

文件main.c代码如下所示:

include <stdio.h>

include "math.h"

int main()
{

printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return 0;

}
使用如下命令,与共享库文件一同生成可执行文件:
gcc main.c libmath.so
也可以使用如下命令,先指定共享库文件的路径,再生成可执行文件:
export LIBRARY_PATH=共享库文件所在路径
gcc main.c –lmath //选项-l用于指定共享库名
还可以使用如下命令,生成可执行文件:
gcc main.c –lmath –L共享库文件所在路径
运行时需要保证LD_LIBRARY_PATH环境变量中包含共享库所在的路径,可使用以下控制台命令设置
$ export LD_LIBRARY_PATH=共享库文件所在路径
6.3 完整代码

本案例中的完整代码如下所示:
文件math.h代码如下所示:

ifndef math_h

define math_h

double add(double x, double y);
void show(double result);

endif

文件calc.c代码如下所示:

include <stdio.h>

include "math.h"

double add(double x, double y)
{

return x + y;

}
文件calc.c代码如下所示:

include <stdio.h>

include "math.h"

void show(double result)
{

printf("%lf\n", result);

}
文件main.c代码如下所示:

include <stdio.h>

include "math.h"

int main()
{

printf("5.3 + 2.8 = ");
show(add(5.3, 2.8));
return 0;

}

一、计算机框架
存疑?
透明
接口 实现用户 使用用户
void funcA(void){

....
funcB();
.....

}
函数的封装

C语言的语法 编译器
bash脚本编程

操作环境
用户操作计算机的时候,也需要一个操作的环境。
shell 用户和系统交互的接口
bash 是shell的一种。

linux内核
GNU项目组 应用程序

GNU/LINUX操作系统

二、什么是操作系统?操作系统的分类
操作系统管理计算机的硬件资源
操作系统是一款软件
操作系统管理计算机的软件资源

子系统:
内存管理子系统
文件管理
文件系统
网络通讯
进程管理
进程间的通讯
线程管理
信号
这些就是我们的课程内容

操作系统的分类
unix linux windows macos ios

GNU 应用程序 linux 内核 LFS

redhat 套件

三、计算机语言的发展史
1 1 2 1
2 2 4 2
3 3 8 3
4 4 16 4
... ... ...
32 32 4G 32

汉语 英语 俄语 日语 ...

11110000,11110000,00000001,00000010 机器语言
add 1,2 汇编指令
11110000,11110001,00000110,00000010
sub 6,2

intel
arm 汇编语言 高级语言(C语言)
amd

举例说明 C程序到可执行程序的过程
代码参见 hello.c

框架 组件
1、预处理阶段
处理预处理指令和程序中的注释
程序中的注释全部清除
从程序源码中,以#开头的都是预处理指令。
指定预处理器(工具软件)所作的工作。

sharp !bang &and @at

include <> 或者“” 文件的内容复制粘贴到这个位置

define 宏

条件编译
gcc是一款集成工具。
day01$gcc -E hello.c -o hello.i
day01$ls
a.out hello.c hello.i 笔记
2、将预处理后的文件编译成汇编文件(cpu直接相关)
gcc -S hello.i -o hello.s

3、将汇编文件汇编成目标文件 (汇编器完成)
gcc -c hello.s -o hello.o

4、将目标文件和库文件、运行时文件链接,形成可执行文件
(链接器完成)
gcc hello.o
gcc hello.o -o hello

加载器 将代码加载到内存中

补充:
echo 字符串 就是将字符串输出到显示器
$? 最近执行程序的结果

帮助的获取
man 3 库函数的名字
man 2 系统调用名字
man 1 linux命令
man linux命令

程序和进程
程序是指令的集合,是静态的,存放在磁盘上的
进程是程序运行的实例,是动态的,在内存里。

小结:
.c 源文件
.i 预处理后的文件
.s 汇编文件
.o 目标文件
.h 头文件

-E 预处理
-S 编译
-c 编译
-o 文件名 指定输出的文件名

include <> 和“”的区别

<> 系统指定的路径下找头文件。找不到 ?报错
“” 首先在当前路径下找头文件,找不到?就到系统指定的路径下找
系统指定的路径是什么路径呢?
gcc -E hello.c -o hello.i -v
gcc hello.c -v

总结:
一、计算机的框架
二、操作系统
操作系统是管理计算机资源的一款软件。
三、计算机语言的发展史
预处理、编译、汇编、链接 、加载

Responses