C语言学习笔记
借助C Primer Plus第六版中文版
[TOC]
一、初识C语言
基本概览,没啥好记的
二、 C语言概述
简单的C程序示例
注释略显杂乱
1 | // 这是单行注释 |
简单程序的结构
包含函数头和函数体,函数体又主要有声明和语句
建议在一开始就进行声明,虽然新规则没有要求了但最好还是这样
大部分语句都以分号结尾!!!不要忘记!!!
提高程序可读性
- 积极写注释
- 使用有意义的变量名
- 使用空行分隔
- 每行一条语句
- 程序第一行就写注释,包括程序名称和目的
进一步使用C
同时声明多个变量:使用逗号隔开
1 | int a, b; |
打印多个值:同样用逗号隔开
1 | printf("I like %d and %d !\n", food, drink); |
待打印的值不一定是变量
1 | printf("The answer is %d !", 3 * total); |
多个函数
可以将自己的函数加入到程序中。示例代码如下:
1 |
|
调试程序
语法错误:把有效的C符号放在了错误的地方
如:用圆括号代替了花括号;声明变量的写法错误;多行注释没写末尾*/;没打分号等等
语义错误:语法正确,意思上的错误
编译器无法检测,因为语言规则正确,但是运行结果不符合预期要求
如何监视程序状态?
- 对于简单代码,假设自己是计算机,模拟运行一遍
- 在程序关键点插入额外的printf,了解程序执行情况
- 利用调试器(推荐)
关键字和保留标识符
一些关键字比较特殊,不能用它们作为标识符,如int、while、break等等
还有一些是保留标识符,C语言已经制定了他们的用途或者保留了他们的使用权,比如那些以下划线字符开头的标识符和标准库函数名,比如printf
练习
进行了第5题和第8题的练习
copilot真的太好用了你知道吗?不过为了练习需求还是不开了
三、数据和C
示例代码
1 |
|
变量与常量数据
程序运行过程中没有变化的量叫做常量,在程序运行期间可能会被改变或者被赋值的叫做变量
数据:数据类型和关键字
对变量而言,要在声明时指定其类型
- int表示基本的整数类型,其关键字long、short、unsigned用于提供其变式
- char表示字母和其他字符,也可以表示较小的整数
- float、double和long double表示带小数点的数
- _Bool表示布尔值(true和false)
- _complex表示复数
- _Imaginary表示虚数
按照计算机的存储方式可以分为两大基本类型:整数类型和浮点数类型
位、字节和字
- 位bit:最小存储单元,可以储存0或1
- 字节byte:1字节有8位,有256种组合
- 字word:设计计算机时给定的自然存储单位,目前一般为64位。字长越大,数据转移越快,允许的内存访问也就更多
整数
8位字节中储存,二进制。例如7表示为0000111
浮点数
第一位表示正负号,最后一位表示10的几次幂
C语言基本数据类型
int类型
目前计算机一般是64位,但是大多数情况任然用32位储存一个int值
声明int变量:用多行或者逗号创建变量。此步是为变量创建存储空间
初始化变量:也就是为变量赋一个初始值。方法如下:
直接赋值
1
cows = 100;
通过函数比如scanf
声明时就初始化
1
2
3int cats = 100, dogs = 200;
//不要像下面这样,把未初始化和初始化的放在同一行,容易误解
int dogs, cats = 100;
int类型常量:C把不含小数点和指数的数作为整数,它们都是整形常量。非常大的整数除外
打印int值:使用printf打印,%d(转换说明)表明了打印整数的位置(%d与所有int类型相匹配)。要确保转换说明的数量与待打印值的数量相同
八进制和十六进制:0前缀表示八进制,0x或者0X表示16进制。如16是020,也是0x10
显示八进制和十六进制:将%d换为%o或者%x、%X,就可以以8进制或者16进制显示数字。如果要在前面加上0和0x前缀,改为%#o或者%#x、%#X
其他整数类型(前缀)
- short,占空间比int少,用于较小数值场合
- long,占用存储空间比int多,较大数值场合
- unsigned,用于非负值场合
如何选择?
- 先考虑unsigned类型,此类型一般用于计数
- 超出int范围,则使用long,甚至long long
- short用于节省空间
通常,编译器会“自动”为数字选择对应的存储类型
若想在int为16为,long为32位的系统中,将“自动”存储为int类型的数值用long类型存储,则可以在该值的末尾加上l或L后缀。一般使用L因为小写l容易弄混。类似的,8进制和16进制也可以;long long则是两个L;u或U表示unsigned
1 | //例如: |
打印short、long等类型
转换说明:
unsigned int:%u
long:%ld(若系统中int和long大小相同那么%d就可以)
八进制或者十六进制就是在对应字母前加上l,如%lo、%lx
short:%hd,其他进制类似long,但是前缀是h
组合unsigned:在最后加上u后缀,如%lu、%llu
使用错误的转换说明会导致输出错误的结果
1 | //例子如下: |
整数溢出
如果整数超过了取值范围就会溢出。unsigned溢出后从0开始,int从-2147483648开始
使用字符:char类型
实际上char存储的是整数,是整数类型。所以使用ASCII编码,用特定整数代表特定字符。也有其他编码方式如Unicode
声明char类型变量
类似其他
1 | char response; |
字符常量和初始化
注意:C语言中单引号双引号不同!
1 | char broiled; |
非打印字符
如退格、换行等,可以用3种方式表示这些字符
1 | // 1.使用ASCII码,如蜂鸣字符的ASCII值是7 |
转义序列部分对照
- \a:警报
- \b:退格,移动光标位置往前一格,不是删除
输入时数据会替换掉后面的字符- \f:换页
- \n:换行
- \r:回车(回到本行第一个字符)
- \t:水平制表符
- \v:垂直制表符
- \\:取消对\的转义
- \‘:单引号’
- \“:双引号’’
- ?:问号?
- \0:后面接0-7的数字,用八进制值ASCII码表示字符
- \x:后面接0-f,用十六进制值ASCII码表示字符
一些需要注意的
- 使用ASCII码时注意数字和数字字符的区别。如字符4对应的ASCII码是52,’4’表示字符4而不是数值4
- 双引号中不需要再把转移序列用单引号括起来
- 优先使用转移序列而不是ASCII码
打印字符
在printf用%c表示待打印的字符
如果使用%d则会直接打印一个整数
有符号&无符号
有些编译器把char视为有符号类型,那么此时char表示的范围就是-128~127;有的视为无符号类型,那么范围就是0~255
可以在char前面使用signed或者unsigned
_Bool类型
用于表示布尔值true或者false,1表示true,0表示false
所以_Bool实际上也是一种整数类型
可移植类型:stdint.h和inttypes,h
头文件需要使用<inttypes.h>
此数据类型略
float、double和long double
C标准规定,float至少能表示6位有效数字,double至少可以表示10位有效数字。float一般占用32位,其中8位表示指数的的值和符号,身下24位用于表示非质指数部分;double一般占用64位而不是32位,因此至少可以表示13位有效数字
声明浮点型变量
1 | float noah, jonah; |
浮点型常量
基本形式:有符号的数字(包括小数点),后面紧跟e或E,最后是一个有符号数表示10的指数
可以省略正号;可以省略小数点(2e5)或指数部分(19.28);可以省略小数部分(3.e16)或整数部分(.45e-6)
e和E左右都不要加空格
默认情况下,编译器假定float是double类型的精度并使用双精度进行乘法运算,然后将结果截断成float,这样计算精度更高但是会损失速度。若要强制视为float类型,在数据最后加上f或F。若要强制视为long double类型,加上l或L。
C99标准添加了一种用16进制表示浮点型常量的方法,此处略
打印浮点值
在printf中使用%f表示待打印的浮点值。若要打印指数计数法的形式,用%e(C99标准也可以用%a)
打印long double,使用%Lf、%Le(或%La)
浮点值的上溢overflow和下溢underflow
上溢:当计算值过大超出当前类型能表达的范围,printf显示为inf或infinity
下溢:因为精度原因损失了有效数位,0.1234e-10除以10得到0.0123e-10
未定义浮点值
如给asin()函数输入一个大于1的值,因为sin的范围不能大于1,所以返回值是NaN,printf会显示为nan等
浮点值舍入错误
若浮点能够储存的有效数字少于运算所需的,就可能发生错误,因为计算机缺少足够的小数位来完成运算。
例如2.0e20先加1,再减2.0e20,得到的结果并不是1
复数和虚数类型
三种复数类型:float_Complex、double_Complex和long double_Complex
类似地,三种虚数类型float_Imaginary,后略
这些类型的变量包括两个float类型的值,分别用于表示复数的实部和虚部
如果包含complex.h头文件,则可使用complex、imaginary用来代替_Complex和_Imaginary
类型大小
使用sizeof()运算符,以字节为单位给出指定类型的大小,在printf中用%zd表示
1 | printf("The int has a size of %zd bytes.\n", sizeof(int)); |
使用数据类型
初始化变量时应使用与变量类型匹配的常数类型
若类型不对应,会损失部分数据,例如float转化为int会直接丢弃小数点后的值,double转化为float会损失精度
参数和陷阱
传递给函数的信息称为参数
printf中参数不对应时并不会给出警告,如果发现程序输出与预期不符,可以进行检查
程序运行情况
刷新输出
printf会将输出发送到一个叫做“缓冲区”的中间存储区域,然后缓冲区的内容再不断被发送到屏幕上
当缓冲区满、遇到换行符或者需要输入的时候,会将缓冲区的内容发送到屏幕上
练习
练习2、4、5、6四道题,目前题目依旧比较简单,基本10行内都可以搞定
简单了解了一下还没学的scanf的用法
四、字符串和格式化输入/输出
字符串简介
字符串是一个或者多个字符的序列
用双引号括起,“告诉程序这是字符串”
char类型数组和null字符串
C语言的字符串储存在char类型的数组中,每个单元储存一个字符
数组末尾的字符 \0 是空字符,C语言用它标记字符串的结束,它不是数字0,是非打印字符
C中的字符串一定以空字符结束,所以数组容量必须至少比带存储字符串中的字符数多1
数组
数组是同类型数据元素的有序序列
1 | //通过以下声明创建了一个包含40个存储单元(元素)的数组 |
使用字符串
在printf中使用%s代表待打印的字符串
scanf在读取输入时,会自动把空字符放入字符串的末尾
字符和字符串
字符串常量”x”和字符常量’x’不同。
- ‘x’是基本类型char,而”x”是派生类型(char数组)
- “x”实际由两个字符组成,”x”和空字符\0
strlen()函数
sizeof()函数以字节为单位给出对象的大小。strlen()函数给出字符串中的字符长度
1 | //sizeof与strlen对比 |
如输入Serendipity Chane,得到输出:sizeof显示name数组有40个存储单元,但只有11个单元用来储存输入的Serendipity,因此strlen()输出结果11
注意:此处输出11并不是因为strlen()计数到了空格,而是因为scanf的%s是读取单词,碰到Serendipity后的空格之后就停止了读取,所以只有Serendipity被输入了name变量,chance没有被输入
而strlen(PRAISE)输出是31,可见空格也算作一个字符。sizeof输出则是32,因为把字符串末尾不可见的空字符也算在内了
常量和C预处理器
有时候需要在程序中使用常量,如使用圆周率3.14159,一般而言,可以直接输入这个值,但是使用一格1符号常量,如π,代替它会更好,原因如下:
- 常量名比单纯的数字表达的信息更多
- 假设程序多处要使用某一个常量,例如税率,其经常变化,需要在程序中反复多次修改不方便,使用符号变量可以简单做到而无需在程序中查找
我们一般可以使用声明变量并给其赋值的方法,但是可能会导致无意中变量的值被改变
因此,我们可以使用C预处理器,只需在程序顶部添加这一行:
1 |
|
同时define指令也可以定义字符和字符串常量,前者单引号后者双引号
注意define指令不需要”=”
const限定符
也可以不使用define而使用const限定符将一个变量限定为只读
1 | const int MONTHS = 12; //MONTHS在程序中不可修改,值为12 |
明示常量
C的头文件limits.h和float.h分别提供了与整数类型和浮点类型大小限制的详细信息
如limits.h头文件包含如下代码
1 |
|
limits.h: https://www.runoob.com/cprogramming/c-standard-library-limits-h.html
float.h: https://www.runoob.com/cprogramming/c-standard-library-float-h.html
printf() 和 scanf()
它们是输入/输出函数,或I/O函数
两个函数工作原理几乎相同,都是用格式字符串和参数列表
printf()
打印数据的指令要和待打印的数据类型相匹配,如打印整数时使用%d。这些符号被称为转换说明
转换说明
- %a和%A:浮点数、十六进制数和p计数法
- %c:单个字符
- %d:有符号的十进制整数
- %e和%E:浮点数,e计数法
- %f:浮点数,十进制计数法
- %g和%G:自动选择%e或%f。%e用于指数小于-4或者大于或等于精度时
- %i:和%d相同,有符号十进制整数
- %o:无符号八进制整数
- %p:指针
- %s:字符串
- %u:无符号十进制整数
- %x和%X:无符号十六进制整数,使用0f或0F
- %%:打印一个百分号
使用printf
printf函数的格式:printf(“格式字符串”, 待打印项1, 待打印项2……)
格式字符串时双引号括起来的内容;待打印项可以是变量、常量,甚至是打印之前先要计算的表达式
1 | printf("Farewell!\n"); //可以不用转义序列 |
要打印%,转义序列是%%
printf()的转换说明修饰符
在%和转换字符之间插入修饰符可以修改基本的转换说明
printf中的修饰符
- 标记:见下一条注释
- 数字:最小字段宽度。如果输出的字符数小于这个,则(在前面)补上空格,如果输出字符数大于这个则无影响
- .数字:精度。
- 对于%e、%E、%f,表示小数点右边数字的位数,超出一般四舍五入
- 对于%g和%G,表示有效数字最大位数
- 对于%s,表示待打印字符的最大数量(如果长度小于这个就打印整个字符串,大于则只输入前面的)
- 对于整型,表示最小位数,如果不足最小位数就在前面补0
- .后不接数字等于.0
- h:和整型一起使用表示short int或者unsigned short int
- hh:和整型一起使用,表示signed char或unsigned char
- j:和整型一起使用,,表示intmax_t或uint_max(此类型定义在stdint.h中)
- l:和整型一起使用,表示long int或unsigned long int
- ll:和整型一起使用,表示long long int或者unsigned long long int
- L:和浮点一起使用,表示long double
- t:和整型一起使用,表示ptrdiff_t(两个指针差值的类型)
- z:和整型一起使用,表示size_t(sizeof返回的类型)
没有float的转换类型,因为float被自动转换成了float类型
printf中的标记
-:左对齐,此时补空格补在右边
+:显示有符号值的正负(在前面加上+/-)
空格:类似+,但是+换成空格
#:对于非零八进制或者十六进制,加上0或者0x和0X;对于浮点数,即使小数点后没有数字,也打印小数点
0:对于数值格式,用0代替空格填充;如果是整数格式且出现了-或者指定了精度,则不影响
转换说明的意义
转换说明将以二进制形式储存在计算机中的值转换成一系列字符(字符串)以便展示(不会改变原始值,类似翻译)
转换不匹配
转换说明与数据类型不一致则会出现错误,具体错误略
参数传递
与计算机数据结构有关,此处略
printf()的返回值
printf()函数有一个返回值,是其打印字符的个数
1 | int a = 100; |
第二行会输出12,因为第一行共打印了12个字符(包括换行符)。但是字符串末尾的\0空字符不会被包括
打印较长的字符串
四种方法断行:
- 在参数之间断为两行(不要在双引号的字符串中间断开)
- 使用多个printf,前面的不用\n
- 在双引号内用\和enter来换行,注意下一行必须顶格
- 将一个双引号拆分成多个双引号,中间可以加空格换行等等
使用scanf
如果用其读取基本变量的类型,则需要在变量名前加上&
如果用其读取字符串,则不需要加上&
1 | scanf("%d", &age); |
scanf使用空白(空格、换行符、制表符)将输入分成多个字段,依次输入(中间只需要至少一个即可,但是%c例外)
scanf的转换说明与修饰符
与printf几乎相同。不同的在下方列出
scanf特殊的修饰符
*:抑制负值
数字:输入达到最大字段宽度,或遇到空白字符时停止
从scanf角度看输入
挨个字符读取,例如%d类型输入12a3,会到a时停止读取,并把a3放入缓冲区,最终输入12
格式字符串中的普通字符
如果代码如下
1 | scanf("%d,%d", &m, &n); |
则必须按以下格式输入“[数字],[数字]”,即需要符合格式字符串
scanf会跳过整数前面的空白,所以逗号后可以加入空格、换行符、制表符
特殊:%c
除了%c,scanf会自动跳过输入的空白,所以”%d,%d”等价于”%d, %d”
而对%c,如果%c前面没有空白,则会从第一个字符开始读取;而如果有空白,则会从第一个非空白字符开始读取
scanf()的返回值
scanf()函数返回成功读取的项数,如果没有成功读取则返回0
可以使用scanf的返回值来检测(和处理)不匹配的输入
printf()和scanf()的*修饰符
printf()
用于通过程序指定字段宽度或者浮点精度
1 | printf("Width is |%*d|\n", width, number); //指示字段宽度 |
scanf()
用于跳过读取
1 | scanf("%*d %*d %d", &n); //此时输入三个数字,只有最后一个会被存入n |
printf()的用法提示
- 要想打印的数据整齐,可以使用足够大的固定字段宽度,如%9d
- 在两个打印的字符中间插入一个空白,可以避免因为数字超过字段宽度而导致两个数连在一起的情况
- 如果要在文字中嵌入一个数字,则字段宽度应该小于该数字长度,避免空白导致的不美观
练习
练习1、2、5、6、7五道题,依旧很简单



