-123.4567高清视界在线观看由%g08.3f输出的结果

当前位置: >>
Linux下c编程
第1章 编译与调试1.1 编译的概念和理解在进行 C 程序开发时,编译就是将编写的 C 语言代码变成可执行程序的过程,这一过程 是由编译器来完成的。编译器就是完成程序编译工作的软件,在进行程序编译时完成了一系 列复杂的过程。1.1.1程序编译的过程在执行这一操作时,程序完成了复杂的过程。一个程序的编译,需要完成词法分析、语 法分析、中间代码生成、代码优化、目标代码生成。本章将讲解这些步骤的作用与原理。 (1)词法分析。指的是对由字符组成的单词进行处理,从左至右逐个字符地对源程序进 行扫描,产生一个个的单词符号。然后把字符串的源程序改造成为单词符号串的中间程序。 在编译程序时,这一过程是自动完成的。编译程序会对代码的每一个单词进行检查。如果单 词发生错误,编译过程就会停止并显示错误。这时需要对程序中的错误进行修改。 (2)语法分析。语法分析器以单词符号作为输入,分析单词符号串是否形成符合语法规 则的语句。例如,需要检查表达式、赋值、循环等结构是否完整和符合使用规则。在语法分 析时,会分析出程序中错误的语句,并显示出结果。如果语法发生错误,编译任务是不能完 成的。 (3)中间代码生成。中间代码是源程序的一种内部表示,或称中间语言。程序进行词法 分析和语法分析以后,将程序转换成中间代码。这一转换的作用是使程序的结构更加简单和 规范。中间代码生成操作是一个中间过程,与用户是无关的。 (4)代码优化。代码优化是指对程序进行多种等价变换,使得从变换后的程序能生成更 有效的目标代码。用户可以在编译程序时设置代码优化的参数,可以针对不同的环境和设置 进行优化。 (5)目标代码生成。目标代码生成指的是产生可以执行的应用程序,这是编译的最后一 个步骤。生成的程序是二进制的机器语言,用户只能运行这个程序,而不能打开这个文件查 看程序的代码。1.1.2编译器所谓编译器,是将编写出的程序代码转换成计算机可以运行的程序的软件。在进行 C 程 序开发时,编写出的代码是源程序的代码,是不能直接运行的。需要用编译器编译成可以运 行的二进制程序。 Edited by Foxit Reader Copyright(C) by Foxit Corporation, For Evaluation Only. 第 5 章 编译与调试在不同的操作系统下面有不同的编译器。C 程序是可以跨平台运行的。但并不是说 Windows 系统下 C 语言编写的程序可以直接在 Linux 下面运行。Windows 下面 C 语言编写的 程序,被编译成 exe 文件。这样的程序只能在 Windows 系统下运行。如果需要在 Linux 系统 下运行,需要将这个程序的源代码在 Linux 系统重新编译。不同的操作系统下面有不同的编 译器。Linux 系统下面编译生成的程序是不能在 Windows 系统上运行的。1.2gcc 编译器gcc 是 Linux 下的 C 程序编译器,具有非常强大的程序编译功能。在 Linux 系统下,C 语 言编写的程序代码一般需要通过 gcc 来编译成可执行程序。1.2.1gcc 编译器简介Linux 系统下的 gcc 编译器(GNU C Compiler) 是一个功能强大、 性能优越的编译器。gcc 支持多种平台的编译,是 Linux 系统自由软件的代表作品。gcc 本来只是 C 编译器的,但是后 来发展为可在多种硬体平台上编译出可执行程序的超级编译器。各种硬件平台对 gcc 的支持 使得其执行效率与一般的编译器相比平均效率要高 20%~30%。gcc 编译器能将 C、C++源程 序、汇程语言和目标程序进行编译链接成可执行文件。通过支持 make 工具,gcc 可以实施项 目管理和批量编译。 经过多年的发展,gcc 已经发生了很大的变化。gcc 已经不仅仅能支持 C 语言,还支持 Ada 语言、C++语言、Java 语言、Objective C 语言、Pascal 语言、COBOL 语言等更多的语言集的编 译。gcc 几乎支持所有的硬件平台,使得 gcc 对于特定的平台可以编译出更高效的机器码。 gcc 在编译一个程序时,一般需要完成预处理(preprocessing) 、编译(compilation) 、汇 编(assembly)和链接(linking)过程。使用 gcc 编译 C 程序时,这些过程是使用默认的设置 自动完成的,但是用户可以对这些过程进行设置,控制这些操作的详细过程。1.2.2的扩展名。5.1.cgcc 对源程序扩展名的支持扩展名指的是文件名中最后一个点的这个点以后的部分。例如下面是一个 C 程序源文件那么这个文件的文件名是“5.1.c” ,扩展名是“.c” 。通常来说,源文件的扩展名标识源文 件所使用的编程语言。例如 C 程序源文件的扩展名一般是“.c” 。对编译器来说,扩展名控制 着缺省语言的设定。在默认情况下,gcc 通过文件扩展名来区分源文件的语言类型。然后根据 这种语言类型进行不同的编译。gcc 对源文件的扩展名约定如下所示。 .c 为扩展名的文件,为 C 语言源代码文件。 .a 为扩展名的文件,是由目标文件构成的库文件。 .C,.cc 或.cpp 为扩展名的文件,标识为 C++源代码文件。 .h 为扩展名的文件,说明文件是程序所包含的头文件。 .i 为扩展名的文件,标识文件是已经预处理过的 C 源代码文件,一般为中间代码文件。93 Edited by Foxit Reader Copyright(C) by Foxit Corporation, Linux 系统下 C 程序开发详解For Evaluation Only..ii 为扩展名的文件,是已经预处理过的 C++源代码文件,同上也是中间代码文件。 .o 为扩展名的文件,是编译后的目标文件,源文件生成的中间目标文件。 .s 为扩展名的文件,是汇编语言源代码文件。 .S 为扩展名的文件,是经过预编译的汇编语言源代码文件。 .o 为扩展名的文件,是编译以后的程序目标文件(Object file) ,目标文件经过连接成 可执行文件 此外,对于 gcc 编译器提供两种显示的编译命令,分别对应于编译 C 和 C++源程序的 编译命令。1.3C 程序的编译本章以一个实例讲述如何用 gcc 编译 C 程序。在编译程序之前,需要用 VIM 编写一个简 单的 C 程序。在编译程序时,可以对 gcc 命令进行不同的设置。1.3.1编写第一个 C 程序本节将编写第一个 C 程序。程序实现一句文本的输出和判断两个整数的大小关系。本书 中编写程序使用的编辑器是 VIM。程序编写步骤如下所示。 打开系统的终端。单击“主菜单”|“系统工具”|“终端”命令,打开一个系统 终端。 在终端中输入下面的命令,在用户根目录“root”中建立一个目录。mkdir c在终端界面中输入“vim”命令,然后按“Enter”键,系统会启动 VIM。 在 VIM 中按“i”键,进入到插入模式。然后在 VIM 中输入下面的程序代码。#include &stdio.h& int max(int i,int j ) { if(i&j) { return(i); } else { return(j); } } void main() { int i ,j,k; i=3; j=5; printf(&hello ,Linux.\n”); 94 Edited by Foxit Reader Copyright(C) by Foxit Corporation, For Evaluation Only. 第 5 章 编译与调试k=max(i,j); printf(&%d\n&,k); }代码输入完成以后,按“Esc”键,返回到普通模式。然后输入下面的命令,保存文件。:w /root/c/a.c这时,VIM 会把输入的程序保存到 c 目录下的文件 a.c 中。 再输入“:q”命令,退出 VIM。这时,已经完成了这个 C 程序的编写。1.3.2用 gcc 编译程序上面编写的 C 程序,只是一个源代码文件,还不能作为程序来执行。需要用 gcc 将这个 源代码文件编译成可执行文件。编译文件的步骤如下所示。 打开系统的终端。单击“主菜单”|“系统工具”|“终端”命令,打开一个系统 终端。这时进入的目录是用户根目录“/root” 。然后输入下面的命令,进入到 c 目录。cd c上一节编写的程序就存放在这个目录中。 “ls” 输入 命令可以查看这个目录下的文件。 显示的结果如下所示。 输入下面的命令,将这个代码文件编译成可执行程序。gcc a.c查看已经编译的文件。在终端中输入“ls”命令,显示的结果如下所示。a.c a.out输入下面的命令对这个程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。hello ,Linux. 5从上面的操作可知,用 gcc 可以将一个 C 程序源文件编译成一个可执行程序。编译以 后的程序需要添加可执行的权限才可以运行。在实际操作中,还需要对程序的编译进行各 种设置。95 Edited by Foxit Reader Copyright(C) by Foxit Corporation, Linux 系统下 C 程序开发详解For Evaluation Only.1.3.3可选参数。查看 gcc 的参数gcc 在编译程序时可以有很多可选参数。在终端中输入下面的命令,可以查看 gcc 的这些gcc --help在终端中显示的 gcc 的可选参数如下所示。进行程序编译时,可以设置下面的这些参数。用法:gcc [选项] 文件... 选项: -pass-exit-codes:在某一阶段退出时返回最高的错误码 --help:显示此帮助说明 --target-help:显示目标机器特定的命令行选项 -dumpspecs:显示所有内建 spec 字符串 -dumpversion:显示编译器的版本号 -dumpmachine:显示编译器的目标处理器 -print-search-dirs:显示编译器的搜索路径 -print-libgcc-file-name:显示编译器伴随库的名称 -print-file-name=&库&:显示 &库& 的完整路径 -print-prog-name=&程序&:显示编译器组件 &程序& 的完整路径 -print-multi-directory:显示不同版本 libgcc 的根目录 -print-multi-lib:显示命令行选项和多个版本库搜索路径间的映射 -print-multi-os-directory:显示操作系统库的相对路径 -Wa,&选项&: 将逗号分隔的 &选项& 传递给汇编器 -Wp,&选项&:将逗号分隔的 &选项& 传递给预处理器 -Wl,&选项&:将逗号分隔的 &选项& 传递给链接器 -Xassembler &参数&:将 &参数& 传递给汇编器 -Xpreprocessor &参数&:将 &参数& 传递给预处理器 -Xlinker &参数&:将 &参数& 传递给链接器 -combine:将多个源文件一次性传递给汇编器 -save-temps:不删除中间文件 -pipe:使用管道代替临时文件 -time:为每个子进程计时 -specs=&文件&: 用 &文件& 的内容覆盖内建的 specs 文件 -std=&标准&:指定输入源文件遵循的标准 --sysroot=&目录&:将 &目录& 作为头文件和库文件的根目录 -B &目录&:将 &目录& 添加到编译器的搜索路径中 -b &机器&:为 gcc 指定目标机器(如果有安装) -V &版本&:运行指定版本的 gcc(如果有安装) -v:显示编译器调用的程序 -###:与 -v 类似,但选项被引号括住,并且不执行命令 -E:仅作预处理,不进行编译、汇编和链接 -S:编译到汇编语言,不进行汇编和链接 -c:编译、汇编到目标代码,不进行链接 -o &文件&:输出到 &文件& -x &语言&:指定其后输入文件的语言。允许的语言包括 c、c++、assembler 等。 以 -g、-f、-m、-O、-W 或 --param 开头的选项将由 gcc 自动传递给其调用的不同子进程。若要 向这些进程传递其他选项,必须使用 -W&字母& 选项。96 Edited by Foxit Reader Copyright(C) by Foxit Corporation, For Evaluation Only. 第 5 章 编译与调试1.3.4设置输出的文件在默认情况下,gcc 编译出的程序为当前目录下的文件 a.out。-o 参数可以设置输出的目 标文件。例如下面的命令,可以设置将代码编译成可执行程序 do。gcc a.c -o do也可以设置输出目录文件为不同的目录。例如下面的命令,是将目录文件设置成/tmp 目 录下的文件 do。gcc a.c -o /tmp/do输入下面的命令,查看生成的目录文件。 结果如下所示, 在编译程序时生成的目录为/tmp 目录下的文件 do。-rwxrwxr-x 1 root root
13:33 /tmp/do1.3.5查看编译过程参数-v 可以查看程序的编译过程和显示已经调用的库。输入下面的命令,在编译程序时 输出编译过程。gcc -v a.c显示的结果如下所示。使用内建 specs。 目标:i386-redhat-linux 配置为:../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --host=i386-redhat-linux 线程模型:posix gcc 版本 4.1.2
(Red Hat 4.1.2-33) /usr/libexec/gcc/i386-redhat-linux/4.1.2/cc1 -quiet -v a.c -quiet -dumpbase a.c -mtune=generic -auxbase a -version -o /tmp/cc8P7rzb.s 忽 略 不 存 在 的 目 录 “ /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../i386redhat-linux/include” #include &...& 搜索从这里开始: #include &...& 搜索从这里开始: /usr/local/include /usr/lib/gcc/i386-redhat-linux/4.1.2/include /usr/include 搜索列表结束。 GNU C 版本 4.1.2
(Red Hat 4.1.2-33) (i386-redhat-linux) 由 GNU C 版本 4.1.2
(Red Hat 4.1.2-33) 编译。 GGC 准则:--param ggc-min-expand=64 --param ggc-min-heapsize=64394 97 Edited by Foxit Reader Copyright(C) by Foxit Corporation, Linux 系统下 C 程序开发详解For Evaluation Only.Compiler executable checksum: ab322ce5b87a7c6c23d6b31 a.c: In function ‘main’: a.c:16: 警告: ‘main’ 的返回类型不是 ‘int’ as -V -Qy -o /tmp/ccEFPrYh.o /tmp/cc8P7rzb.s GNU assembler version 2.17.50.0.18 (i386-redhat-linux) using BFD version version 2.17.50.0.18-1
/usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 --eh-frame-hdr --build-id -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crt1.o从显示的编译过程可知, 自动加载了系统的默认配置, gcc 调用系统的库函数完成了程序 的编译过程。1.3.6设置编译的语言gcc 可以对多种语言编写的源代码。如果源代码的文件扩展名不是默认的扩展名,gcc 就 无法编译这个程序。可以用-x 选择来设置程序的语言。可以用下面的步骤来练习这一操作。 输入下面的命令,将 C 程序文件复制一份。cp a.c a.u复制出的文件 a.u 是一个 C 程序文件, 但扩展名不是默认的扩展名。 这时输入下面的 命令编译这个程序。gcc a.u显示的结果如下所示,表明文件的格式不能识别。a.u: file not recognized: File format not recognized collect2: ld 返回 1这时,用-x 参数设置编译的语言,命令如下所示。这样就可以正常地编译文件 a.u。gcc -x 'c' a.u需要注意的是,这里的 c 需要用单引号扩起来。当编译扩展名不是.c 的 C 程序时,需要 使用-x 参数。1.3.7-asci 设置 ANSIC 标准ANSIC 是 American National Standards Institute(ANSI:美国标准协会) 出版的 C 语言 标准。使用这种标准的 C 程序可以在各种编译器和系统下运行通过。gcc 可以编译 ANSIC 的 程序,但是 gcc 中的很多标准并不被 ANSIC 所支持。在 gcc 编译程序时,可以用-ansic 来设置 程序使用 ANSIC 标准。例如下面的命令,在设置程序编译时,用 ANSIC 标准进行编译。gcc -asci a.out 5.3.81.3.8g++编译 C++程序gcc 可以编译 C++程序。编译 C 程序和 C++程序时,使用的是不同的命令。编译 C++程98 Edited by Foxit Reader Copyright(C) by Foxit Corporation, For Evaluation Only. 第 5 章 编译与调试序时,使用的命令是 g++。该命令的使用方法与 gcc 是相似的。下面是使用 g++命令编译 C++ 程序的实例。 下面是一个 C++程序的代码,实现与 1.3.1 节程序同样的功能。C++程序的代码与 C 程序 的代码非常相似。#include &iostream& int max(int i,int j ) { if(i&j) { return(i); } else { return(j); } } int main() { int i ,j,k; i=3; j=5; printf(&hello ,Linux.\n&); k=max(i,j); printf(&%d\n&,k); return(0); }输入下面的命令,编译这个 C++程序。g++ 5.2.cpp Co 5.2.out输入下面的命令,对这个程序添加可执行权限。chmod +x 5.2.ou输入下面的命令,运行这个程序,程序的代码如下所示。hello ,Linux. 5从结果可知,这个程序与 1.3.1 节中的程序运行结果是相同的。1.4 编译过程的控制编译过程指的是 gcc 对一个程序进行编译时完成的内部处理和步骤。编译程序时会自动 完成预处理(Preprocessing) 、编译(Compilation) 、汇编(Assembly)和链接(Linking)4 个 步骤。本节将讲解如何对这 4 个步骤进行控制。99 Linux 系统下 C 程序开发详解1.4.1编译过程简介gcc 把一个程序的源文件,编译成一个可执行文件,中间包括很多复杂的过程。可以用图 1-1 来表示编译中 4 个步骤的作用和关系。源文件 源文件使用-E 选项进行预处理 使用-E选项进行预处理预处理文件 预处理文件使用-s 选项汇编 使用-S选项汇编汇编文件 汇编文件-c 选项生成目标文件 -c选项生成目标文件 链接目标文件和库 链接目标文件和库 生成可执行文件 生成可执行文件目标文件 目标文件可执行文件 可执行文件图 1-1gcc 编译源文件到可执行文件的过程在 4 个过程中,每一个操作都完成了不同的功能。编译过程的功能如下所示。 预处理:在预处理阶段,主要完成对源代码中的预编译语句(如宏定义 define 等)和 文件包含进行处理。需要完成的工作是对预编译指令进行替换,把包含文件放置到需 要编译的文件中。完成这些工作后,会生成一个非常完整的 C 程序源文件。 编译:gcc 对预处理以后的文件进行编译,生成以.s 为后缀的汇编语言文件。该汇编语 言文件是编译源代码得到的汇编语言代码,接下来交给汇编过程进行处理。汇编语言 是一种比 C 语言更低级的语言,直接面对硬盘进行操作。程序需要编译成汇编指令以 后再编译成机器代码。 汇编:汇编过程是处理汇编语言的阶段,主要调用汇编处理程序完成将汇编语言汇编 成二进制机器代码的过程。通常来说,汇编过程是将.s 的汇编语言代码文件汇编为.o 的目标文件的过程。所生成的目标文件作为下一步链接过程的输入文件。 链接:链接过程就是将多个汇编生成的目标文件以及引用的库文件进行模块链接生成 一个完整的可执行文件。在链接阶段,所有的目标文件被安排在可执行程序中的适当 的位置。同时,该程序所调用到的库函数也从各自所在的函数库中链接到程序中。经 过了这个过程以后,生成的文件就是可执行的程序。1.4.2控制预处理过程参数-E 可以完成程序的预处理工作而不进行其他的编译工作。下面的命令,可以将本章 编写的程序进行预处理,然后保存到文件 a.cxx 中。gcc -E -o a.cxx a.c输入下面的命令,查看经过预处理以后的 a.cxx 文件。vim a.cxx可以发现, 文件 a.cxx 约有 800 行代码。 程序中默认包含的头文件已经被展开写到到这个100 第5章编译与调试文件中。显示的文件 a.cxx 前几行代码如下所示。可见,在程序编译时,需要调用非常多的头 文件和系统库函数。# # # # # # # # # # # # # # # # # # # # # 1 &a.c& 1 &&built-in&& 1 &&command line&& 1 &a.c& 1 &/usr/include/stdio.h& 1 3 4 28 &/usr/include/stdio.h& 3 4 1 &/usr/include/features.h& 1 3 4 335 &/usr/include/features.h& 3 4 1 &/usr/include/sys/cdefs.h& 1 3 4 360 &/usr/include/sys/cdefs.h& 3 4 1 &/usr/include/bits/wordsize.h& 1 3 4 361 &/usr/include/sys/cdefs.h& 2 3 4 336 &/usr/include/features.h& 2 3 4 359 &/usr/include/features.h& 3 4 1 &/usr/include/gnu/stubs.h& 1 3 4 1 &/usr/include/bits/wordsize.h& 1 3 4 5 &/usr/include/gnu/stubs.h& 2 3 4 1 &/usr/include/gnu/stubs-32.h& 1 3 4 8 &/usr/include/gnu/stubs.h& 2 3 4 360 &/usr/include/features.h& 2 3 4 29 &/usr/include/stdio.h& 2 3 41.4.3生成汇编代码参数-S 可以控制 gcc 在编译 C 程序时只生成相应的汇编程序文件,而不继续执行后面的 编译。下面的命令,可以将本章中的 C 程序编译成一个汇编程序。gcc -S -o a.s a.c输入下面的命令,查看汇编文件 a.s。可以发现这个文件一共有 60 行代码。这些代码是 这个程序的汇编指令。部分汇编程序代码如下所示。.file .text .globl max .type max: pushl movl subl movl cmpl jle .L2 movl movl jmp .L4 .L2: movl &a.c&max, @function %ebp %esp, %ebp $4, %esp 8(%ebp), %eax 12(%ebp), %eax 8(%ebp), %eax %eax, -4(%ebp)12(%ebp), %eax 101 Edited by Foxit Reader Copyright(C) by Foxit Corporation, Linux 系统下 C 程序开发详解For Evaluation Only.movl %eax, -4(%ebp) .L4: movl -4(%ebp), %eax leave ret .size max, .-max .section .rodata .LC0: .string &hello ,Linux.& .LC1: .string &%d\n& .text .globl main .type main, @function main: leal 4(%esp), %ecx andl $-16, %esp ....... popl %ebp leal -4(%ecx), %esp ret .size main, .-main .ident &GCC: (GNU) 4.1.2
(Red Hat 4.1.2-33)& .section .note.GNU-stack,&&,@progbits1.4.4生成目标代码参数-c 可以使得 gcc 在编译程序时只生成目录代码而不生成可执行程序。输入下面的命 令,将本章中的程序编译成目录代码。gcc -c -o a.o a.c输入下面的命令,查看这个目录代码的信息。file a.o显示文件 a.o 的结果如下所示,显示文件 a.o 是一个可重定位的目标代码文件。a.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped1.4.5链接生成可执行文件gcc 可以把上一步骤生成的目录代码文件生成一个可执行文件。在终端中输入下面的命令。gcc a.o -o aa.out这时生成一个可执行文件 aa.out。输入下面的命令查看这个文件的信息。file aa.out显示的结果如下所示,表明这个文件是可在 Linux 系统下运行的程序文件。aa.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped 102 Edited by Foxit Reader Copyright(C) by Foxit Corporation, For Evaluation Only. 第 5 章 编译与调试1.5gdb 调试程序所谓调试,指的是对编好的程序用各种手段进行查错和排错的过程。进行这种查错处理 时,并不仅仅是运行一次程序检查结果,而是对程序的运行过程、程序中的变量进行各种分 析和处理。本节将讲解使用 gdb 进行程序的调试。1.5.1gdb 简介gdb 是一个功能强大的调试工具,可以用来调试 C 程序或 C++程序。在使用这个工具进 行程序调试时,主要使用 gdb 进行下面 5 个方面的操作。 启动程序:在启动程序时,可以设置程序运行环境。 设置断点:断点就是可以在程序设计时暂停程序运行的标记。程序会在断点处停止, 用户便于查看程序的运行情况。这里的断点可以是行数、程序名称或条件表达式。 查看信息:在断点停止后,可以查看程序的运行信息和显示程序变量的值。 分步运行:可以使程序一个语句一个语句的执行,这时可以及时地查看程序的信息。 改变环境:可以在程序运行时改变程序的运行环境和程序变量。1.5.2在程序中加入调试信息为了使用 gdb 进行程序调试,需要在编译程序中加入供 gdb 使用的调试信息。方法是在 编译程序时使用一个-g 参数。在终端中输入下面的命令,在编译程序时加入调试信息。gcc -g -o a.debug a.c这时,编译程序 a.c,生成一个 a.bedug 的可执行程序。这个可执行程序中加入了供调试 所用的信息。1.5.3启动 gdb在调试文件以前,需要启动 gdb。在终端中输入下面的命令。gdb这时,gdb 的启动信息如下所示。这些提示显示了 gdb 的版本和版权信息。GNU gdb Red Hat Linux (6.6-35.fc8rh) Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type &show copying& to see the conditions. There is absolutely no warranty for GDB. Type &show warranty& for details. This GDB was configured as &i386-redhat-linux-gnu&. (gdb)103 Edited by Foxit Reader Copyright(C) by Foxit Corporation, Linux 系统下 C 程序开发详解For Evaluation Only.1.5.4在 gdb 中加载需要调试的程序使用 gdb 调试一个程序之前,需要加载这个程序。加载程序的命令是 file。在(gdb)提示 符后面输入下面的命令加载程序 a.debug。file a.debug命令的运行结果如下所示,显示已经加载了这个文件,并且使用了系统库文件。Reading symbols from /root/c/a.debug...done. Using host libthread_db library &/lib/libthread_db.so.1&.1.5.5在 gdb 中查看代码用 gcc 命令编译程序加入了-g 命令以后, 编译后的 a.debug 程序中加入了断点。 可以用 list 命令显示程序的源代码和断点。下面的步骤是查看加入断点以后的代码。 在(gdb)提示符后面输入下面的命令。list 1这时,gdb 会显示第一个断点以前的代码。显示的代码如下所示。1 2 3 4 5 6 7 8 9 10 (gdb) #include &stdio.h& int max(int i,int j ) { if(i&j) { return(i); } else {这时,按“Enter”键,显示下一个断点以前的代码。结果如下所示。11 12 13 14 15 16 17 18 19 20 (gdb) return(j); } } void main() { int i ,j,k; i=3; j=5; printf(&hello ,Linux.\n&);按“Enter”键,显示下一个断点以前的代码。结果如下所示。21 22 104 k=max(i,j); printf(&%d\n&,k); Edited by Foxit Reader Copyright(C) by Foxit Corporation, For Evaluation Only. 第 5 章 编译与调试23 (gdb)}1.5.6在程序中加入断点程序会运行到断点的位置停止下来,等待用户处理信息或者查看中间变量。如果自动设 置的断点不能满足调试要求, 可以用 break 命令增加程序的断点。 例如需要在程序的第 6 行增 加一个断点,可以输入下面的命令。break 6这时 gdb 显示的结果如下所示。Breakpoint 1 at 0x8048402: file a.c, line 6.输入下面的命令,在程序的第 18 行、19 行、21 行增加断点。break 18 break 19 break 211.5.7查看断点命令 info breakpoint 可以查看程序中设置的断点。输入“info breakpoint”命令,结果如 下所示。显示程序中所有的断点。1 2 3 4 breakpoint breakpoint breakpoint breakpoint keep keep keep keep y y y y 0xxxx in in in in max at a.c:6 main at a.c:18 main at a.c:19 main at a.c:21加上相应的断点编号, 可以查看这一个断点的信息。 例如下面的命令就是查看第二个断点。info breakpoint 2显示的结果如下所示。2 breakpoint keep y 0x in main at a.c:181.5.8运行程序gdb 中的 run 命令可以使这个程序以调试的模式运行。下面的步骤是分步运行程序,对程 序进行调试。 在(ddb)提示符后输入“run”命令,显示的结果如下所示。Starting program: /root/c/a.debug warning: Missing the separate debug info file: /usr/lib/debug/.build-id/ac/2eeb15d6ac4cd64de0cb50838ff6.debug warning: Missing the separate debug info file: /usr/lib/debug/.build-id/ba/4ea426e9410cafb798f25cefad5.debug Breakpoint 2, main () at a.c:18 18 i=3;105 Edited by Foxit Reader Copyright(C) by Foxit Corporation, Linux 系统下 C 程序开发详解For Evaluation Only.结果显示了程序中的异常,并将异常记录到了系统文件中。然后在程序的第二个断 点的位置第 18 行停下。 这时输入“next”命令,程序会在下一行停下,结果如下所示。19 j=5;输入“continue”命令,程序会在下一个断点的位置停下。结果如下所示。Continuing. Breakpoint 3, main () at a.c:19 21 k=max(i,j);输入“continue”命令,程序运行到结束。结果如下所示,表明程序已经运行完毕正 常退出。5 Program exited with code 02.step 命令与 next 命令的作用相似,对程序实现单步运行。不同之处是,在遇上函数 调用时,step 函数可以进行到函数内部。而 next 函数只是一步完成函数的调用 。1.5.9看方法。变量的查看print 命令可以在程序的运行中查看一个变量的值。本节将用下面的步骤来讲解变量的查 输入下面的命令,运行程序。run程序在第一个断点位置停下。显示的结果如下所示。Breakpoint 2, main () at a.c:18 18 i=3;程序进入第 18 行之前停下,并没有对 i 进行赋值。可以用下面的命令来查看 i 的值。print i显示的结果如下所示,表示 i 现在只是一个任意值。$5 = -输入下面的命令,使程序运行一步。step显示的结果如下所示。19 j=5;这时程序在 19 行以前停下,这时输入下面的命令,查看 i 的值。print i这时显示的 i 的结果如下所示。表明 i 已经赋值为 3。106 第5章编译与调试$6 = 3这时输入“step”命令,再次输入“step”命令,显示的结果如下所示。21 k=max(i,j);这时输入“step”命令,会进入到子函数中,结果如下所示。这时,显示了传递给函 数的变量和值。max (i=3, j=5) at a.c:5 5 if(i&j)这时,输入“step”命令,显示的结果如下所示,表明函数会返回变量 j。11 return(j);输入下面的命令,查看 j 的值。print j显示的结果如下所示,表明 j 的值为 5。$7 = 5这时再运行两次“step”命令,显示的结果如下所示。22 printf(&%d\n&,k);这时,输入下面的命令,查看 k 的值。print k显示的结果如下所示,表明 k 的值为 5。$8 = 517 完成了程序的调试运行以后,输入“q”命令,退出 gdb。1.6 程序调试实例本节讲解一个程序调试实例。先编写一个程序,在程序运行时,发现结果与预想结果有 些不同。然后用 gdb 工具进行调试,通过对单步运行和变量的查看,查找出程序的错误。1.6.1编写一个程序本节将编写一个程序,要求程序运行时可以显示下面的结果。1+1=2 2+1=3 2+2=4 3+1=4 3+2=5 3+3=6 4+1=5 4+2=6 4+3=7 4+4=8很明显,这个程序是通过两次循环与一次判断得到的。程序中需要定义三个变量。下面 用这个思路来编写这个程序。 打开一个终端。在终端中输入“vim”命令,打开 VIM。107 Edited by Foxit Reader Copyright(C) by Foxit Corporation, Linux 系统下 C 程序开发详解For Evaluation Only.在 VIM 中按“i”键,进入到插入模式。然后在 VIM 中输入下面的代码。#include &stdio.h& main() { int i,j,k; for(i=1;i&=4;i++) { for(j=1;j&=4;j++); { if(i&=j) { k=i+j; printf(&%d+%d=%d &,i,j,k); } } printf(&\n&); } }在 VIM 中按“Esc”键,返回到普通模式。然后输入下面的命令,保存这个文件。:w /root/c/test.c输入“:q”命令退出 VIM。很容易发现,在第二个循环后1.6.2编译文件本节将对上一节编写的程序进行编译和运行。在运行程序时,会发现程序有错误。 在终端中输入下面的命令,编译这个程序。gcc /root/c/test.c程序可以正常编译通过,输入下面的命令,运行这个程序。/root/c/a.out程序的显示结果是 4 个空行,并没有按照预想的要求输出结果。 输入下面的命令,对这个程序进行编译。在编译加入-g 参数,为 gdb 调试做准备。gcc -g -o test.debug 6.2.c这时,程序可以正常编译通过。输出的文件是 test.debug。这个文件中加入了文件调 试需要的信息。1.6.3程序的调试本节将讲述使用 gdb 对上一节编写的程序进行调试,查找出程序中的错误。 在终端中输入“gdb”命令,进入到 gdb,显示的结果如下所示。GNU gdb Red Hat Linux (6.6-35.fc8rh) Copyright (C) 2006 Free Software Foundation, Inc.108 Edited by Foxit Reader Copyright(C) by Foxit Corporation, For Evaluation Only. 第 5 章 编译与调试GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type &show copying& to see the conditions. There is absolutely no warranty for GDB. Type &show warranty& for details. This GDB was configured as &i386-redhat-linux-gnu&.导入文件。在 gdb 中输入下面的命令。file /root/c/test.debug这时显示的结果如下所示。表明已经成功加载了这个文件。Reading symbols from /root/c/test.debug...(no debugging found)...done. Using host libthread_db library &/lib/libthread_db.so.1&. symbols查看文件。在终端中输入下面的命令。list显示的文件查看结果如下所示。1 #include &stdio.h& 2 3 main() 4 { 5 int i,j,k; 6 for(i=1;i&=4;i++) 7 { 8 for(j=1;j&=4;j++); 9 { 10 if(i&=j) (gdb) 11 { 12 k=i+j; 13 printf(&%d+%d=%d &,i,j,k); 14 } 15 } 16 printf(&\n&); 17 } 18 } (gdb) Line number 19 6.2.c has 18 lines.在程序中加入断点。从显示的代码可知,需要在第 6 行、第 11 行、第 12 行和第 13 行加入断点。在 gdb 中输入下面的命令。break break break break 6 11 12 13gdb 显示的添加断点的结果如下所示。109 Linux 系统下 C 程序开发详解Breakpoint Breakpoint Breakpoint Breakpoint1 2 3 4at at at at0xxxx8048432:file file file file6.2.c, 6.2.c, 6.2.c, 6.2.c,line line line line6. 11. 12. 13.输入下面的命令,运行这个程序。run运行到第一个断点显示的结果如下所示。Breakpoint 1, main () at 6.2.c:6 6 for(i=1;i&=4;i++)输入“step”命令,程序运行一步,结果如下所示。8 for(j=1;j&=4;j++);这说明程序已经进入了 for 循环。这时输入下面命令,查看 i 的值。print i显示的结果如下所示。$2 = 1这时再输入“step”命令,显示的结果如下所示。10 if(i&=j)这时再输入“step”命令,显示的结果如下所示。16 printf(&\n&);这表明,在进行 j 的 for 循环时,没有反复执行循环体。这时再输入“step”命令, 显示的结果如下所示。for(i=1;i&=4;i++)这表明,程序正常的进行了 i 的 for 循环。这是第二次执行 for 循环。17 输入“step”命令,显示的结果如下所示。8 for(j=1;j&=4;j++);18 这表明,程序执行到 for 循环。这时再次输入“step”命令,显示的结果如下所示。10 if(i&=j)19 输入“step”命令,显示的结果如下所示。16 printf(&\n&);20 输入“step”命令,显示的结果如下所示。6 for(i=1;i&=4;i++)21 这说明,程序正常的进行了 i 的 for 循环,但是没有执行 j 的 for 循环。这一定是 j 的110 第5章编译与调试for 循环语句有问题。这时就不难发现 j 的 for 循环后面多了一个分号。 22 输入“q”命令,退出 gdb。1.6.4gdb 帮助的使用gdb 有非常多的命令。输入“help”命令可以显示这些命令的帮助信息。本节将讲解帮助 信息的使用。 在 gdb 输入“help”命令,显示的帮助信息如下所示。List of classes of commands: aliases -- Aliases of other commands breakpoints -- Making program stop at certain points data -- Examining data files -- Specifying and examining files internals -- Maintenance commands obscure -- Obscure features running -- Running the program stack -- Examining the stack status -- Status inquiries support -- Support facilities tracepoints -- Tracing of program execution without stopping the program user-defined -- User-defined commands Type &help& followed by a class name for a list of commands in that class. Type &help all& for the list of all commands. Type &help& followed by command name for full documentation. Type &apropos word& to search for commands related to &word&. Command name abbreviations are allowed if unambiguous.上面的帮助信息显示,输入“help all”会输出所有帮助信息。 在“help”命令后面加上一个命令名称,可以显示这个命令的帮助信息。例如输入 “help file” ,显示的 file 命令帮助信息如下所示。Use FILE as program to be debugged. It is read for its symbols, for getting the contents of pure memory, and it is the program executed when you use the `run' command. If FILE cannot be found as specified, your execution directory path ($PATH) is searched for a command of that name. No arg means to have no executable file and no symbols.1.7gdb 常用命令除了前面讲述的 gdb 命令以外,gdb 还有很多种命令。这些命令可以完成程序调试的各 种功能。其他的常用命令含义如下所示。 backtrace: 显示程序中的当前位置和表示如何到达当前位置的栈跟踪 (同义词: where) 。 breakpoint:在程序中设置一个断点。 cd:改变当前工作目录。 clear:删除刚才停止处的断点。111 Linux 系统下 C 程序开发详解commands:命中断点时,列出将要执行的命令。 continue:从断点开始继续执行。 delete:删除一个断点或监测点,也可与其他命令一起使用。 display:程序停止时显示变量和表达式。 down:下移栈帧,使得另一个函数成为当前函数。 frame:选择下一条 continue 命令的帧。 info:显示与该程序有关的各种信息。 info break:显示当前断点清单,包括到达断点处的次数等。 info files:显示被调试文件的详细信息。 info func:显示所有的函数名称。 info local:显示当函数中的局部变量信息。 info prog:显示被调试程序的执行状态。 info var:显示所有的全局和静态变量名称。 jump:在源程序中的另一点开始运行。 kill:异常终止在 gdb 控制下运行的程序。 list:列出相应于正在执行的程序的源文件内容。 next:执行下一个源程序行,从而执行其整体中的一个函数。 print:显示变量或表达式的值。 pwd:显示当前工作目录。 pype:显示一个数据结构(如一个结构或 C++类)的内容。 quit:退出 gdb。 reverse-search:在源文件中反向搜索正规表达式。 run:执行该程序。 search:在源文件中搜索正规表达式。 set variable:给变量赋值。 signal:将一个信号发送到正在运行的进程。 step:执行下一个源程序行,必要时进入下一个函数。 undisplay display:命令的反命令,不要显示表达式。 until:结束当前循环。 up:上移栈帧,使另一函数成为当前函数。 watch:在程序中设置一个监测点(即数据断点) 。 whatis:显示变量或函数类型。1.8 编译程序常见的错误与问题在编写程序时,无论是逻辑上还是语法上,不可能一次做到完全正确。于是在编译程序 时,就会发生编译错误。本节将讲述程序编译时常见的错误类型与处理方法。112 第5章编译与调试1.8.1逻辑错误与语法错误在编程时,出现的错误可能有逻辑错误和语法错误两种。这两种错误的发生原因和处理 方法是不同的。本节将讲述这两种错误的处理方法。 逻辑错误指的是程序的设计思路发生了错误。这种错误在程序中是致命的,程序可能 正常编译通过,但是结果是错误的。当程序正常运行而结果错误时,一般都是编程的 思路错误。这时,需要重新考虑程序的运算方法与数据处理流程是否正确。 语法错误:语法错误指的是程序的思路正确,但是在书写语句时,发生了语句错误。 这种错误一般是编程时不小心或是对语句的错误理解造成的。在发生语句错误时,程 序一般不能正常编译通过。这时会提示错误的类型和错误的位置,按照这些提示改正 程序的语法错误即可完成错误的修改。1.8.2C 程序中的错误与异常C 程序中的错误,根据严重程序的不同,可以分为异常与警误两类。在编译程序时,这 两种情况对编译的影响是不同的,对错误与异常的处理方式是不同的。 1.什么是异常 异常指的是代码中轻微的错误,这些错误一般不会影响程序的正常运行,但是不完全符 合编程的规范。在编译程序时,会产生一个“警告” ,但是程序会继续编译。下面的程序会使 程序发生异常,在编译时产生一个警告错误。 在除法中,0 作除数。 在开方运算时,对负数开平方。 程序的主函数没有声明类型。 程序的主函数没有返回值。 程序中定义了一个变量,但是没有使用这个变量。 变量的存储发生了溢出。 2.什么是错误 错误指的是程序的语法出现问题,程序编译不能正常完成,产生一个错误信息。这时会 显示错误的类型与位置。根据这些信息可以对程序进行修改。1.8.3编译中的警告提示在编译程序时,如果发生了不严重的异常,会输出一个错告错误,然后完成程序的编译。 例如下面的内容是一个程序在编译时产生的警告。5.1.c: In function 'main': 5.1.c:16: 警告: ‘main’ 的返回类型不是 ‘int’ 5.1.c:18: 警告:被零除这些的含义如下所示。 (1) “In function 'main':”表示发生的异常在 main 函数内。113 Linux 系统下 C 程序开发详解(2) “5.1.c:16:”表示发生异常的文件是 5.1.c,位置是第 16 行。 (3)下面的信息是第 16 行的异常,表明程序的返回类型不正确。‘main’ 的返回类型不是 ‘int’(4)下面的警告信息表明程序的第 18 行有除数为 0 的错误。5.1.c:18: 警告:被零除1.8.4找不到包含文件的错误程序中的包含文件在系统或工程中一定要存在,否则程序编译时会发生致命错误。例如 下面的语句包含了一个不正确的头文件。#include &stdio1.h&编译程序时,会发生错误,错误信息如下所示。5.1.c:2:20: 错误:stdio2.h:没有那个文件或目录1.8.5错误地使用逗号程序中逗号的含义是并列几个内容,形成某种算法或结构。程序中如果错误地使用逗号,会 使程序在编译时发生致命错误。例如下面的代码,是程序中的 if 语句后面有一个错误的逗号。int max(int i,int j ) { if(i&j), { return(i); } else { return(j); } }程序编译时输出的错误信息如下所示。表明 max 函数中逗号前面的表达式有错误,实际 上的错误是多一个逗号。5.1.c: In function ‘max’: 5.1.c:4: 错误:expected expression before ‘,’ token 5.1.c: In function ‘max’:1.8.6括号不匹配错误程序中的引号、单引号、小括号、中括号、大括号等符号必须成对出现。这方面的错误 会使程序发生符号不匹配的错误。发生这种错误后,编译程序往往不能理解代码的含义,也 不能准确显示错误的位置,而是显示表达式错误。例如下面的代码,在最后一行上了一个花 括号。int max(int i,int j ) 114 第5章编译与调试{ if(i&j) { return(i); } else { return(j); }编译程序时,会显示下面的错误信息。5.1.c:22: 错误:expected declaration or statement at end of input1.8.7小括号不匹配错误程序中的小括号一般在一行内成对出现并且相匹配。小括号不匹配时,程序发生致命错 误。例如下面的代码,第一行多了一个右半边括号。if(i&j)) { return(i); } else { return(j); }编程程序时,会发生下面的错误。显示括号前面有错误,并且导致下面的 else 语句也有 错误。5.1.c:4: 错误:expected statement before ‘)’ token 5.1.c:8: 错误:expected expression before ‘else’1.8.8变量类型或结构体声明错误程序中的变量或结构体的名称必须正确,否则程序会发生未声明的错误。例如下面的代 码,用一个不存在的类型来声明一个变量。程序在运行时,会显示出这个变量错误,并且会显示有其他的错误。5.1.c:17: 5.1.c:17: 5.1.c:17: 5.1.c:17: 错误: ‘ch’ 未声明 (在此函数内第一次使用) 错误:(即使在一个函数内多次出现,每个未声明的标识符在其 错误:所在的函数内只报告一次。) 错误:expected ‘;’ before ‘a’1.8.9使用不存在的函数的错误如果程序引用了一个不存在的函数,会使用程序发生严重的错误。例如下面的代码,引 用了一个不存在的函数 add。115 Linux 系统下 C 程序开发详解k=add(i,j);程序显示的错误信息如下所示,表明在 main 函数中的 add 函数没有定义。/tmp/ccYQfDJy.o: In function `main': 5.1.c:(.text+0x61): undefined reference to `add' collect2: ld 返回 15.8.10大小写错误C 程序对代码的大小写是敏感的,不同的大小写代表不同的内容。例如下面的代码,将 小写的“int”错误的写成了“Int” 。I程序显示的错误信息如下所示,表明“Int”类型不存在或未声明。发生这个错误时,会 输出多行错误提示。5.1.c:16: 5.1.c:16: 5.1.c:16: 5.1.c:16: 错误: ‘Int’ 未声明 (在此函数内第一次使用) 错误:(即使在一个函数内多次出现,每个未声明的标识符在其 错误:所在的函数内只报告一次。) 错误:expected ‘;’ before ‘t’1.8.11 数据类型的错误程序中的某些运算,必须针对相应的数据类型,否则这个运算会发生数据类型错误。例 如下面的代码,错误地将两个整型数进行求余运算。float a,b; a= a %程序编译时,输出下面的错误,表明“%”运算符的操作数无效。5.1.c:19: 错误:双目运算符 % 操作数无效1.8.12赋值类型错误任何一个变量,在赋值时必须使用相同的数据类型。例如下面的代码,错误地将一个字 符串赋值给一个字符。 c=&a&;程序编译时的结果如下所示,表明赋值时数据类型错误。5.1.c:19: 警告:赋值时将指针赋给整数,未作类型转换1.8.13循环或判断语句中多加分号分号在程序中的作用是表示一个语句结束。在程序的语句中用一个单独的分号表示一个 空语句。但是在循环或判断结构的后面,一个分号会导致程序的逻辑发生错误。关于这些结 构的使用方法,后面的章节将会详细讲到。下面的程序,在 for 语句的后面,错误的添加了一116 第5章编译与调试个分号,导致程序不能正常地进行循环。#include &stdio.h& main() { int sum, sum=0; for(j=0;j&11;j++); { sum=sum+j; } printf(“%d”,sum); }这个程序的本意是要求出 10 以内的整数和。但是在 for 语句的后面,错误地使用了一个 分号。这时,程序不能正确地进行循环,而是把分号作为一个语句进行循环,所以程序输出 的结果为“11” 。1.9 小结程序的编译和调试是编程的一个重要环节。 本章讲解了 Linux 系统中 C 编程的编译器 gcc 和编译器 gdb 的使用。使用 gcc 时,需要对编译进行各种设置,需要理解 gcc 各项参数的作 用。gdb 的学习重点是 gdb 单步运行程序的理解,通过程序的单步运行发现程序中的问题。117 第2.12章 C 语言基础C 程序的基本概念C 语言有着严格的格式和语法规则,用户需要按照这些要求来编写程序。本章将讲解 C 程序的组成、语句、注释等基本概念。2.1.1C 程序的基本结构C 程序由语句、函数、包含文件等部分组成。本节将以一个简单的 C 程序实例来讲解 C 程序的基本结构问题。这个程序实现两个整数的大小判断功能。 (1)单击“主菜单”|“系统工具”|“终端”命令,打开一个终端。在终端中输入“vim” 命令,打开 VIM。 (2)输入程序。在 VIM 中按“i”键,进入到插入模式,然后在 VIM 中输入下面的代码。 需要注意的是,/* */符号中的内容是注释,程序中不必输入这些注释。#include &stdio.h& int max(int i , int j) { /*包含文件。*//*注释:这是一个自定义函数,实现两个整数的大小比较功能。 */ if(i&=j) return(i); /*返回较大的数。*/ else return(j); } int main() /*主函数。*/ { int i =3; /*定义三个变量,并且赋值。*/ int j =5; t=max(3,5); /*将较大的数赋值给 t。*/ printf(&the big number is %d\n&,t); /*输出文本和结果。*/ }(3)保存文件。输入这些代码以后,按“Esc”键返回到普通模式,输入命令“:w 2.1.c” , 保存这个文件。然后输入命令“:q”退出 VIM。 (4)在终端中输入下面的命令,编译这个程序。gcc Co 2.1.out 2.1.c(5)在终端中输入下面的命令,对这个程序添加可执行权限。 第6章C 语言基础chmod +x 2.1.out(6)输入下面的命令,运行这个程序。./2.1.out(7)程序的运行结果如下所示,显示较大值是 5。the big number is 5由上面的例子可知,C 程序的源代码有下面的特点。 (1)程序一般用小写字母书写。 (2)大多数语句结尾必须要分号作为终止符,表示一个语句结束。同一个语句需要写在 一行上。 (3) 每个程序必须有一个主函数, 主函数用 main()声明, 并且只能有一个主函数。 Linux 在 系统中,main 主函数应该是 int 类型。 (4)每个程序中的自定义函数和主函数,需要用一对大括号括起来。 (5)程序需要使用#include &&语句来包含系统文件,这些系统文件完成系统函数的定义。 (6)一个较完整的程序大致包括下面这些内容。 包含文件:一组#include &*.h&语句。 用户自定义函数:用户已编写的完成特定功能的模块。 主函数:程序自动执行的程序体。 变量定义:定义变量以存储程序中的数据。 数据运算:程序通过运算完成各种逻辑功能。这些运算由各种语句和函数实现的。 注释:注释写在/* */符号之间,不是程序的必需部分,但是可以增强程序的可读性。 需要注意的是,C 程序是严格区分大小写的,程序中同一个字母的和大写小写字母代表 不同的内容。可以将多个语句写在一行上,但是每个语句必须有一个分号结束。例如本节代 码中的 max 函数,也可以写成下面的形式。int max(int i , int j){ if(i&=j) return(i); else return(j);}这段程序也是可以正常执行的,但是不便于程序的阅读与修改。在编程中需要使用正确 的格式,每个语句写在一行上,并且使用正确的注释与缩进,使代码的层次清晰明了。2.1.2所示。C 程序的一般格式通过上一节的例子可知程序的主要部分是主函数和自定义函数。C 程序的一般结构如下程序的包含文件 子函数类型声明 全局变量定义 main 主函数() { 局部变量定义 &程序体& } 119 Linux 系统下 C 程序开发详解自定义函数 1() { 局部变量定义 &程序体& } 自定义函数 2() { 局部变量定义 &程序体& } …… 自定义函数 N() { 局部变量定义 &程序体& }主函数与自定义函数的位置是随意的。自定义函数可以写在主函数的前面或后面,如果 放在后面,需要在程序的前面声明这个函数。2.1.3C 程序中的注释所谓注释,就是在编写程序时对代码的补充说明,不影响程序。任何一个程序员,都不 可能完全记忆所写过的代码,注释的作用是对代码的阅读和理解进行提示。程序在编译时, 会自动去除注释的内容。 在编写程序时,需要正确地书写注释,养成良好的注释习惯。例如下面代码中使用的注 释方法就是常用的注释方法。其中,程序右侧的注释可以按“Tab”键使注释对齐。作者:小明。 内容:变量的输入与输出。 时间: 程序的最前面注释出代码的信息。*/ #include &stdio.h& int main() { scanf(“%d”,&i); printf(“%d”,i) } /*/*包含文件。*/ /*主函数。*/ /*定义一个变量。*/ /*输入一个变量。*/ /*输出变量。*/2.2数据类型数据类型指的是一类数据的集合,是对数据的抽象描述。数据类型的不同决定了所占存 储空间的大小不同。每个变量在使用之前必须定义其数据类型。C 程序有整型(int) 、浮点型 (float) 、字符型(char) 、指针型(*) 、无值型(void)这些常用数据类型。还有结构体(struct) 和联合体(union)两种自定义数据类型。本章将讲解前三种基本的数据类型。120 第6章C 语言基础2.2.1整型(int)整型可以简单理解为整数。与整数不同的是,一个整型变量有一定的字节长度和存储数 据范围。整型变量是有正负的,在定义整型变量时,需要注意变量的正负问题。如表 2.1 所 示是不同长度与正负的整型变量。表 2.1 整型变量类 signed short int signed long int unsigned short int unsigned long int 型 说 明 有符号短整型数。简写为 short 或 int,字长为 2 字节,数的范围是- 有符号长整型数。简写为 long,字长为 4 字节,数的范围是-~ 无符号短整型数。简写为 unsigned int,字长为 2 字节,数的范围是 0~65535 无符号长整型数。简写为 unsigned long,字长为 4 字节,数的范围是 0~例如下面的代码中定义和使用了整型变量。#include &stdio.h& int main() { i=123; j=1234L; k=0x5e; printf(&%d\n&,i); printf(&%ld\n&,j); printf(&%d\n&,k); }/*定义一个 signed short int 变量 i。*/ /*定义一个 signed long int 变量 i。*/ /*定义一个 unsigned short int 变量 i。*/ /*i 赋值为 123。*/ /*j 赋值为 1234。*/ /*k 赋值为十六进制的 5e。*/用下面的命令编译这段代码。gcc 2.2.c然后对编译的程序添加可执行权限。chmod +x a.out输入下面的命令运行这个程序。./a.out程序的运行结果如下所示。123 1234 94在程序中整型变量有以下三种表示方法。 普通十进制数,在程序中除了 0 以外不以 0 开始。如 0,123,-123 等。 八进制数,程序中以 0 开始,如 013,-013 等。 十六进制数,程序中以 0x 或 0X 开始,如 0x1E、0XEF 等。121 Linux 系统下 C 程序开发详解另外, 可在整型常数后添加一个 “L” “l” 或 字母表示该数为长整型数, 如代码中的 j=1234L, 表示 j 为长整型数。2.2.2浮点型(float)浮点型数指的是数值的小数点在存储空间中可以移动,可以用来表示不同精度与大小的 数值。小数、科学计数法的值都是用浮点型变量来存储的。根据所占的存储空间与表示范围 不同,浮点型数可以分为下面的两种。 float 单 精 度 浮 点 数 , 字 长 为 4 个 字 节 共 32 位 二 进 制 数 。 可 表 示 数 的 范 围 是 3.4x10-38E~3.4x10+38E。 double 双精度浮点数,字长为 8 个字节共 64 位二进制数,可表示数的范围是 1.7x10-308E~1.7x10+308E。#include &stdio.h& int main() { m=1.234; n=.1234; printf(&%f\n&,m); printf(&%f\n&,n); }//定义一个 float 变量。 //定义一个 double 变量。 //对 m 赋值。 //输出变量。输入下面的命令,编译这个程序。gcc 2.3.c输入下面的命令,对编译的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。1..123400与整数变量相比,浮点型变量在下面这些方面是不同的。 浮点常数只有十进制一种进制。 绝对值小于 1 的浮点数,其小数点前面的零可以省略。如 0.123 可写为.123。 用默认格式输出浮点数时,都是保留 6 位小数,没有小数的后面加零。2.2.3字符型(char)字符型变量在计算机中以 ASCII 码方式表示,长度为一个字节。各种字母、符号都可以 是一个字符型的变量。数字 0~9 也可以存储为一个字符型变量。当数字存储为字符型变量时,122 第6章C 语言基础保存的是这个数字的 ASCII 码的值,与 int 型保存数值是不相同的。 注意:ASCII 码是计算机内部的字符编码集。所有的信息都是以 ASCII 码的形式 保存在计算机中的。一个字符变量对应 ASCII 码表中的一个字符。 在程序中,字符可直接用单引号引起来。如‘A’‘b’‘9’‘@’都是一个字符型变量, 、 、 、 也可以用这一字符的 ASCII 码值来表示一个字符,例如 87 表示大写字母‘W’ 。 除了屏幕上可以直接显示的字符以外,还有一些字符是用来进行格式控制,并不能直接 显示在屏幕上。例如\n 表示一个换行符,ASCII 码为八进制 010,而不是一个字符\加一个字 符 n。C 程序中常用的转义字符如表 2.2 所示。表 2.2 转义字符字 \a \b \f \n \r \t \v \\ \? \' \& \0 符 响铃(BEL) 退格(BS) 换页(FF) 换行(LF) 回车(CR) 水平制表(HT) 垂直制表(VT) 反斜杠 问号字符 单引号字符 双引号字符 空字符(NULL) 名 称 007 008 012 010 013 009 011 092 063 039 034 000 ASCII 码在程序中字符变量需要用 char 来定义。 例如下面的代码, 是字符变量的定义与使用实例。#include &stdio.h& int main() { m='a'; n='!'; t='\n'; printf(&%c\n&,m); printf(&%c\n&,n); printf(&%c\n&,t); }/*输出 a 再输出一个换行。*/ /*输出!再输出一个换行。*/ /*输出的 t 为一个换行.*/输入下面的命令,编译这个程序。gcc 2.4.c输入下面的命令,对编译的程序添加可执行权限。123 Linux 系统下 C 程序开发详解chmod +x a.out输入下面的命令,运行这个程序。结果如下所示。a !2.2.4变量名变量名只能由字母、数字、下画线组成,且头一个字符只能是字母或下画线,变量名之 间不能有空格或其他字符。这样的变量才是合法的。例如下面这些变量名是合法变量名。ab _a a2 AAA A_B __AB下面这些变量名,不符合变量名的要求,是不合法的。3a a.b $ab a b /*不能以数字开头。*/ /*变量名之间不能有特殊符号。*/ /*不能以特殊符号开头。*/ /*变量名中不能有空格。*/不合法的变量名会引起程序的错误。除了这些变量名以外,系统关键字也不能作为变量 名。例如下面这些词是系统关键字,作为变量名时会引发错误。int printf float exit2.2.5字符 NULLNULL 是一个特殊的字符,在 C 程序中有着重要的功能。NULL 在 ASCII 码表中是 0, 在程序中表示空字符,或者没有这个数值。在定义变量时,如果要对变量赋值为空值,可将 这个变量赋值为 NULL,如下面代码所示。int a=NULL; char b=NULL; float c=NULL;注意:ASCII 码是计算机字符的内部编码,每一个字符对应 ASCII 码表中的一个 编号。详细介绍见本书 8.5.3 节中的内容。2.3变量的赋值与输出变量的赋值指的是对一个变量指定一个值,这个值可以用于程序的运算。变量的输出指 的是将变量的值显示在屏幕上,一般情况下,程序执行完毕以后需要将结果输出。2.3.1变量的赋值赋值符号是等号,含义是将等号右边的值或表达式结果传递给等号左边的变量。例如下 面的代码就是定义变量和赋值的例子。#include &stdio.h& int main() 124 第6章C 语言基础{ int i =0,j=1,k=123; char a,b,c; s=j; a='a'; b=a; c=b; }/*定义三个整型变量,定义变量同时赋值。*/ /*定义三个字符型变量。*/ /*将变量 j 的值赋值给 s。*/ /*将字符 a 赋值给变量 a。*/ /*把变量 a 的值赋值给 b。*/ /*把变量 b 的值赋值给 c。*/2.3.2printf 函数输出变量在前面的代码中,都是使用 printf()函数进行变量的输出。printf()函数称为格式输出函数, 功能是按用户指定的格式,把指定的数据显示到屏幕上。 Printf()函数是一个标准库函数,它的函数原型在头文件“stdio.h”中,该函数的使用方法 如下所示。printf(“格式控制字符串”,输出变量列表)输出变量列表中给出了各个输出变量。格式控制字符串用于指定输出格式,由格式字符 串和非格式字符串两种组成。 格式字符串是以%开头的字符串,在%后面跟有各种格式字符。不同格式字符串的作 用是说明输出数据的类型、形式、长度、小数位数等。如“%d”表示按十进制整型输 出, “%ld”表示按十进制长整型输出。这些格式字符串与输出变量列表中的变量一一 对应。 格式字符串以外的内容是非格式字符串,在输出时按照原有的字符直接输出。 格式字符串的内容和意义如下所示。 %c:输出单个字符,参数为该字符的 ASCII 码。 %d:以十进制形式输出带符号整数(正数不输出符号) 。 %e 或%E:以指数形式输出单、双精度实数,默认保留 6 位小数。 %f:以小数形式输出单或双精度实数,默认保留 6 位小数。 %g 或%G:以%f 或%e 中较短的输出宽度输出单、双精度实数。如果指数小于-4 或大 于等于默认精度, 则使用%e 或%E 格式输出。 否则用%f 格式输出, 省略末尾多余的 0。 %i:以十进制形式输出带符号整数,同%d。 %o:以八进制形式输出无符号整数(不输出前缀 0) 。 %s:输出字符串,参数为 char 指针,显示字符串中所有的字符。 %u:以十进制形式输出无符号整数。 %x 或%X:以十六进制形式输出无符号整数,%x 表示输出小写,%X 表示输出大写。 例如下面的代码是 printf()函数格式化输出的实例。#include &stdio.h& int main() { int i=117; char a='m';/*定义整型变量 i=117。*/ /*定义字符定量 a。*/ 125 Linux 系统下 C 程序开发详解float m=123.1234567 ; printf(&%d\n&,i); printf(&%c\n&,i); printf(&%o\n&,i); printf(&%x\n&,i); printf(&%X\n&,i); printf(&%c\n&,a); printf(&%d\n&,a); printf(&%o\n&,a); printf(&%x\n&,a); printf(&%X\n&,a); printf(&%f\n&,m); printf(&%e\n&,m); }/*定义一个浮点型变量。*/ /*以整型输出 i。*/ /*以字符型输出 i。*/ /*以八进制输出 i。*/ /*以十六进制小写输出 i。*/ /*以十六进制大写输出 i。*/ /*以字符型输出 a。*/ /*以整型输出 a。*/ /*以八进制输出 a。*/ /*以十六进制小写输出 a。*/ /*以十六进制大写输出 a。*/ /*以浮点型输出 f。*/ /*以科学计数法输出 m。*/输入下面的命令,编译这一个文件。gcc 2.5.c输入下面的命令,对编译的文件添加可执行权限。chmod +x a.out输入下面的命令运行这一个程序。./a.out程序的运行结果如下所示。117 u 165 75 75 m 109 155 6d 6D 123..23123e+02从程序的运行结果可知,字符是以 ASCII 码的形式保存的,相对应一相整型的。当整型 数 117 以整型输出时是 117,而以字符型输出时是 u。2.3.3下所示。scanf 函数从键盘读入变量scanf()函数是键盘格式化输入函数,可以从键盘输入读取信息,这个函数的使用方法如scanf(&&格式化字符串&&,&地址表&);这里的格式化字符串与上一节中的格式化字符串含义是相同的。当有多个要读取的变量126 第6章C 语言基础时,可以用空格或其他符号隔开,在键盘输入时也相应地输入这些符号。例如下面的代码就 是使用 scanf()函数从键盘读取输入,再输出变量的例子。#include &stdio.h& int main() { int i , printf(&please enter a char:\n&); scanf(&%c&,&m); printf(&the char is %c.\n&,m); printf(&please enter a number:\n&); scanf(&%d&,&i); printf(&please enter another number:\n&); scanf(&%d&,&j); printf(&%d + %d = %d \n&,i ,j ,i+j); }/*定义程序中的变量。*/ /*提示信息。*/ /*输入一个字母。*/ /*输出一个字母。*/ /*输入一个整数。*//*输出两个整数和这两个整数的和。*/输入下面的命令,编译这一个文件。gcc 2.9.c输入下面的命令,对编译的文件添加可执行权限。chmod +x a.out输入下面的命令运行这一个程序。./a.out运行程序时,光标等待输入内容。输入内容以后,按“Enter”键继续向后执行。程序的 运行结果如下所示。please enter a char:这时从键盘输入一个字母 a,然后按“Enter”键。这时程序的结果如下所示。the char is a. please enter a number:这时输入一个数字 5,然后按“Enter”键,程序显示结果如下所示。please enter another number:这时输入一个数字 3,然后按“Enter”键,程序显示结果如下所示。5 + 3 = 82.4运算符运算符指的是用来完成各种运算的符号。C 程序中有着丰富的运算符,很多运算都是通 过运算符来实现的。本节将讲解常用运算符的操作。127 Linux 系统下 C 程序开发详解2.4.1算术运算符算术运算符是指进行简单数学计算的运算符,是程序中最简单最常用的运算符。算术运 算符的种类和使用方法如表 2.3 所示。表 2.3 算术运算符运 算 符 + * / ++ -+= -= *= /= % 加号和减号 乘号和除号 自加,在原来的值上面加 1 自减,在原来的值上面减 1 相加赋值和相减赋值,对原有值加或者减去一个值 相乘赋值或相除赋值,对原有值乘或除一个值 求余运算,第一个数除以第二个数求余数 含 义例如下面的代码是使用这些运算符进行简单的数学运算的例子。#include &stdio.h& int main() { int i =3 ,j = 7 ,k; float m=2.5 , n = 3.8,t; k= 3+7; printf(&%d\n&,k); k*=5; printf(&%d\n&,k); k=k%j; printf(&%d\n&,k); printf(&%d\n&,i+j); t= 3.8/2.5; printf(&%f\n&,t); printf(&%f\n&,m*n+5); }/*定义变量和赋值。*/ /*做加法运算。*/ /* *=运算,表示把 k 乘以 5 再赋值给 k。*/ /*求余运算。*/ /*可以直接在参数列表中进行运算。*/ /*以浮点数的形式输出。*/ /*直接在参数列表中进行运算。*/输入下面的命令编译这个程序。gcc 2.2.c输入下面的命令,对编译后的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。10 50 1 10 128 第6章C 语言基础1..500000对于++、--运算的使用,将符号写在前面和写在后面的含义是不同的。++写在后面时, 表示取得值然后再自加,写在前面时,表示先自加再取值。例如下面的代码,就是自加和自 减运算符使用的例子。#include &stdio.h& int main() { int i =3 ,j = 7; i++; printf(&%d\n&,i); printf(&%d\n&,i++); printf(&%d\n&,i++); printf(&%d\n&,++i); printf(&%d\n&,--i); printf(&%d\n&,i++ + j++); }/*i 的值为 4。*/ /*输出 i 的值为 4,然后自加得 5。*/ /*输出 i 的值为 5,然后自加得 6。*/ /*自加得 7,输出结果得 7。*/ /*自减得 6,输出结果为 6。*/ /*先取值相加得 13,然后分别自加。*/输入下面的命令编译这个程序。gcc 2.7.c输入下面的命令,对编译后的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。4 4 5 7 6 132.4.2关系运算符所谓关系运算符,指的是比较两个数的大小关系或相等关系的符号。关系运算符的种类 与使用方法如表 2.4 所示。表 2.4 关系运算符运 算 符 & &= & &= 大于。第一个数大于第二个数时返回真 大于等于。第一个数大于等于第二个数时返回真 小于。第一个数小于第二个数时返回真 小于等于。第一个数小于等于第二个数时返回真 含 义129 Linux 系统下 C 程序开发详解== !=等于。两个数相等时返回真 不等于。两个数不相等时返回真关系运算符的返回值是真(true)和假(false)的概念。在 C 语言中,真值 true 可以是不 为 0 的任何值。而假值 false 则为 0。使用关系运算符,若表达式为真则返回 1,否则表达式 为假则返回值为 0。例如下面的关系运算符的使用。6&5 10=9 返回 1 返回 02.4.3逻辑运算符所谓逻辑运算符,指的是用形式逻辑原则来建立数值间关系运算的符号。逻辑运算有与、 或、非三种,使用方法如表 2.5 所示。表 2.5 逻辑运算符运 算 符 && || ! 含 逻辑与 逻辑或 逻辑非 义 所有的条件为真则返回真 只要有一个条件为真则返回真 真值取非返回假,假值取反返回真 返 回 值例如下面的代码,是关系运算符与逻辑运算符的使用实例。#include &stdio.h& int main() { int i=5,j=3; printf(&%d\n&,i&j); printf(&%d\n&,i==j); printf(&%d\n&,i&j); printf(&%d\n&,i!=j); printf(&%d\n&,i!=j); printf(&%d\n&,(i&j)&&(i&=j)); printf(&%d\n&,(i&j)&&(i&j)); printf(&%d\n&,1&&1&&1); printf(&%d\n&,1&&0&&1); printf(&%d\n&,0||0||1); printf(&%d\n&,!0); }/*判断 i 是否大于等于 j,返回真,输出 1。*/ /*判断 i 是否等于 j,返回假,输出 0。*//*判断两个条件,然后做逻辑与运算。*/ /*所有条件为真则返回真。*/ /*有一个条件为假则返回假。*/ /*有一个条件为真则返回真。*/ /*非假返回真。*/输入下面的命令编译这个程序。gcc 2.8.c输入下面的命令,对编译后的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out130 第6章C 语言基础程序的运行结果如下所示。1 0 0 1 1 1 0 1 0 1 12.5小结本章讲解了数据类型、变量赋值与输出、常用运算符等知识。这些知识是编写 C 程序的 基础,通过这些知识的学习,可以理解 C 程序的一些概念,编写简单的 C 语言程序。在本章 的学习中,数据类型与运算符的使用是重点,需要详细地理解数据类型的概念和输出方式。131 第3.13章 C 程序的常用语句流程控制语句在程序中,需要对语句的执行进行分支选择,或者重复执行某些语句,这些实现程序逻 辑功能或多次循环执行运算的语句就是流程控制语句。流程控制语句有条件语句与循环语句 两种。条件语句实现程序的逻辑功能,循环语句实现程序的重复执行功能。3.1.1 if 条件语句if 条件语句的作用,是对一个条件进行判断。如果判断的结果为真,则执行条件后面的 语句。如果执行判断的结果为假,则跳过后面的语句。最基本的 if 条件语句结构如下所示。if(条件) { 条件成立时需要执行的内容; }这种条件语句的执行流程如图 3-1 所示。if条件判断真执行语句假结束图 3-1if 条件语句的流程图如果 if 后面的判断结果只有两种情况,即第一种条件不成立时一定是第二种情况。则条 件语句可以使用 if else 结构,这种条件语句的使用方法如下所示。if(条件) { 第7章C 程序的常用语句条件成立时需要执行的内容; } else { 条件不成立时执行的内容; }这种条件语句中,先判断 if 的条件内容。如果返回的结果为真,则执行 if 后面的语句, 否则就执行 else 后面的语句。程序的执行流程如图 3-2 所示。进入if条件判断否真 执行if后面的语句 执行else后面的语句 结束 结束图 3-2if else 条件语句的流程图例如下面的实例是实现从键盘输入一个整数,用 if 语句判断这个整数是奇数还是偶数, 然后输出判断结果。 单击“主菜单”|“系统工具”|“终端”命令,打开一个终端。在终端中输入“vim” 命令,然后按“Enter”键打开 VIM。 在 VIM 中按“i”键进入到插入模式,然后输入下面的代码。#include &stdio.h& int main() { int i , printf(&please input a number:\n&); scanf(&%d&,&i); j =i %2; if(j==0) { printf(&%d oushu.\n&,i); } else { printf(&%d jishu.\n&,i); } }/*定义两个整型变量。*/ /*提示输入。*/ /*从键盘读取一个整数。*/ /*输入的数对 2 求余数。*/ /*如果余数为 0 则输入是偶数。*//*否则输出为奇数。*/在 VIM 按“Esc”键返回到普通模式。然后输入“:w 7.1.c”命令,将这个文件保存133 Linux 系统下 C 程序开发详解到主目录下的文件 7.1.c。然后输入“:q”命令退出 VIM。 编译程序,在终端中输入下面的命令,然后按“Enter”键。gcc 7.1.c对编译生成的程序添加可执行权限,在终端中输入下面的命令,然后按“Enter”键。chmod +x a.out在终端中输入下面的命令,运行这个程序。./a.out程序在运行时,显示下面的信息,提示输入一个整数。please input a number:在键盘上输入一个整数 12,程序的显示结果如下所示。12 oushu3.1.2 if 语句的嵌套所谓 if 语句嵌套,指的是在一个 if 语句的执行内容中再进行 if 判断语句。当判断情况很 多时,需要使用 if 语句嵌套。下面就是一种简单的 if 语句嵌套。if(条件) { if(条件) { 执行的内容; } else { 执行的内容; } } else { if(条件) { 执行的内容; } else { 执行的内容; } } }在这个嵌套结构中,先进行一次条件判断,然后判断的结果进行另外一次条件判断。这 种结构的执行流程可用图 3-3 来表示。134 第7章C 程序的常用语句进入 if条件判断 真 if条件判断 真 执行if后面的语句 执行else后面的语句 结束 否 if条件判断 真 执行if后面的语句 执行else后面的语句 否 否图 3-3if 嵌套语句的流程假设对考试分数进行 A、B、C、D 评级,85 分以上为 A,70~85 分为 B,60~70 分为 C, 60 分以下为 D。要实现这个功能,需要对分数进行多次 if 判断,可以使用如图 3-3 所示的 if 嵌套结构。程序的代码如下所示。#include &stdio.h& int main() { /*定义一个变量。*/ printf(&please input a number:\n&); /*提示输入信息。*/ scanf(&%d&,&i); /*从读取一个变量。*/ if(i&=70) /*第一次 if 判断。*/ { if(i&=85) /*85 分以上的输出 A。*/ { printf(&A&); } else /*其他的分数就是 70~85 分的,输出 B。*/ { printf(&B&); } } else /*这里其他的就是 70 分以下的。*/ { if(i&=60) /*60~70 分的输出 C。*/ { printf(&C&); } else { printf(&D&); /*其他的一定是 60 分以下的,输出 D。*/ } } 135 Linux 系统下 C 程序开发详解}编译并运行这个程序,在提示后面输入一个数字 77,然后按“Enter”键。程序的 if 嵌套 结构会判断分数的级别,显示的结果是 B。3.1.3switch 选择执行语句当 if 语句的判断情况很多时,可以使用 switch 选择执行语句。在这种结构中,switch 对 一个条件进行判断,然后分别列出可能的结果,每个结果执行不同的语句。switch 语句的使 用方法如下所示。switch(条件) { case 结果 1 : 执行内容 1; case 结果 2 : 执行内容 2; …… case 结果 n : 执行内容 }这种结构的执行流程如图 3-4 所示。进入 switch条件判断 条件3 条件1 执行内容3 执行内容1 B 结束 执行内容2 执行内容n 条件2 条件n图 3-4switch 语句的流程图下面是一个 switch 语句的使用实例。 scanf()函数从键盘读取一个数字, 用 如果数字在 0~6 之间,输出对应的星期一到星期六,否则提示错误。 单击“主菜单”|“系统工具”|“终端”命令,打开一个终端。在终端中输入“vim” 命令,然后按“Enter”键打开 VIM。 在 VIM 中按“i”键进入到插入模式,然后输入下面的代码。#include &stdio.h& int main() { 136 第7章C 程序的常用语句 printf(&please input a number:\n&); scanf(&%d&,&i); if(i&0 || i &6) 提示信息。*/ { printf(&input error.\n&); } else { switch(i) { case 0: printf(&Sunday\n&) ; case 1: printf(&Monday\n&) ; case 2: printf(&Tuesday\n&) ; case 3: printf(&Wednesday\n&) ; case 4: printf(&Thursday\n&) ; case 5: printf(&Friday\n&) ; case 6: printf(&Saturday\n&) ; } } }/*定义一个变量。*/ /*提示输入信息。*/ /*从键盘读取一个数字。*/ /*如果这个数字小于 0 或大于 6 则输出错误/*否则执行判断。*/ /*执行判断。*//*7 个判断条件和结果。*/在 VIM 按“Esc”键返回到普通模式。然后输入“:w 7.2.c”命令,将这个文件保存 到主目录下的文件 7.1.c。然后输入“:q”命令退出 VIM。 输入下面的命令,编译这个程序。gcc 7.2.c输入下面的命令,对编译后的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序运行时,会输出下面的提示信息要求输入一个数字。please input a number:这时输入一个数字 5,然后按“Enter”键。程序显示的结果如下所示。Friday137 Linux 系统下 C 程序开发详解3.1.4for 循环语句循环语句指的是使用循环结构使一个程序过程多次执行。for 循环语句是最常用的循环语 句,这种结构的用法如下所示。for(起始条件;循环条件;循环变量变化) { 循环体执行的内容; }例如下面的程序,实现从 1~6 之间 6 个数字的输出。1. 2. 3. 4. 5. 6. 7. 8. 9. #include &stdio.h& int main() { for(i=1;i&=6;i++) { printf(&%d\n&,i); } }/*定义循环变量。*/ /*开始 for 循环*/ /*循环体,输出 i 的值和换行。*/输入下面的命令,编译这个程序。gcc 7.4.c输入下面的命令,对编译后的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。1 2 3 4 5 6这个程序的执行流程可用图 3-5 来表示。 根据图 3-5 可知,for 循环是用反复判断循环条件、执行循环体、改变循环变量的方法来 实现程序的循环流程的。上述代码的执行过程如下所示。 第 4 行定义一个循环变量 i。138 第7章C 程序的常用语句循环初始条件 进入循环i=1;循环条件 i&=6; 循环变量改变 i++; 成立 执行循环体 结束循环 不成立图 3-5for 循环的程序流程第 5 行,先执行循环初始条件,对 i 赋值为 1。 进行第一次条件判断 i 是否小于等于 6。判断的结果为真则执行下面的循环体,为假 时则跳出循环。 循环体执行完毕以后,进入循环条件改变运算 i++。然后再次进行条件判断,从而完 成程序的循环。 当 i 等于 7 时,条件判断不成立,这时跳出循环,结束程序。 需要注意的是, 循环变量的改变和条件判断要确保 for 循环可以中断, 否则就会构成死循 环。死循环指的是循环语句无法达到停止条件,永远执行下去,可能引起计算机的死机。下 面是一个 for 循环实例,求出 100 以内的整数和。#include &stdio.h& int main() { int sum = 0; for(i=0;i&=100;i++) { sum =sum + } printf(&sum = %d&,sum); }/*定义循环变量。*/ /*定义求和的结果。*/ /*循环语句。*/ /*循环体。*/ /*输出结果。*/输入下面的命令,编译这个程序。gcc 7.5.c输入下面的命令,对编译的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out139 Linux 系统下 C 程序开发详解程序会从 1 到 100 进行循环,每次循环时,与 sum 相加的结果赋值给 sum,这样就得到 了 100 以内整数的和。结果如下所示。sum = 5050需要注意的是,定义求和结果 sum 时,需要对变量 sum 赋初值 0,这样可以与后面的数 相加而结果不变。进行求积运算时,需要对变量赋初值 1。3.1.5for 循环的嵌套在 for 循环结构的循环体中,再次执行一个循环语句,这种结构就是 for 循环嵌套。在循 环嵌套结构中, 每执行一次外层循环, 就会多次执行内层循环。 循环嵌套的结构如下所示。 forfor(起始条件;循环条件;循环变量变化) { for(起始条件;循环条件;循环变量变化) { 循环体; } }这种结构的执行流程如图 3-6 所示。外循环初始条件 循环条件 外层循环变量改变 成立 内循环初始条件 不成立 执行下次外循环 循环条件 成立 执行循环体 循环变量改变结束循环图 3-6for 循环的程序流程例如下面是一个循环嵌套结构实例,输出一个星号图案。在外层循环中,实现个数和行 数的计数,在内层循环中实现星号的输出。#include &stdio.h& int main() { int i ,j; for(i=1;i&7;i++) { 140/*定义程序中的变量。*/ /*外层循环。*/ 第7章C 程序的常用语句for(j=1;j&=i;j++) { printf(&* &); } printf(&\n&); } }/*内层循环。*/ /*输出星号和一个空格。*/ /*输出一个换行。*/输出下面的命令,编译这个程序。gcc 7.6.c输入下面的命令,对编译的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。* * * * * * * * * * ** * * * * * * * * *3.1.6for 循环应用实例:输出九九乘法口诀表乘法口诀表需要用 for 循环嵌套来实现, 一个循环变量作被乘数, 另一个循环变量作乘数。 在输出结果时,需要用一个 if 判断来实现排列。每一行的循环需要输出一个换行。程序的代 码如下所示。#include &stdio.h& int main() { int i,j; for(i=1;i&=9;i++) { for(j=1;j&=i;j++) { if (j&=i) { printf(&%d*%d=%d &,j ,i , i*j ); } } printf(&\n&); } }/*定义两个循环变量。*/ /*外层循环。*/ /*内层循环。*/ /*判断 i 与 j 的大小,实现排列。*/ /*输出乘法式。*//*输出一个换行。*/输出下面的命令,编译这个程序。141 Linux 系统下 C 程序开发详解gcc 7.7.c输入下面的命令,对编译的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7 1*8=8 1*9=9 2*2=4 2*3=6 2*4=8 2*5=10 2*6=12 2*7=14 2*8=16 2*9=183*3=9 3*4=12 3*5=15 3*6=18 3*7=21 3*8=24 3*9=274*4=16 4*5=20 4*6=24 4*7=28 4*8=32 4*9=365*5=25 5*6=30 5*7=35 5*8=40 5*9=456*6=36 6*7=42 7*7=49 6*8=48 7*8=56 8*8=64 6*9=54 7*9=63 8*9=72 9*9=81在这个程序中,需要注意乘法式的输出,下面是代码中的输出语句。printf(&%d*%d=%d &,j ,i , i*j );在输出格式字符串中,三个%d 分别对应输出列表中的 j、i、i*j。而字符*、=与空格按照 原来的格式输出,这就实现了乘法式的输出。3.1.7 while 循环语句while 语句是另一种循环语句,可以实现与 for 循环一样的功能。while 循环的使用方法如 下所示。while(循环条件) { 循环体语句; 循环条件改变; }在 while 循环结构中, 先判断循环的条件是否成立。 如果条件成立则执行循环体中的语句, 然后继续判断条件是否成立。如果条件不成立则跳出循环,执行后面的语句。while 循环的执 行流程可用图 3-7 来表示。142 第7章C 程序的常用语句进入 条件判断 条件成立 执行循环体 不成立结束循环图 3-7while 循环的执行流程图因为 while 循环直接进入循环, 而没有对循环变量赋值, 所以需要在执行循环以前对循环 变量赋值。在循环体中也需要更改循环变量的值。下面的实例是用 while 循环求出 1 到 100 之间的整数和。#include &stdio.h& void main() { int i, i=0; sum=0; while(i&=100) { sum=sum+i; i++; } printf(&sum = %d \n&,sum); }/*定义程序的变量。*/ /*变量赋初值。*/ /*求和变量赋初值。*/ /*进入 while 循环。*/ /*sum 与 i 求和赋值给 sum。*/ /*i 自加。*/ /*输出结果。*/输出下面的命令,编译这个程序。gcc 7.8.c输入下面的命令,对编译的程序添加可执行权限。chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。sum = 5050在这个程序中需要注意下面两个问题。 (1)需要对循环变量与求和变量赋初值。while 循环在进入循环时没有赋值语句,如果在 循环以外没有对循环变量赋初值,则程序会发生异常。 (2)在循环体中一定要改变循环变量的值,否则 while 循环的条件就始终成立,无法跳出143 Linux 系统下 C 程序开发详解循环。这种情况可能造成计算机死机。3.1.8 do while 循环语句do while 循环与 while 循环相似, 实现程序对循环体的多次执行。 while 循环不同的是, 与 do while 循环先执行一次循环体,然后判断条件是否成立。如果条件成立则执行下一次循环, 否则结束循环。这种循环结构的使用方法如下所示。do { 循环体; } while(循环条件);do while 循环的程序流程如图 3-8 所示。进入 执行循环体 条件成立 条件判断 条件不成立 结束循环图 3-8do while 循环的执行流程图与 while 循环一样,do while 循环在进入循环以前,需要对循环变量赋初值。在循环体中 需要更改循环变量的值。下面是一个使用 do while 循环的实例,可以完成 0 到 100 之间偶数 的求和。#include &stdio.h& void main() { int i, i=0; sum=0; do { sum=sum+i; i=i+2; }while(i&=100); printf(&sum = %d \n&,sum); }/*定义两个程序变量。*/ /*循环变量赋初值。*/ /*循环结果赋初值。*/ /*开始 do 循环。*/ /*求和。*/ /*变量加 2,使 i 是 100 以内的偶数。*/ /*判断条件。*/输出下面的命令,编译这个程序。gcc 7.9.c输入下面的命令,对编译的程序添加可执行权限。144 第7章C 程序的常用语句chmod +x a.out输入下面的命令,运行这个程序。./a.out程序的运行结果如下所示。sum = 25503.1.9转移控制语句:continue有时需要在判断或循环的程序执行过程中实现程序流程的强制转移,这就需要使用转移 控制语句。C 程序中有 continue, break 和 return。 在循环语句中,continue 语句可以中断循环体后面语句的执行,直接进入下一次循环。下 面是一个在 for 循环中使用 continue 语句的实例,实现 100 以内偶数的求和。在程序中,用对 变量 i 除 2 求余判断结果的方法,来判断是不是一个偶数。如果不是偶数,则用 continue 语句 进行下一次循环。#include &stdio.h& void main() { int i, sum=0; for(i=0;i&=100;i++) { if(i%2==1) { } sum = sum +i; } printf(&sum = %d \n&,sum); }/*定义程序的变量。*/ /*对求和变量赋初值为 0。*/ /*for 语句实现 1 到 100 的循环。*/ /*判断是否为奇数。*/ /*如是奇数则跳过下面的语句,进入下一次循环。*/ /*sum 与 i 求和的结果赋值给 sum。*/ /*输出结果。*/输出下面的命令,编译这个程序。gcc 7

我要回帖

更多关于 http wwww.4567.tv 的文章

 

随机推荐