[Linux] Linux C编程一站式学习 Part.1
C语言入门
程序基本概念
- 程序和编程语言
- C语言--(编译器)--汇编语言--(汇编器)--机器语言(目标代码 / 可执行代码)
- 可移植 / 平台无关:平台指计算机体系结构或操作系统,或二者的组合。不同的平台有不同的指令集,可识别不同的机器指令格式
- 直接用某种计算机的汇编或机器指令写出来的程序只能在这种计算机上运行,各种计算机上都有C编译器可以把C程序编译成计算机自己的机器指令
- 编译 / 解释语言:解释语言不需生成目标代码,由解释器一行行地,边翻译边执行
- 自然语言和形式语言
- 自然语言是人类讲的语言,如汉语、英语、法语
- 形式语言是为了特定应用认为设计的语言,如数学符号、分子式、编程语言
- 形式语言有严格的语法(Syntax)规则
- 语法规则有关于符号(Token)和结构(Structure)的规则所组成
- 符号:事先定义好的运算符,关于符号的规则为词法(Lexical)规则
- 结构:符号的排列方式,关于结构的规则为语法(Grammar)规则
- 解析(Parse):分析句子结构的过程
- 自然语言与形式语言的区别
- 歧义性(Ambiguity):形式语言的设计要求清晰、毫无歧义,每个语句都有明确含义而不管上下文如何
- 冗余性(Redundancy):自然语言为消除歧义引入冗余,形式语言极少冗余
- 与字面意思的一致性:自然语言充满隐喻(Metaphor),形式语言字面意思就是真实意思
- 阅读形式语言(包括计算机程序)的建议
- 形式语言远比自然语言紧凑,要多花时间阅读
- 结构很重要,不要从上到下或从左到右地读,而应在大脑里解析,识别Token,分解结构
- 关注细节,如拼写错误和符号错误等
- 程序调试
- Bug的分类
- 编译时错误:语法错误导致编译失败
- 运行时错误:编译器检查不出,但运行时出错导致程序崩溃(注意区分编译时和运行时两个概念)
- 逻辑错误和语义错误:编译和运行都很顺利,但没有实现预计结果
- 编程建议
- 编程=调试:编程就是逐步调试直到获得期望结果
- 总是从一个能正确运行的小规模程序开始,每做一步小的改动就立即调试
- Bug的分类
常量、变量和表达式
- 定义、赋值、初始化
- 定义:分配一块内存空间,并给它命名,如 int hour;
- 赋值:把一个值存储在内存空间中,如 hour = 11;
- 初始化:定义 + 赋值,如 int hour = 11;
- 初始化是一种特殊的变量定义语句,而不是赋值语句
- 变量名除了用在等号左边表示赋值外,其他情况都表示把它的值取出来,替换在那里
- 表达式(Expression)由运算符(Operator)和操作数(Operand)组成
- 赋值语句是表达式的一种
- 运算符有优先级(Precedence),如不希望按默认的优先级运算需加括号(Parenthesis)
- C语言规定等号运算符的结果就是等号左边被赋予的那个值
- 常量可以赋值给变量,也可以和变量、运算符一起组成表达式
- 最简单的表达式由单个常量或变量组成
- 任何表达式都有一个值,表达式可以加 ; 号构成表达式语句
简单函数
- sin是函数(Function),sin(pi/2)是函数调用(Function Call),pi/2是参数(Argument)
- 函数调用sin(pi/2)也是表达式,由函数调用运算符() 和两个操作数组成,表达式的值为函数返回值(Return Value)
- C语言的函数可以有副作用(Side Effect),这是它和数学函数在概念上的根本区别
- 如表达式a=b,返回值是a的值,副作用是a的值被改变
- 很多时候我们关心函数的副作用,而非返回值,如printf(),其返回值是实际打印的字符数,而非打印内容
- 对于完全利用Side Effect的函数,可将其返回值定义为void
- 函数原型(Prototype):函数名 + 参数类型和个数 + 返回值类型
- 函数要先声明后使用
- 定义变量时可以把同类型的变量列在一起,但定义函数参数时不可以,如void print_time(int hour, minute){}; 是错误的写法
- 形参(Parameter)相当于函数中定义的变量,调用函数传递参数的过程相当于定义形参变量并用实参(Argument)的值来初始化
- 函数提供了一个接口(Interface),调用函数就是使用这个接口,使用的前提是必须和接口保持一致
- 能用函数传参代替的就不要用全局变量
- 全局变量只能用常量表达式初始化,而局部变量可以用任意类型的表达式初始化
- C语言规定全局变量的初始值保存在编译生成的目标代码中,所以必须编译时就能计算出来,如全局变量pi的初始化语句double pi = 3.14 + 0.0016;是合法的,而double pi = acos(-1.0);是不合法的
- 若全局变量在定义时不初始化,则初始值是0(或"\0"或"0.0"等)
- 局部变量在定义时不初始化,则初始值不确定,故局部变量在使用前一定要先赋值
- 局部变量的的存储空间在每次函数调用时分配,函数返回时释放
分支语句
- 局部变量的的存储空间在每次进入语句块时分配,退出语句块时释放
- 把语句封装成函数的步骤:把语句放在函数体中,把变量改成函数的参数
- else总是和它上面最近的一个if配对
- case后面跟的必须是常量表达式,类似全局变量,必须在编译时计算出值
深入理解函数
- return语句
- 函数返回值相当于定义一个和函数返回值类型相同的临时变量,并用return后面的表达式来初始化
- 函数的返回值不是左值,不能给它赋值
- 写带有return语句的函数时,要小心检查所有的代码路径(Code Path),在任何条件下都执行不到的代码称为Dead Code
- 增量式(Incremental)开发
- 尽可能复用(Reuse)以前写的代码,避免写重复的代码。封装就是为了复用
- 递归
- 如果定义一个概念需要用到这个概念本身,那么它的定义就是递归的
- 相信你正在写的递归函数是正确的,并调用它,然后在此基础上写完递归函数,那么它就是正确的
- 不要忘记写Base Case
- 递归和循环是等价的
循环语句
- 递归和循环两种思路的区别:递归靠递推关系(如n!=n*(n-1)!),循环是公式展开(如n!=n*(n-1)*(n-2)...3*2*1)。展开公式更容易理解,但当公式过于复杂无法展开时,递推就更直观些
- 递归在整个过程中会分配和释放很多变量,但所有变量都只在初始化时赋值,没有任何变量的值发生过改变,而循环是通过对几个变量多次赋值来达到目的
- 递归的思路称为函数式编程(Functional Programming),循环的思路称为命令式编程(Imperative Programming)
- 递归描述要做什么(Declarative),循环描述具体一步一步怎么做(Imperative)
- 函数式编程的“函数”类似于数学函数的概念,是没有Side Effect的,Imperative方式对变量的多次修改会导致问题,如影响代码的线程安全,故应以一种“一致”的方式进行多次赋值
- do/while语句要在while语句后加分号
- ++i:传入参数,返回值(参数+1),Side Effect:变量i的值+1
- i++:传入参数,返回值(参数),Side Effect:变量i的值+1
- goto:无条件跳转,只能跳到同一个函数的某个标号处,而不能跳到别的函数里
- goto语句只用于在函数末尾处理出错,函数中任何地方出现了错误条件都可立即跳到末尾,处理完后函数返回
结构体
- 数据抽象:类似“提取公因式”,ab+ac=a(b+c),左边如果a变了,两个因子都要修改,右边就只要改一个因子
- 组合使得系统可以任意复杂,而抽象使得系统的复杂度是可控的,任何改动都只局限在某一层,而不会波及整个系统
- All problems in computer science can be solved by another level of indirection(abstraction)--Butler Lampson
- 通过一个复数存储表示抽象层把complex_struct结构体的存储格式和上层的复数运算函数隔开
- 枚举类型:用于让结构体接收不同类型的输入,如 enum coordinate_type { RECTANGULAR, POLAR };struct complex_struct { enum coordinate_type t; double a, b; };,通过定义数据类型标识,使得直角坐标和极坐标的数据都可以适配到complex_struct 结构体中
数组
- 数组(Array)是一种复合数据类型,由一系列相同类型的元素(Element)组成
- 使用数组下标不能超过数组的长度范围,C编译器不检查数组越界错误
- 数组和结构体的不同:数组不能相互赋值,不能作为函数的参数或返回值
- 数组名做右值使用时,自动转换成指向数组首元素的指针
- 使用C标准库得到的随机数其实是伪随机数(Pseudorandom),只不过看起来很随机,且每次运行的结果是一样的
- 通过其他方式得到一个不确定的数作为Seed,然后在此基础上生成伪随机数,如 srand(time(NULL));(当前时间剧1970年1月1日00:00:00的秒数)
- 使用rand()生成伪随机数,头文件stdlib.h,返回值是0和RAND_MAX之间的整数,RAND_MAX是头文件中定义的常量,若想将随机数限定在某个范围内可用%处理,如int x = rand() % 10;(0~9内的随机数)
- define在预处理阶段处理,可避免硬编码(Hard coding)(类似抽象,避免一个地方的改动波及大的范围)
字符串
- 字符串可看做是一个数组,元素是字符型的,末尾有一个字符“\0”表示字符串结束。字符串是只读的,不允许修改
- 做右值使用时,自动转换成指向首元素的指针
- 初始化字符串时,可不指定数组长度,而让编译器自动计算
- 数据驱动的编程(Data-driven Programming):编程最重要的是选择正确的数据结构来组织信息,控制流程和算法尚在其次,只要数据结构选的正确,代码自然容易理解和维护
编码风格
- 代码主要是为了写给人看的,只是顺便也能用机器执行而已
- Linux内核的CodingStyle
- 用缩进(Tab)体现语句块的层次关系
- if/else、while、for等可以带语句块的语句,语句块的 { 和 } 应该和关键字写在一起,用空格隔开,而不是单独占一行
- 函数定义的 { 和 } 单独占一行
- switch和语句块里的case、default对齐写
- 代码中每个逻辑段落之间用一个空行分隔开
- 函数内根据相关性分组,用空行分隔
- 注释
- 整个源文件的顶部注释:文件名、作者、版本历史
- 函数注释:函数功能、参数、返回值、错误码
- 语句注释:写在语句上侧
- 代码简短注释:写在代码右侧,和代码间至少用一个空格隔开,一个源文件中所有的右侧注释最好上下对齐
- 函数内的注释要尽可能少,只说明代码能做什么,而不是怎样做的(只要代码清晰,怎样做是一目了然的,否则就是代码可读性很差)
- 复杂的结构体、宏定义和变量定义需要注释
- 变量
- 清晰明了,可用完整单词和大家易于理解的缩写
- 变量、函数类型采用全小写加下划线,常量(宏定义和枚举)采用全大写加下划线
- 慎用匈牙利命名法,不用汉语拼音
- 函数
- 一个函数只做一件事
- 函数内部缩进层次不多于4层
- 函数不要写的太长
- 执行函数就是执行一个动作,函数名通常应包含动词
- 局部变量不要太多
- indent工具:可把代码格式化为某种风格
gdb
- 调试步骤:分析现象->假设错误原因->产生新的现象验证假设
- 单步执行和跟踪函数调用
- 断点(Breakpoint)
- 观察点(Watchpoint):不知道某一存储单元在哪里被改动的
- 段错误:如果某个函数中发生访问越界,很可能并不立即产生段错误,而在函数返回时产生段错误
排序与查找
- 循环正确性的判断方法 Loop Invariant
- 第一次执行循环体前判断条件为真
- 如果“第N-1次循环后判断条件为真”这个前提成立,则可证明第N次循环后判断条件仍为真
- 所有循环结束后判断条件为真,则该算法正确
栈与队列
- 一个问题中数据的存储、访问方式决定了解决问题可以采用什么样的算法,设计算法就要同时设计相应的数据结构来支持这种算法
- 堆栈--后进先出--深度优先搜索(DFS)--回溯(Backtrack)
- 队列--先进先出(FIFO)--广度优先搜索(BFS)
参考
http://docs.linuxtone.org/ebooks/C&CPP/c/
[Linux] Linux C编程一站式学习 Part.1的更多相关文章
- Linux网络编程一站式学习
提要 学过非常多遍计算机网络,依旧不会网络编程. 看完这篇文章之后就不会是这样了. 环境:Ubuntu14.04 64bit 何为Socket 是基于TCP/IP的网络应用编程中使用的有关数据通信的概 ...
- 有关于《Linux C编程一站式学习》(备份)
Linux C编程一站式学习 -- PDF版本,共37章: Linux C编程一站式学习 -- 在线版,来自灰狐: Linux C编程一站式学习 -- 在线版,来自亚嵌教育: Linux C一站式学习 ...
- gdb笔记 ---《Linux.C编程一站式学习》
gdb笔记 ---<Linux.C编程一站式学习> 单步执行和跟踪函数调用 函数调试实例 #include <stdio.h> int add_range(int low, i ...
- [Linux] Linux C编程一站式学习 Part.3
Linux系统编程 文件与I/O C标准I/O库函数与Unbuffered I/O函数 C标准I/O库函数printf().putchar().fputs(),会在用户空间开辟I/O缓冲区 系统函数o ...
- Liunx+C编程一站式学习
Liunx+C编程一站式学习这本书有什么特点?面向什么样的读者?这本书最初是为某培训班的嵌入式系统Linux工程师就业班课程量身定做的教材之一.该课程是为期四个月的全日制职业培训,要求学员毕业时具备非 ...
- [Linux] Linux C编程一站式学习 Part.2
C语言本质 计算机中数的表示 浮点数:符号位+指数部分(2的多少次方)+尾数部分(小数点后的数字) 用偏移的指数(Biased Exponent)表示负指数 正规化(Normalize):尾数部分最高 ...
- Linux C 编程一站式学习
个人认为这是一个挺不错的从C语言到Linux系统开发的教程,这本是两个网上的文档. 其中 一本是<How To Think Like A Computer Scientist: Learning ...
- Linux C编程一站式学习读书笔记——socket编程
前言 研一的时候写过socket网络编程,研二这一年已经在用php写api都快把之前的基础知识忘干净了,这里回顾一下,主要也是项目里用到了,最近博客好杂乱啊,不过确实是到了关键时刻,各种复习加巩固准备 ...
- Linux C编程一站式学习
http://docs.linuxtone.org/ebooks/C&CPP/c/ 很全面的介绍
随机推荐
- 分享15个实用VSCode插件,快来收藏吧!
Visual Studio Code 是由微软开发的一款免费.跨平台的文本编辑器.它有卓越的性能和丰富的功能.VSCode 也有一个扩展和主题市场,为了帮助大家挑选出值得下载的插件,我们针对性的收集了 ...
- 使用KeepAlived来实现高可用的DR模型
环境 VMware 16 CentOS8 相关软件 keepalived ipvsadm httpd 准备工作 准备四个节点,如上图,Node01 ~ Node04, 本文默认你会在VMWare上安装 ...
- 面试题-你听过TCP Fast Open (TFO/TCP快速打开)吗?能解释一下吗?
TCP Fast Open (TFO/TCP快速打开) TCP快速打开(TCP Fast Open,TFO)是什么? TCP快速打开(TCP Fast Open,TFO)是对TCP的一种简化握手手续的 ...
- DAOS 分布式异步对象存储|数据平面
DAOS 通过两个紧密集成的平面进行运转.数据平面处理繁重的运输操作,而控制平面负责进程编排和存储管理,简化数据平面的操作. 模块接口 I/O 引擎支持一个模块接口,该接口允许按需加载服务器端代码.每 ...
- matplotlib安装问题解决
p.p1 { margin: 0; font: 11px Menlo; color: rgba(0, 0, 0, 1) } span.s1 { font-variant-ligatures: no-c ...
- 结对作业-stage_1
教学班 罗杰.任建班周五3.4节 gitlab项目地址 Here it is. 成员 周远航(3004) 李辰洋(3477) 结对编程体验 感受 在前期设计时,两人合作可以收集更多资料,提供更多想法, ...
- JavaWeb 补充(Filter&Listener)
目录 Filter:过滤器 Listener:监听器 Filter:过滤器 1. 概念: * 生活中的过滤器:净水器,空气净化器,土匪. * web中的过滤器:当访问服务器的资源时,过 ...
- Markdown排版介绍
如何排版章节 Markdown: 大标题 ========== 小标题 ---------- # 一级标题 ## 二级标题 ### 三级标题 #### 四级标题 例如 三级 和四级 发布后的效果: 三 ...
- 【CTF】XCTF Misc 心仪的公司 & 就在其中 writeup
前言 这两题都是Misc中数据包的题目,一直觉得对数据包比较陌生,不知道怎么处理. 这里放两道题的wp,第一题strings命令秒杀觉得非常优秀,另外一题有涉及RSA加密与解密(本文不具体讨论RSA非 ...
- 【CTF】图片隐写术 · 盲水印
前言 盲水印同样是CTF Misc中极小的一个知识点,刚刚做到一题涉及到这个考点的题目. 感觉还挺有意思的,就顺便去了解了下盲水印技术. 数字水印 数字水印(Digital Watermark)一种应 ...