该笔记是在Visual Studio Code1.67.1上运行
摘自C语言程序设计现代方法第二版(修订版)
第2章 C语言基本概念
| # 格式简要介绍 |
| #include<stdio.h> |
| int main(void) |
| { |
| printf("这是第一个示例: " ); |
| return 0; |
| } |
| |
| 按行释义: |
| #include<stdio.h> 是必不可少的,它"包含"了C语言标准的输入/输出的相关信息 |
| |
| 程序可执行代码都在main函数中,这个函数代表"主"程序,C程序中允许有多个函数,但只有main函数是必须有的,main函数前面的int表明该函数将返回一个整数值,圆括号中的void表明main函数没有参数 |
| |
| main函数中的第一行代码是用来显示期望信息的,printf函数来自标准输入/输出库,可以产生完美的格式化输出,\n告诉printf函数执行完消息显示后要进行换行操作 |
| |
| return 0; 表明程序终止时会向操作系统返回值0,return 也可以写成 return x + 3、return x + 7... |
| return 0有2个作用: |
| 1、使main函数终止(即结束程序) |
| 2、指出main函数的返回值是0 |
| 注:如main函数的末尾没有return语句程序依然能终止,但许多编译器会产生一条告警,告警意为函数应该返回一个整数却没有这么做 |
| |
| |
| |
| # 编译和链接 |
| 编译器通常要求带上文件的扩展名.c, 源代码写完后需要把程序转化为机器可执行的形式,对C程序来说转化通常包含下列3步 |
| 1、预处理 -- 首先程序会被交给预处理器(preprocessor)。预处理器执行以#开头的命令(通常称为指令)。预处理器有点类似于编辑器,它可以给程序添加内容,也可以修改程序 |
| 2、编译 -- 修改后的程序现在可以进人编译器(compiler) 了。编译器会把程序翻译成机器指令(即目标代码)。然而,这样的程序还是不可以运行的。 |
| 3、链接 -- 链接器(linker)把由编译器产生的目标代码和所需的其他斯加代码整合在起,这样才最终产生了完全可执行的程序。这些附加代码包括程序中用到的库函数(如printf函数)。 |
| |
| preprocessor --> compiler --> linker |
| |
| 上述的过程是自动实现的,因为预处理器通常会和编译器集成在一起,所以人们不会注意到它在工作 |
| |
| 根据不同的编译器和操作系统,编译和链接所需的命令也是多种多样的,在UNIX系统环境下通常把C编译器命名为cc,为了编译和链接上述程序,需要在终端或命令行窗口输人如下命令: |
| % cc pun.c |
| 注:字符%是UNIX系统的提示符,不需要输人,在使用编译器cc时,系统自动进行链接操作,无须单独的链接命令 |
| |
| 在编译和链接好程序后,编译器cc会把可执行程序放到默认名为a.out的文件中。编译器cc有许多选项,其中一个选项(-o)允许为含有可执行程序的文件选择名字。 |
| 例如要把文件pun.c生成的可执行文件命名为pun,那么只需输人下列命令: |
| % cc -o pun pun.c |
| |
| |
| |
| |
| # 指令 |
| 在编译C程序之前预处理器会首先对其进行编辑,我们把预处理器执行的命令成为指令,如 #include<stdio.h> 则说明在编译前把 <stdio.h> 中的信息 "包含" 到程序中 |
| <stdio.h> 被称为头,每个头都包含一些标准库的内容,<stdio.h>没有内置的 "读" 和 "写"命令 |
| 所有指令都是以字符 # 开始,指令默认只占一行且每条指令的结尾没有分号或其他特殊标记 |
| |
| |
| # 函数 |
| 函数分为2大类:程序员编写的函数 和 作为C语言实现的一部分提供的函数,后者称为库函数(library function),因为他们属于一个由编译器提供的函数"库" |
| |
| |
| # 语句 |
| 要求某个函数执行分派给它的任务称为调用这个函数 |
| C语言规定每条语句都要以分号结尾(复合语句除外),原因是由于语句可以连续占用多行,有时很难确定它的结束位置,因此用分号来向编译器显示语句的结束位置,这点和 指令 不同 |
| |
| |
| # 注释 |
| C语言中注释有4种写法: |
| /*这是第一种注释*/ ===> 单行注释 |
| |
| |
| /* 这是第二种注释 |
| 可要分清了 */ |
| |
| |
| /* 这是第三种注释 |
| 可要分清了 |
| */ |
| |
| |
| /* |
| * 这是第四种注释 |
| * 可要分清了 |
| */ |
| |
| |
| C99提供了另一种写法: //这是注释 ===> 单行注释的2种写法都可以 |
| |
| 注:传统风格的注释不允许嵌套 |
| |
| |
| |
| |
| |
| |
| # 变量和赋值 |
| 每一个变量都必须有一个类型(type),类型用来说明变量所存储的数据的种类,类型会影响变量的存储方式以及允许对变量进行的操作,所以选择合适的类型非常关键 |
| 例如数值型变量的类型决定了变量所能存储的最大值和最小值,同时也决定了是否允许在小数点后出现数字 |
| int(integer)型变量可存整数,整数的取值范围是受限制的,做大的整数通常是2147483647,但在某些计算机上也可能是32767 |
| float(floating-point)型变量可存储带小数位的数,但float型变量在进行算术运算时float型通常比int型变量慢,且float型变量所存储的数值往往只是实际数值的一个近视值 |
| float类型的值通常分成2部分存储: 小数部分 和 指数部分 |
| |
| |
| # 声明 |
| 在使用变量前必须对其进行声明(为编译器所做的描述),声明变量首先要指定变量的类型,然后说明变量的名字,例如 |
| int book; |
| int height, length, width, volume; |
| float profit; |
| float profit, loss; |
| |
| int mai(void) |
| { |
| 声明 |
| 语句 |
| } |
| 注: 建议在声明和语句之间留出一个空行 |
| |
| |
| # 赋值 |
| 变量通过赋值(assignment)的方式获得值,例如: |
| height = 8; |
| length = 9; |
| 变量在赋值或以其他方式使用前必须先声明,即: |
| int height; |
| float length; |
| int volume; |
| |
| height = 8; |
| length = 3.5f; # 把一个包含小数点的常量赋值给float型变量时最好在该变量后面加上一个字母f(代表float,默认保留小数点后6位数字),见下注 |
| volume = height * length; # 赋值运算的右侧可以是一个含有常量、变量和运算符的公式(术语叫表达式) |
| |
| print("%d\n%f\n", height, length) # 占位符%d用来指明在显示过程中变量height和length的值的显示位置,如要强制%f显示小数点后9位数字可在%和f中间加上数字 |
| printf("%d\n", volume); |
| |
| 注: |
| printf("%.2f", length) 即保留小数点后2位 |
| 包含小数点但不以f结尾的常量是double(double precision的缩写)型 |
| |
| |
| |
| # 初始化 |
| 当程序开始执行时,某些变量会被自动设置为零(大多数变量则不会),没有默认值且尚未在程序中被赋值的变量是未初始化的(uninitialized) |
| 在声明变量时加入初始值 |
| int height = 8, length = 12, width = 10; # 在同一个声明中可以对任意数量的变量进行初始化 |
| |
| |
| # 读入输入 |
| 为了获取输入就要用到scanf函数,scanf函数和printf相对应,2个函数中的f都是 "格式化" 的意思,且2个函数都需要使用格式串(format string)来指定输入/输出数据的形式 |
| scanf函数需要知道将获得的输入数据的格式 |
| 示例: |
| scanf("%d", &i); 正确 |
| scanf("4df%d", &i); 错误,格式串中不能出现任意多余的字符 |
| |
| printf函数需要知道输出数据的显示格式 |
| 示例: |
| printf("这是你输入的数: %d", i) |
| |
| 一般每一个scanf函数调用都紧跟在一个printf函数调用的后面,这样可提示用户何时输入和输入什么 |
| 示例: |
| #include<stdio.h> |
| int main(void) |
| { |
| int i; ==> 定义变量 |
| printf("请输入你要输出的数: "); ==> 打印提示语 |
| scanf("%d", &i); ==> 获取输入 |
| printf("%d", i); ==> 打印变量/输入 |
| |
| return 0; ==> 返回类型 |
| } |
| 注:如用户输入的不是数值,会返回未知的数值 |
| |
| |
| |
| |
| # 定义常量的名字 |
| 当程序含有常量时建议给这些变量命名,在以后阅读程序是也许会有人不明白之前定义常量的含义,因此可采用称为宏定义(macro definition)的特性给常量命名,宏名字一般为大写 |
| 格式1: |
| #define INCHES_PER_POUND 166 |
| |
| 说明:这里的 #define 是预处理指令,类似前面的#include,因而该行结尾也没有分号,当对程序进行编译是,预处理器会把每一个宏替换为其表示的值,例如: |
| weight = (volume + INCHES_PER_POUND - 1) / INCHES_PER_POUND |
| 简化为:weight = (volume + 166 -1) / 166 |
| |
| 格式2: |
| #define INCHES_PER_POUND (2 * 6) |
| |
| |
| |
| |
| # 标识符 |
| 在编写程序时需要对变量、函数、宏和其他实体进行命名,这些名字成为标识符(identifier)。在C语言中标识符可含有字母、数字、下划线,但必须以字符或下划线开头 |
| C语言区分大小写因此在标识符中只用小写字母(宏命名除外) |
| C对标识符的最大长度在C89中没有限制,但只要求编译器记住前31个字符(C99中是63个字符),因此如2个名字的前31个字符都相同则编译器可能会无法区分它们 |
| 在C99中标识符还可用某些 "通用字符名" |
| |
| |
| |
| |
| # C程序的书写规范 |
| 可把C程序堪称一连串的记号(token),标识符、关键字、运算符、逗号、分号这样的标点符号以及字面串都是记号,例如: |
| printf ( "Height : %d\n" , height ) ; |
| 1 2 3 4 5 6 7 |
| |
| 释义: |
| 1和5都是标识符,3是字面串,2467是标点符号 |
| |
| 大多数情况下程序中记号间的空格数量没有严格要求,除非2个记号合并后会产生第三个记号,否则一般情况下记号间不需要留有间隔 |
| 为便与区分才在记号间加的空格,基于这个原因才会通常在每个运算符的前后都放上一个空格,此外需要在每个逗号后放一个空格 |
| |
| 如果语句非常长,很难将它压缩在一行内 |
| printf("fjfjkaflakfkkf: %d\n", |
| (volume + INCHES_PER_PRUND -1) / INCHES_PER_PRUND); |
| |
| |
| |
| |
| # 问与答 |
| Linux本身只是操作系统的"内核"(处理程序调度和基本输入/输出服务的部分),为了获得具备完整功能的操作系统,GUN软件是必要的 |
| |
| -std=c89 或 -std=c99 指明使用哪个版本的C编译器来检查程序 |
| 示例: |
| gcc -std=c99 -o pun pun.c |
| |
| |
| 在C语言中main函数的结尾使用exit(0) 和 return 0 两者是一样的 |
| |
| 缩进时推荐采用2个空格 |
| |
| |

第3章格式化输入/输出
| # printf函数 |
| printf函数被设计用来显示格式串的内容且在该串中的指定位置插入可能的值,调用printf函数时必须提供格式串,格式串后面的参数是需要在显示时插入到该串中的值 |
| 格式:printf(格式串, 表达式1, 表达式2 ....); |
| 示例: printf("%d", i); |
| |
| 格式串包含平台字符和转换说明(conversion specification),期中转换说明以字符%开头。 |
| 转换说明是用来表示打印过程中待填充的值的占位符,跟随在字符%后边的信息指定了把数值从内部形式(二进制)转换成打印形式(字符)的方法,这就是"转换说明"这一术语的由来 |
| 例如转换说明%d指定printf函数把int型值从二进制形式转换成十进制数字组成的字符串(转换说明%f对float型值也是类似的转换) |
| |
| C语言编译器不会检测格式串中转换说明的数量是否和输出项的数量相匹配,也不检测转换说明是否适合要显示项的数据类型,如果使用了不正确的转换说明,程序将产生无意义的输出 |
| |
| |
| # 转换说明 |
| 转换说明可用 %m.pX格式 或 %-m.pX格式,这里的m和p都是整型常量,X是字母,m 和 p 都是可选的 |
| 在转换说明%10.2f中,m是10,p是2,X是f |
| 如果省略p那么m和p之间的小数点也要去掉,如下面3种情况: |
| (1)在转换说明%10.2f中,m是10,p是2,X是f |
| (2)在转换说明%10f中,m是10,p(和小数点)省去了 |
| (3)在转换说明%.2f中,p是2,m省去了 |
| |
| 最小栏宽(minimum field width) |
| m指定了要显示的最少字符数量, |
| 如要显示的数值所需的字符数少于m则值在字符案内是右对齐的(-m是左对齐),值前面用空格填充 |
| 例如转换说明%4d将以 *123的形式显示数123(*表示一个空格) |
| |
| 如要显示的数值所需的字符数多于m则栏宽会自动扩展为所需的尺寸 |
| 例如转换说明%4d将以12345的形式显示12345,且不会丢失数字 |
| |
| 精度(precision) |
| p依赖于转换指定符(conversion specifier)X的选择,X表明在显示数值前需要对其进行哪种转换 |
| d --- 表示十进制(基数为10)形式的整数,p指明待显示数字的最少个数(必要时数前加零),如省略p则默认它的值为1 |
| e --- 表示指数(科学技术法)形式的浮点数,p指明小数点后应出现数字的个数(默认是6位),如p为0则不显示小数点 |
| f --- 表示"定点十进制"的浮点数,没有指数,p的含义和e的一样 |
| g --- 表示指数形式或定点十进制形式的浮点数,形式的选择根据数的大小决定,p为可显示的有效数字(非小数点后的数字)的最大数量,和转换指定符f不同的是g的转换不显示尾随的零,和如要显示的数值没有小数点后的数字则g不会小数点 |
| |
| 编写程序时无法预知数的大小或者数值变化范围很大的情况下,说明符g对于数的显示是特别有用的 |
| 在用于显示大小适中的数值时说明符g采用定点十进制形式 |
| 在用于显示非常大或非常小的数值时说明符g会转换成指数形式以便减少所需的字符数 |
| |
| 示例: |
| int i = 40; |
| float x = 839.21f; |
| print("|%d|%5d|%-5d|%5.3d|\n", i, i, i, i); |
| print("|%10.3f|%10.3e|%-10g|\n", x, x, x); |
| |
| 输出: |
| |40| 40|40 | 040| |
| | 839.210| 8.392e+02|839.21 | |
| |
| 释义: |
| %d --- 以十进制形式显示变量且占用最少的空间 |
| %5d --- 以十进制形式显示变量且最少占用5个字符的空间,如表示变量的值不够用满5个字符则变量前面添加空格来凑够5个字符,即原串右对齐前面用空格补齐(默认是右对齐) |
| %-5d --- 以十进制形式显示变量且最少占用5个字符的空间,如表示变量的值不够用满5个字符则变量后面添加空格来凑够5个字符,即原串左对齐后面用空格补齐(默认是右对齐) |
| %5.3d --- 以十进制形式显示变量且最少占用5个字符的空间,并至少有3位数字,如表示变量的值不够用满5个字符则变量前面添加空格来凑够5个字符(默认是右对齐) |
| %10.3f --- 以定点十进制形式显示变量且整个变量总共用10个字符,其中小数点后保留3位数字,因为现变量只有7个字符(小数点前/后各3位,小数点本身是1位)所以在变量x前面加3个空格,其最后结果是 3个空格+839.210 |
| %10.3e --- 以指数形式显示变量x且整个变量总共用10个字符,其小数点后保留3位数字,因为变量x总共需要9个字符(包括指数),所以在变量x前面有一个空格 ===> 这里没懂,如有知道的大佬可告知 |
| %-10g --- 以定点十进制形式 或 以指数形式显示变量,且总共用10个字符,在这里printf函数选择用十进制形式变量x负号会进行强制左对齐,因此有4个空格跟在变量x后面 |
| |
| |
| # 转义序列 |
| 格式串中常用到的代码\n被称为转义序列(escape sequence) |
| 警报(响铃)符:\a |
| 回退符:\b |
| 换行符:\n |
| 水平制表符: \t |
| 转义符:\ 示例:\" 和 \\ 即将 " 和 \ 转义称普通字符,这点和shell里面是同一个意思 |
| |
| |
| # scanf函数 |
| scanf函数根据特定的格式读取输入,scanf函数的格式串也可包含普通字符和转换说明两部分 |
| 很多时候scanf函数的格式串只包含转换说明,如下: |
| int i, j; |
| float x, y; |
| |
| printf("请输入要打印的数: "); |
| scanf("%d%d%f%f", &i, &j, &x, &y); ===> scanf中的变量前面有&,而printf中的变量前则没有,这里的&用于创建一个指向变量的指针,所以不能有其他任意字符 |
| printf("输入的是: %d-%d-%f-%f", i, j, x, y); |
| |
| return 0; |
| |
| |
| |
| |
| # 问与答 |
| 1、转换说明符%i也可用于读写整数,%i 和 %d有什么区别? |
| 答:在printf格式串中使用时二者没区别,但在scanf格式串中%d只能与十进制形式的整数相匹配,而%i则可匹配八进制(前缀有0)、十进制、十六进制(前缀有0x) |
| 如用户意外的将0放在数的开始处则用%i代替%d会有意想不到的结果,因为这是一个缺陷,所以建议坚持采用%d |
| |
| 2、如printf函数将%作为转换说明的开始,需要如何显示字符%? |
| 答:如printf函数在格式串中遇到2个连续的字符%,则仅显示一个字符%,例如:printf("Net profit: %d%%\n", profit) 输出为 Net profit: 10% |
| |
| 3、如要求读入一个数,而用户录入了非数值的输入时scanf函数会如何处理? |
| 答:如输入中有数字和其他字符则scanf只会读入数字,其他字符会在下次进行读取,如输入中只有其他字符则没有一个值会被存储到变量中,所有其他字符都将留给下一次scanf函数调用 |
第4章 表达式
计算通用产品代码的校验码
| 美国和加拿大的货物生产商会在超市销售的每件商品上放置一个条形码。 这种被称为通用产品代码(Universal Product Code, UPC)的条形码可以识别生产商和产品。 |
| 每个条形码表示一个12位的数,通常这个数会打印在条形码下面。例如以下的条形码来自Sufers法式面包腊肠比萨的包装: |
| |

| 数字 0 13800 15173 5 出现在条形码的下方。 |
| 第1个数字表示商品的种类(大部分商品用0成者7表示,2表示需要称量的商品,3表示药品或与健康相关的商品,而5表示赠品)。 |
| 第一组5位数字用来标识生产商( 13800是雀巢美国的冷冻食品公司的代码)。 |
| 第二组5位数字用来标识产品(包括包装尺寸)。 |
| 最后1位数字是“校验位”,它唯一的目的是帮助识别前面数字中的错误。如果条形码扫描出现错误,那么前11位数字可能会和最后一位数字不匹配, 超市扫描机将拒绝整个条形码。 |
| |
| 下面是一种计算校验位的方法: |
| 首先把第1位、第3位、第5位、第7位、第9位和第11位数字相加: |
| 然后把第2位、第4位、第6位、第8位和第10位数字相加 |
| 接着把第一次加法的结果乘以3,再和第二次加法的结果相加; |
| 随后再把上述结果减去1; |
| 相减后的结果除以10取余数; |
| 最后用9减去上一步骤中得到的余数。 |
| |
| 拿上述条形码举例,即0+3+0+1+1+3得到第一个和8, |
| 由1+8+0+5+7 得到第二个和21。 |
| 把第一个和乘以3(得24)后再加上第二个和得到45(即24+21的和),45减1得到44。 |
| 把44除以10取余数为4。再用9减去余数4,结果为5 |
| |
| |
| 下面编写一个程序来计算任意通用产品代码的校验位。要求用户输入通用产品代码的前11位数字,然后程序显示出相应的校验位。为了避免混淆,要求用户分3部分输人数字: |
| 左边的第一个数字、第一组5位数字以及第二组5位数字 |
| 程序会话的形式如下所示: |
| Enter the first (single) digit: 0 |
| Enter first group of five digits: 13800 |
| Enter second group of five digits: 15173 |
| Check digit: 5 |
| |
| |
| 程序不是按一个5位数来读取每组5位数字的,而是将它们读作5个1位数。把数看成一个个独立的数字来读取更为方便,而且也无须担心由于5位数过大而无法存储到int型变量中。 |
| (某些编译器限定int型变量的最大值为32767。)为了读取单个的数字,我们使用带有%1d转换说明的scanf函数,其中1d匹配只有1位的整数。 |
| 文件名:upc.c |
| #include<stdio.h> |
| int main(void) |
| { |
| int d, i1, i2, i3, i4, i5, j1, j2, j3, j4, j5, |
| first_sum, second_sum, total; |
| |
| printf("请输入第一个(段)数字: " ); |
| scanf("%1d", &d); |
| |
| printf("请输入第二段中的5个数: "); |
| scanf("%1d%1d%1d%1d%1d", &i1, &i2, &i3, &i4, &i5); |
| |
| printf("请输入第三段中的5个数: "); |
| scanf("%1d%1d%1d%1d%1d", &j1, &j2, &j3, &j4, &j5); |
| |
| first_sum = d + i2 + i4 + j1 + j3 + j5; |
| second_sum = i1 + i3 + i5 + j2 + j4; |
| total = 3 * first_sum + second_sum; |
| |
| printf("check digit: %d\n\n", 9 - ((total - 1) % 10)); |
| |
| return 0; |
| } |
| |
| |
| |
| # 赋值运算符 |
| 第44页 |
| |
| |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
2018-05-19 nginx-1.13.12 源码配置清单