一C语言程序框架
一个C程序,就是一个.c文件(后缀为.c) ---- 这个文件也被称之为 源文件,除了源文件之外,开发过程中,还包括头文件(.h)
C语言的第一个代码如下:
#include <stdio.h> //引用头文件 stdio ---- 标准的输入和输出 i---in o---out,如果一个程序中,不需要使用到标准io,那么,它可以不引入这个头文件
int main(void)//C程序入口函数,也就是程序是从这个函数开始执行(它是第一个被调用的函数)
{
printf("Hello, World!\n"); //这个就是C语言的一个语句,以分号结束 ,该语句为一个函数的调用
return 0;
}
在进程C程序设计时,程序是由.c和.h组成,但是,在一些场合下,程序中所使用的接口(.c和.h),不一定全部是自己进行设计,接口可能来自系统(C标准库接口),也可能来自第三方库
如果来自系统,那么,一般只需要引入接口的头文件即可,在头文件中,可能看到接口的原型和参数,具体功能的实现一般被封装在对应的.c文件中,而.c文件通常被封装成动态库(.so)或者静态库(.a)---以Linux为例,
动态库和静态库制作,在后面的课程中进行学习
#include <stdio.h> //C语言的标准 输入 和输出
输出:printf-----这个函数是由C语言提供,所以,要先学习了解
这个函数的原型:
void ----->无,没有
它做为函数的返回值,说明没有返回值,不返回
它做为函数的形参,说明这个函数不接受传递参数
main() 和 main(void) 思考
main() ---- 它是可以接受传参,并且参数的个数不一定
main(void) ---- 它是不接受传参
main函数如此,那么,其他自定义的函数也一样
main这个函数是C程序的入口函数,并且一个程序只有一个,函数名不能修改,因为它是被
操作系统调用的
功能:格式化输出,默认是将信息输出到控制台(window)或者终端(linux)
头文件:#include <stdio.h>
函数原型:
int printf(const char *format, ...);
函数参数说明:
参数一:const char *format ----> 参数类型为字符串类型
参数二: ... ---- 不确定,它的有无取决于参数一,
如果参数一有格式化,那么,就有它
格式化 ----- > %进制 ----》%d %c %s等,它的一个作用就相当于内容占位符
void main()
{
}
int main(void)
{
return 0;
}
以下两个是等 价
int main(int argc,char** argv)
{
return 0;
}
int main(int argc,char * argv[ ])
{
return 0;
}
'Hello,World!' 中文意思是“你好,世界”。因为 The C Programming Language 中使用它做为第一个演示程序,后来的程序员在学习编程或进行设备调试时延续了这一习惯
二 程序创建和编译
第一步:点击工具的菜单 -----》选择“文件”---》在下拉列表中,选择“新建文件或者工程”,在弹出对话框中,做如下选择
第二步:在弹出对话框,输入工程名和工程路径
备注:工程路径不能是中文
第三步:工程的工具集选择
四步:项目创建完成说明
第五步:点击完成后,工程会自动进行构建
在这个构建过程中,需要一点时间,耐心等待即可。
2.2 工程编译
第一步:设置要编译的工程
第二步:执行工程编译
方法一:右击工程,在弹出的对话框中,选择"运行" ---适合开发工具打开多个工程
方法二:快捷运行按钮
三 编译器(GCC)
GCC(GNU Compiler Collection,即 GNU 编译器套装),是一套由 GNU 开发的编程语言编译器。它是一套以 GPL 及 LGPL 许可证所发行的自由软件,也是 GNU 计划的关键部分,亦是自由的类 Unix 及苹果计算机 Mac OS X 操作系统的标准编译器。GCC(特别是其中的 C 语言编译器)也常被认为是跨平台编译器的事实标准。
Linux 系统下的 GCC 编译器实际上是 GNU 编译工具链中的一款软件,可以用它来调用其他不同的工具进行诸如预处理、编译、汇编和链接这样的工作。GCC 不仅功能强大,性能优越,其执行效率比一般的编译器相比要高 20%~30%,而且由于其是 GNU 项目之一,是开源的软件,我们可以直接从网上免费地下载安装它,是名副其实的免费大馅儿饼。
有兴趣的同学,可以查看gcc命令的用法
qq_44203805@ubuntu:/mnt/hgfs/ctest$ man gcc
3.1 程序编译
3.1.1 使用GCC 编译器
- 打开终端并切换到代码所在的路径
- 使用gcc 编译器进行编译
gcc hello.c // 直接使用gcc 进行编译, 并默认生成a.out ---->相当于windows下的exe程序
gcc hello.c -Wall // -Wall 希望编译器把所有的警告都输出 ,它是一个可选项参数,在编译程序时,可加可不加
gcc -o hello hello.c -Wall // -o 指定生成文件的名字一般与源代码名字一致,也可以跟源文件名字不一样,并且-o 和 生成的文件位置可以调整,但是,他们俩必须在一起
//gcc hello.c -Wall -o hello
3.1.2 程序运行
3.1.2 程序运行
./hello // 运行当前目录下的hello文件
最终演示效果:
总结:自己写的C程序,可以像其他的程序在任何路径下去执行,比如:gcc 这部分内容在Linux基础会继续重点讲解
主函数其他形式:
函数头如下:
int main(int argc, char const *argv[])
打印输出主函数收到的参数:
printf("%d , %s \n" , argc , argv[3]);
注意:
argc --> 指的是 给主函数传递的参数的个数
argv --> 本质上是一个数组,存放的是一些字符串的地址
给主函数所传递的各个参数内容
\n --> 作为输出内容的换行符
刷新缓冲区(标准输出的缓冲区是行缓冲)
通俗理解:当标准输出缓冲区中收到一个换行符,的时候把缓冲区的数据进行刷新(输出)
换行符可以让数据立即刷新到终端。
演示结果:
以下这种写法,也是可以,但是,从语法不严谨,一般不推荐使用
函数如下: int main() //int main(void)他们两个区别,会在函数传参过程中讲解 { }
3.2 编译器的作用
这个程序是一个符合 C 语言语法的文本源程序,不能指望 CPU 能像我们这样“读懂”它,CPU 没长眼睛,它只能“读懂”像这样的二进制序列:10110101010001……,因此我们必须将写好的文本程序编译生成一个可以被处理器直接解释的二进制指令文件,GCC 可以帮助我们达到这个目的。
开发的最主要两个参数:提高程序的开发效率 提高程序的效率运行
计算机(CPU)无法直接识别我们所写的C代码,因此我们需要借助编译器来把 C的代码翻译成计算机能直接读懂的二进制可执行文件。
编译器的编译过程:
3.2.1 预处理
GCC 在第一个阶段会调用预处理器 cpp 来对 C 源程序进行预处理,所谓的预处理就是解释源程序当中的所有的预处理指令,那些诸如#include、#define、#if 等以井号’#’开头的语句(注释也会去掉)就是预处理指令,预处理指令实际上并不是 C 语言本身的组成部分,而 是为了更好地组织程序所使用的一些“预先处理的”工作,这些工作用一种叫做与处理指令的语句来描述,然后用预处理器来解释,这些工作包括我们熟悉的诸如文件包含、宏定义、条件编译等等。
$ gcc hello.c -o hello.i -E
加上一个编译选项 -E 就可以使得 GCC 在进行完第一阶段的预处理之后停下来,生成一个默认后缀名为.i 的文本文件。
3.2.2 编译
经过预处理之后生成的.i 文件依然是一个文本文件,不能被处理器直接解释,我们需要进一步的翻译。接下来的编译阶段是四个阶段中最为复杂的阶段,它包括词法和语法的分析,最终生成对应硬件平台的汇编语言(不同的处理器有不同的汇编格式),具体生成什么平台的汇编文件取决于所采用的编译器,如果用的是 GCC,那么将会生成 x86 格式的汇编文件,如果用的是针对 ARM 平台的交叉编译器,那么将会生成 ARM 格式的汇编文件。
gcc hello.i -o hello.s -S
加上一个编译选项 -S 就可以使得 gcc 在进行完第一和第二阶段之后停下来,生成一个默认后缀名为.s 的文本文件。打开此文件看一看,你会发现这是一个符合 x86 汇编语言的源程序文件。
3.2.3 汇编
接下来的步骤相对而言比较简单,编译器 gcc 将会调用汇编器 as 将汇编源程序翻译成为可重定位文件。汇编指令跟处理器直接运行的二进制指令流之间基本是一一对应的关系,该阶段只需要将.s 文件里面的汇编翻译成指令即可。
gcc hello.s -o hello.o -c
大家看到,只要在编译的时候加上一个编译选项-c,则会生成一个扩展名为.o 的文件,这个文件是一个 ELF 格式的可重定位(relocatable)文件,所谓的可重定位,指的是该文件虽然已经包含可以让处理器直接运行的指令流,但是程序中的所有的全局符号(变量、函数的入口地址)尚未定位,所谓的全局符号,就是指函数和全局变量,函数和全局变量默认情况下是可以被外部文件引用的,由于定义和调用可以出现在不同的文件当中,因此他们在编译的过程中需要确定其入口地址,比如 a.c 文件里面定义了一个函数 func( ),b.c 文件里面调用了该函数,那么在完成第三阶段汇编之后,b.o 文件里面的函数 func( )的地址将是 0,显然这是不能运行的,必须要找到 a.c 文件里面函数 func( )的确切的入口地址,然后将 b.c 中的“全局符号”func重新定位为这个地址,程序才能正确运行。因此,接下来需要进行第四个阶段:链接。
3.2.4 链接
如前面所述,经过汇编之后的可重定位文件不能直接运行,因为还有两个很重要的工作没完成,首先是重定位,其次是合并相同权限的段。
关于重定位的问题,上面已经给出了简单的描述。更一般的地,我们编译一个程序通常都需要链接系统的标准 C 库、gcc 内置库等基本库文件。因为 Linux 下任何一个程序编译都需要用到这些基本库的全局符号。
$ gcc hello.o -o hello -lc -lgcc 标准 C 库和 gcc 内置库是如此的基本,因此-lc
-l 注意不是数字 1 他是英文字符 l ,表示需要链接的库的名字 -l ----> link ---链接程序运行所依赖的库的连接工作