龙空技术网

Linux C 编程 - 程序的基本概念

大话幽默一刻 236

前言:

现时各位老铁们对“c编译原理语义分析”大致比较注重,朋友们都想要学习一些“c编译原理语义分析”的相关内容。那么小编在网络上搜集了一些对于“c编译原理语义分析””的相关知识,希望各位老铁们能喜欢,看官们快快来了解一下吧!

程序以及使用的编程语言

一段程序是告诉计算机应该如何完成一个任务,这里需要进行的运算可以是数学运算,也可以是符号运算。从硬件角度来说,计算机是由数字电路组成的运算机器,只能对数字做运算,符号运算的原理是在计算机内部符号是用数字来表示的。此外,程序还可以处理声音和图像,声音和图像在计算机内部也是用数字来表示的,这些数字经过特定的硬件设备转换成人可以听到,看到的声音和图像。

程序是由一系列指令组成的,指令是指示计算机做某种运算的命令,主要包括以下几类:

输入(input)

从键盘,文件或者其它设备中输入的数据。

输出(output)

把数据显示到屏幕,或者存入一个文件,或者发送到其它设备。

基本运算

执行最基本的算术运算和数据读取与存入。

测试和分支

测试某个条件,然后根据不同的测试结果执行不同的后续指令。

循环和递归

重复执行一系列操作。

对于一个程序,不管它多么复杂,都是由这几类指令组成的。编写程序可以拆分成以下的过程:

把复杂的任务分解成子任务,把子任务再分解成更简单的任务,层层分解,直到最后简单的可以使用以上指令来完成。

编程语言(Programming Language)分为低级语言(Low-level Language) 和高级语言(High-level Language)。机器语言(Machine Language) 和汇编语言(Assembly Language) 属于低级语言,直接使用计算机指令编写程序。而C,Java,Python等属于高级语言,用语句(Statement)编写程序,语句是计算机指令的抽象表示。

C语言的语句和低级语言的指令之间不是简单的一对一对应关系,一条a=b+1; 语句要翻译成三条汇编或机器指令,这个过程称为编译(Compile), 由编译器(Compiler) 来完成,显然编译器的功能比汇编器要复杂。

用C语言编写的程序必须经过编译转成机器指令才能被计算机执行,编译需要花一些时间,这是用高级语言编程的一个缺点,然而更多的是优点。首先,用C语言编程更容易,写出来的代码更加紧凑,可读性更强,出错也更容易修改。C 语言是可移植的与平台无关。

平台:可以指计算机体系结构(Architecture), 也可以指操作系统(Operating System), 也可以指开发平台(编译器,链接器等)。不同的计算机体系结构有不同的指令集(Instruction Set), 可以识别的机器指令格式是不同的,直接用某种体系结构的汇编或机器指令写出来的程序只能在这种体系结构的计算机上运行。

即使在相同的体系结构和操作系统下,用不同的C编译器(或者同一个C编译器的不同版本)编译同一个程序得到的结果也有可能不同,C语言有些语法特性在C标准中并没有明确规定,各编译器有不同的实现,编译出来的指令的行为特性也会不同,应该尽量避免使用不可移植的语法特性。

总结一下编译执行的过程:

source code ==> compiler ==> executable ==> loader
程序的调试

编译时错误

编译器只能翻译语法正确的程序,否则将导致编译失败,无法生成可执行文件。

运行时错误

编译器检查不出这类错误,仍然可以生成可执行文件,但在运行时会出错而导致程序崩溃。

要注意区分编译时和运行时(Run-time)这两个概念,不仅在调试时需要区分这两个概念,在学习C语言的很多语法时都需要区分这两个概念,有些事情在编译时做,有些事情则在运行时做。

逻辑错误和语义错误

如果程序里有逻辑错误,编译和运行都会很顺利,看上去也不产生任何错误信息,但是程序没有干它该干的事情,而是干了别的事情。

编写第一个程序

Hello World

#include <stdio.h> /* main: generate some simple output */ int main(void){    printf("Hello, world.\n");    return 0;}

将文件保存为main.c, 然后编译运行:

$ gcc main.c$ ./a.out$ Hello, world.

gcc是Linux平台的C编译器,编译后在当前目录下生成可执行文件a.out,直接在命令行输入这个可执行文件的路径就可以执行它。如果不想把文件名叫a.out,可以用gcc-o参数自己指定文件名:

$ gcc main.c -o main$ ls1  1.c  2  2.c  main  main.c  MOBILE.txt  UNICOM.txt$ ./mainHello, world.

正如前面所说,编译器对于语法错误是毫不留情的,如果你的程序有一点拼写错误,例如第一行写成了stdoi.h,在编译时会得到错误提示:

$ gcc main.c -o mainmain.c:1:19: fatal error: stdoi.h: No such file or directory #include <stdoi.h>                   ^compilation terminated.

有些时候编译器的提示信息不是error而是warning,例如把上例中的printf("Hello, world.\n");改成printf(1);然后编译运行:

$ gcc main.c -o mainmain.c: In function ‘main’:main.c:7:5: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast [enabled by default]     printf(1);     ^In file included from main.c:1:0:/usr/include/stdio.h:362:12: note: expected ‘const char * __restrict__’ but argument is of type ‘int’ extern int printf (const char *__restrict __format, ...);            ^main.c:7:5: warning: format not a string literal and no format arguments [-Wformat-security]     printf(1);
$ ./mainSegmentation fault (core dumped)

这个警告信息是说类型不匹配,但勉强还能配得上。警告信息不是致命错误,编译仍然可以继续,如果整个编译过程只有警告信息而没有错误信息,仍然可以生成可执行文件。但是,警告信息也是不容忽视的。出警告信息说明你的程序写得不够规范,可能有Bug,虽然能编译生成可执行文件,但程序的运行结果往往是不正确的,例如上面的程序运行时出了一个段错误,这属于运行时错误。

一个好的习惯是打开gcc-Wall选项,也就是让gcc提示所有的警告信息,不管是严重的还是不严重的,然后把这些问题从代码中全部消灭。比如把上例中的printf("Hello, world.\n");改成printf(0);然后编译运行:

chengy@ubuntu-1:~/workroom/c_test/0802$ gcc -Wall main.cmain.c: In function ‘main’:main.c:7:5: warning: null argument where non-null required (argument 1) [-Wnonnull]     printf(0);

如果printf中的0是你不小心写上去的(例如错误地使用了编辑器的查找替换功能),这个警告就能帮助你发现错误。虽然本书的命令行为了突出重点通常省略-Wall选项,但是强烈建议你写每一个编译命令时都加上-Wall选项。

标签: #c编译原理语义分析