没有合适的资源?快使用搜索试试~ 我知道了~
首页Linux C编程入门指南:语言与调试详解
Linux C编程入门指南:语言与调试详解
5星 · 超过95%的资源 需积分: 9 2 下载量 141 浏览量
更新于2024-07-19
收藏 8.76MB PDF 举报
"《Linux C编程一站式学习》是一本针对C语言编程初学者和有一定经验但希望深化理解的读者编写的教材。全书围绕Linux平台展开,旨在通过两个相互交织的线索——语法讲解和程序设计原理,帮助读者全面掌握C语言。第一部分侧重于基础,包括程序的基本概念、常量、变量、表达式、函数等,从自然语言到C语言标准,逐步引导读者理解程序工作原理,例如通过HelloWorld示例、数学函数和自定义函数的讲解,使读者掌握变量声明、赋值和参数传递。 第二部分则深入剖析了C语言的分支语句(如if、switch)、循环语句(while、do-while、for)、函数的递归与嵌套以及数据结构如结构体和数组。作者还特别关注编码风格,强调良好的代码组织和注释的重要性,推荐使用indent工具进行格式化。此外,书中还介绍了如何使用gdb调试器进行单步执行、设置断点和观察点,以及基本的算法如排序和查找,以及数据结构中的栈与队列。 这本书特别适合初次接触C语言的学习者,作为入门教材,有助于构建坚实的编程基础。同时,对于已有一定编程经验但想进一步了解操作系统底层原理的读者,这本书也能提供一个查漏补缺和深化理解的平台。C语言标准的详细解析使得它也适合用作C语言语法的参考书籍。每一章节都配有习题,便于读者通过实践巩固所学知识。《Linux C编程一站式学习》是一本内容丰富、实用性强的C语言教程。"
资源详情
资源推荐
4
#! /bin/sh
VAR=1
VAR=$(($VAR+1))
echo $VAR
定义 Shell 变量 VAR 的初始值是 1,然后自增 1,然后打印 VAR 的值。用 Shell 程序/bin/sh 解释执行
这个脚本,结果如下:
$ /bin/sh script.sh
2
这里的/bin/sh 称为解释器(Interpreter),它把脚本中的每一行当作一条命令解释执行,而不需要先生
成包含机器指令的可执行文件再执行。如果把脚本中的这三行当作三条命令直接敲到 Shell 提示符下,也
能得到同样的结果:
$ VAR=1
$ VAR=$(($VAR+1))
$ echo $VAR
2
图 1.2. 解释执行的过程
编程语言仍在发展演化。以上介绍的机器语言称为第一代语言(1GL,1st Generation Programming
Language),汇编语言称为第二代语言(2GL,2nd Generation Programming Language),C、C++、Java、
Python 等可以称为第三代语言(3GL,3rd Generation Programming Language)。目前已经有了 4GL(4th
Generation Programming Language)和 5GL(5th Generation Programming Language)的概念。3GL 的编程
语言虽然是用语句编程而不直接用指令编程,但语句也分为输入、输出、基本运算、测试分支和循环等几
种,和指令有直接的对应关系。而 4GL 以后的编程语言更多是描述要做什么(Declarative)而不描述具体
一步一步怎么做(Imperative),具体一步一步怎么做完全由编译器或解释器决定,例如 SQL 语言
(SQL,Structured Query Language,结构化查询语言)就是这样的例子。
习题
1、解释执行的语言相比编译执行的语言有什么优缺点?
5
这是我们的第一个思考题。本书的思考题通常要求读者系统地总结当前小节的知识,结合以前的知
识,并经过一定的推理,然后作答。本书强调的是基本概念,读者应该抓住概念的定义和概念之间的关系
来总结,比如本节介绍了很多概念:程序由语句或指令组成,计算机只能执行低级语言中的指令(汇编语
言的指令要先转成机器码才能执行),高级语言要执行就必须先翻译成低级语言,翻译的方法有两种--
编译和解释,虽然有这样的不便,但高级语言有一个好处是平台无关性。什么是平台?一种平台,就是一
种体系结构,就是一种指令集,就是一种机器语言,这些都可看作是一一对应的,上文并没有用“一一对
应”这个词,但读者应该能推理出这个结论,而高级语言和它们不是一一对应的,因此高级语言是平台无
关的,概念之间像这样的数量对应关系尤其重要。那么编译和解释的过程有哪些不同?主要的不同在于什
么时候翻译和什么时候执行。
现在回答这个思考题,根据编译和解释的不同原理,你能否在执行效率和平台无关性等方面做一下比
较?
希望读者掌握以概念为中心的阅读思考习惯,每读一节就总结一套概念之间的关系图画在书上空白
处。如果读到后面某一节看到一个讲过的概念,但是记不清在哪一节讲过了,没关系,书后的索引可以帮
你找到它是在哪一节定义的。
2. 自然语言和形式语言
自然语言(Natural Language)就是人类讲的语言,比如汉语、英语和法语。这类语言不是人为设计
(虽然有人试图强加一些规则)而是自然进化的。形式语言(Formal Language)是为了特定应用而人为设
计的语言。例如数学家用的数字和运算符号、化学家用的分子式等。编程语言也是一种形式语言,是专门
设计用来表达计算过程的形式语言。
形式语言有严格的语法(Syntax)规则,例如,3+3=6 是一个语法正确的数学等式,而 3=+6$则不
是,H
2
O 是一个正确的分子式,而
2
Zz 则不是。语法规则是由符号(Token)和结构(Structure)的规则所
组成的。Token 的概念相当于自然语言中的单词和标点、数学式中的数和运算符、化学分子式中的元素名
和数字,例如 3=+6$的问题之一在于$不是一个合法的数也不是一个事先定义好的运算符,而
2
Zz 的问题之
一在于没有一种元素的缩写是 Zz。结构是指 Token 的排列方式,3=+6$还有一个结构上的错误,虽然加号
和等号都是合法的运算符,但是不能在等号之后紧跟加号,而
2
Zz 的另一个问题在于分子式中必须把下标
写在化学元素名称之后而不是前面。关于 Token 的规则称为词法(Lexical)规则,而关于结构的规则称为
语法(Grammar)规则
[1]
。
当阅读一个自然语言的句子或者一种形式语言的语句时,你不仅要搞清楚每个词(Token)是什么意
思,而且必须搞清楚整个句子的结构是什么样的(在自然语言中你只是没有意识到,但确实这样做了,尤
其是在读外语时你肯定也意识到了)。这个分析句子结构的过程称为解析(Parse)。例如,当你听到“The
other shoe fell.”这个句子时,你理解 the other shoe 是主语而 fell 是谓语动词,一旦解析完成,你就搞懂了句
子的意思,如果知道 shoe 是什么东西,fall 意味着什么,这句话是在什么上下文(Context)中说的,你还
能理解这个句子主要暗示的内容,这些都属于语义(Semantic)的范畴。
虽然形式语言和自然语言有很多共同之处,包括 Token、结构和语义,但是也有很多不一样的地方。
6
歧义性(Ambiguity)
自然语言充满歧义,人们通过上下文的线索和自己的常识来解决这个问题。形式语言的设计
要求是清晰的、毫无歧义的,这意味着每个语句都必须有确切的含义而不管上下文如何。
冗余性(Redundancy)
为了消除歧义减少误解,自然语言引入了相当多的冗余。结果是自然语言经常说得啰里啰
嗦,而形式语言则更加紧凑,极少有冗余。
与字面意思的一致性
自然语言充斥着成语和隐喻(Metaphor),我在某种场合下说“The other shoe fell”,可能并
不是说谁的鞋掉了。而形式语言中字面(Literal)意思基本上就是真实意思,也会有一些例外,例
如下一章要讲的 C 语言转义序列,但即使有例外也会明确规定哪些字面意思不是真实意思,它们
所表示的真实意思又是什么。
说自然语言长大的人(实际上没有人例外),往往有一个适应形式语言的困难过程。某种意义上,形
式语言和自然语言之间的不同正像诗歌和说明文的区别,当然,前者之间的区别比后者更明显:
诗歌
词语的发音和意思一样重要,全诗作为一个整体创造出一种效果或者表达一种感情。歧义和
非字面意思不仅是常见的而且是刻意使用的。
说明文
词语的字面意思显得更重要,并且结构能传达更多的信息。诗歌只能看一个整体,而说明文
更适合逐字句分析,但仍然充满歧义。
程序
计算机程序是毫无歧义的,字面和本意高度一致,能够完全通过对 Token 和结构的分析加以
理解。
现在给出一些关于阅读程序(包括其它形式语言)的建议。首先请记住形式语言远比自然语言紧凑,
所以要多花点时间来读。其次,结构很重要,从上到下从左到右读往往不是一个好办法,而应该学会在大
脑里解析:识别 Token,分解结构。最后,请记住细节的影响,诸如拼写错误和标点错误这些在自然语言
中可以忽略的小毛病会把形式语言搞得面目全非。
[1]
很不幸,Syntax 和 Grammar 通常都翻译成“语法”,这让初学者非常混乱,Syntax 的含义其实包含
了 Lexical 和 Grammar 的规则,还包含一部分语义的规则,例如在 C 程序中变量应先声明后使用。即使在
英文的文献中 Syntax 和 Grammar 也常混用,在有些文献中 Syntax 的含义不包括 Lexical 规则,只要注意上
下文就不会误解。另外,本书在翻译容易引起混淆的时候通常直接用英文名称,例如 Token 没有十分好的
翻译,直接用英文名称。
7
3. 程序的调试
编程是一件复杂的工作,因为是人做的事情,所以难免经常出错。据说有这样一个典故:早期的计算
机体积都很大,有一次一台计算机不能正常工作,工程师们找了半天原因最后发现是一只臭虫钻进计算机
中造成的。从此以后,程序中的错误被叫做臭虫(Bug),而找到这些 Bug 并加以纠正的过程就叫做调试
(Debug)。有时候调试是一件非常复杂的工作,要求程序员概念明确、逻辑清晰、性格沉稳,还需要一
点运气。调试的技能我们在后续的学习中慢慢培养,但首先我们要区分清楚程序中的 Bug 分为哪几类。
编译时错误
编译器只能翻译语法正确的程序,否则将导致编译失败,无法生成可执行文件。对于自然语
言来说,一点语法错误不是很严重的问题,因为我们仍然可以读懂句子。而编译器就没那么宽容
了,只要有哪怕一个很小的语法错误,编译器就会输出一条错误提示信息然后罢工,你就得不到
你想要的结果。虽然大部分情况下编译器给出的错误提示信息就是你出错的代码行,但也有个别
时候编译器给出的错误提示信息帮助不大,甚至会误导你。在开始学习编程的前几个星期,你可
能会花大量的时间来纠正语法错误。等到有了一些经验之后,还是会犯这样的错误,不过会少得
多,而且你能更快地发现错误原因。等到经验更丰富之后你就会觉得,语法错误是最简单最低级
的错误,编译器的错误提示也就那么几种,即使错误提示是有误导的也能够立刻找出真正的错误
原因是什么。相比下面两种错误,语法错误解决起来要容易得多。
运行时错误
编译器检查不出这类错误,仍然可以生成可执行文件,但在运行时会出错而导致程序崩溃。
对于我们接下来的几章将编写的简单程序来说,运行时错误很少见,到了后面的章节你会遇到越
来越多的运行时错误。读者在以后的学习中要时刻注意区分编译时和运行时(Run-time)这两个
概念,不仅在调试时需要区分这两个概念,在学习 C 语言的很多语法时都需要区分这两个概念,
有些事情在编译时做,有些事情则在运行时做。
逻辑错误和语义错误
第三类错误是逻辑错误和语义错误。如果程序里有逻辑错误,编译和运行都会很顺利,看上
去也不产生任何错误信息,但是程序没有干它该干的事情,而是干了别的事情。当然不管怎么
样,计算机只会按你写的程序去做,问题在于你写的程序不是你真正想要的,这意味着程序的意
思(即语义)是错的。找到逻辑错误在哪需要十分清醒的头脑,要通过观察程序的输出回过头来
判断它到底在做什么。
通过本书你将掌握的最重要的技巧之一就是调试。调试的过程可能会让你感到一些沮丧,但调试也是
编程中最需要动脑的、最有挑战和乐趣的部分。从某种角度看调试就像侦探工作,根据掌握的线索来推断
是什么原因和过程导致了你所看到的结果。调试也像是一门实验科学,每次想到哪里可能有错,就修改程
序然后再试一次。如果假设是对的,就能得到预期的正确结果,就可以接着调试下一个 Bug,一步一步逼
近正确的程序;如果假设错误,只好另外再找思路再做假设。“当你把不可能的全部剔除,剩下的——即
使看起来再怎么不可能——就一定是事实。”(即使你没看过福尔摩斯也该看过柯南吧)。
8
也有一种观点认为,编程和调试是一回事,编程的过程就是逐步调试直到获得期望的结果为止。你应
该总是从一个能正确运行的小规模程序开始,每做一步小的改动就立刻进行调试,这样的好处是总有一个
正确的程序做参考:如果正确就继续编程,如果不正确,那么一定是刚才的小改动出了问题。例如,Linux
操作系统包含了成千上万行代码,但它也不是一开始就规划好了内存管理、设备管理、文件系统、网络等
等大的模块,一开始它仅仅是 Linus Torvalds 用来琢磨 Intel 80386 芯片而写的小程序。据 Larry Greenfield
说,“Linus 的早期工程之一是编写一个交替打印 AAAA 和 BBBB 的程序,这玩意儿后来进化成了
Linux。”(引自 The Linux User's Guide Beta1 版)在后面的章节中会给出更多关于调试和编程实践的建
议。
4. 第一个程序
通常一本教编程的书中第一个例子都是打印“Hello, World.”,这个传统源自[K&R],用 C 语言写这个
程序可以这样写:
例 1.1. 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
$ ./main
Hello, world.
虽然这只是一个很小的程序,但我们目前暂时还不具备相关的知识来完全理解这个程序,比如程序的
第一行,还有程序主体的 int main(void){...return 0;}结构,这些部分我们暂时不详细解释,读者现在只需要
把它们看成是每个程序按惯例必须要写的部分(Boilerplate)。但要注意 main 是一个特殊的名字,C 程序
总是从 main 里面的第一条语句开始执行的,在这个程序中是指 printf 这条语句。
剩余710页未读,继续阅读
瑟寒凌风
- 粉丝: 338
- 资源: 17
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 多模态联合稀疏表示在视频目标跟踪中的应用
- Kubernetes资源管控与Gardener开源软件实践解析
- MPI集群监控与负载平衡策略
- 自动化PHP安全漏洞检测:静态代码分析与数据流方法
- 青苔数据CEO程永:技术生态与阿里云开放创新
- 制造业转型: HyperX引领企业上云策略
- 赵维五分享:航空工业电子采购上云实战与运维策略
- 单片机控制的LED点阵显示屏设计及其实现
- 驻云科技李俊涛:AI驱动的云上服务新趋势与挑战
- 6LoWPAN物联网边界路由器:设计与实现
- 猩便利工程师仲小玉:Terraform云资源管理最佳实践与团队协作
- 类差分度改进的互信息特征选择提升文本分类性能
- VERITAS与阿里云合作的混合云转型与数据保护方案
- 云制造中的生产线仿真模型设计与虚拟化研究
- 汪洋在PostgresChina2018分享:高可用 PostgreSQL 工具与架构设计
- 2018 PostgresChina大会:阿里云时空引擎Ganos在PostgreSQL中的创新应用与多模型存储
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功