C语言学习笔记

借助C Primer Plus第六版中文版

[TOC]

一、初识C语言

基本概览,没啥好记的

二、 C语言概述

简单的C程序示例

注释略显杂乱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 这是单行注释
/*
这是多行注释
*/

#include <stdio.h> //预处理器指令,包含一个文件。C程序顶部的信息集合被称为头文件
int main(void) //main总是第一个被调用的函数,void表示main不带任何参数
{ //函数体开始,必须使用花括号
int num; //声明变量,int代表整型。int是关键字,num是变量,属于标识符
//给变量命名:只能大小写字母、数字和下划线,第一个字符不能是数字。尽量避免下划线开头,可能会与其他标识符冲突
num = 1; //赋值表达语句,从右侧把值赋到左侧

printf("I am a simple "); //调用printf函数,printf是函数名,圆括号内是参数。可以理解为这里控制权从main转给了printf
printf("computer.\n"); // \n是一个转移序列,代表换行
printf("My favourite number is %d because it is first.\n", num); // %d是占位符,指明输出num的位置

return 0; //return语句,目前可以看做是结束main函数的要求
} //函数体结束

简单程序的结构

包含函数头和函数体,函数体又主要有声明和语句

建议在一开始就进行声明,虽然新规则没有要求了但最好还是这样

大部分语句都以分号结尾!!!不要忘记!!!

提高程序可读性

  1. 积极写注释
  2. 使用有意义的变量名
  3. 使用空行分隔
  4. 每行一条语句
  5. 程序第一行就写注释,包括程序名称和目的

进一步使用C

同时声明多个变量:使用逗号隔开

1
int a, b;

打印多个值:同样用逗号隔开

1
printf("I like %d and %d !\n", food, drink);

待打印的值不一定是变量

1
printf("The answer is %d !", 3 * total);

多个函数

可以将自己的函数加入到程序中。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
void butler(void); //函数原型,告诉编译器在程序中要使用该函数,类似声明
int main(void)
{
printf("I will summon the butler function.\n");
butler(); //调用函数,写出函数名和圆括号即可
printf("Yes. Bring me some tea and writeable DVDs.\n");

return 0;
}
void butler(void) //函数定义,即函数本身的源代码。同时这也是函数头,其信息表明butler不带任何参数,且没有返回值
{
printf("You rang, sir?\n");
}
//函数放的位置和运行顺序没有关系,只看什么时候被调用。一般而言main函数放在第一个,因为它提供了程序的基本框架

调试程序

语法错误:把有效的C符号放在了错误的地方

如:用圆括号代替了花括号;声明变量的写法错误;多行注释没写末尾*/;没打分号等等

语义错误:语法正确,意思上的错误

编译器无法检测,因为语言规则正确,但是运行结果不符合预期要求

如何监视程序状态?

  1. 对于简单代码,假设自己是计算机,模拟运行一遍
  2. 在程序关键点插入额外的printf,了解程序执行情况
  3. 利用调试器(推荐)

关键字和保留标识符

一些关键字比较特殊,不能用它们作为标识符,如int、while、break等等

还有一些是保留标识符,C语言已经制定了他们的用途或者保留了他们的使用权,比如那些以下划线字符开头的标识符和标准库函数名,比如printf

练习

进行了第5题和第8题的练习

copilot真的太好用了你知道吗?不过为了练习需求还是不开了

三、数据和C

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main(void)
{
float weight; //新的浮点数类型的变量。其可以储存带小数的数字
float value;

printf("Are you worth your weight in platinum?\n");
printf("Let's check it out.\n");
printf("Please enter your weight in pounds: ");

scanf("%f", &weight); //用于读取键盘输入,%f表示读取浮点数。&weight表示把输入值赋给weight变量
value = 1700.0 * weight * 14.5833;
printf("Your weight in platinum is worth $%.2f.\n", value); //%.2f表示输出输出浮点数显示小数点的后两位
printf("You are easily worth that! If platinum prices drop,\n");
printf("eat more to maintain your value.\n");

return 0;
}

变量与常量数据

程序运行过程中没有变化的量叫做常量,在程序运行期间可能会被改变或者被赋值的叫做变量

数据:数据类型和关键字

对变量而言,要在声明时指定其类型

  • 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位,所以用64位储存一个int值

  1. 声明int变量:用多行或者逗号创建变量。此步是为变量创建存储空间

  2. 初始化变量:也就是为变量赋一个初始值。方法如下:

    1. 直接赋值

      1
      cows = 100;
    2. 通过函数比如scanf

    3. 声明时就初始化

      1
      2
      3
      int cats = 100, dogs = 200;
      //不要像下面这样,把未初始化和初始化的放在同一行,容易误解
      int dogs, cats = 100;
  3. int类型常量:C把不含小数点和指数的数作为整数,它们都是整形常量。非常大的整数除外

  4. 打印int值:使用printf打印,%d(转换说明)表明了打印整数的位置(%d与所有int类型相匹配)。要确保转换说明的数量与待打印值的数量相同

  5. 八进制和十六进制:0前缀表示八进制,0x或者0X表示16进制。如16是020,也是0x10

  6. 显示八进制和十六进制:将%d换为%o或者%x、%X,就可以以8进制或者16进制显示数字。如果要在前面加上0和0x前缀,改为%#o或者%#x、%#X

其他整数类型(前缀)

  1. short,占空间比int少,用于较小数值场合
  2. long,占用存储空间比int多,较大数值场合
  3. unsigned,用于非负值场合

如何选择?

  1. 先考虑unsigned类型,此类型一般用于计数
  2. 超出int范围,则使用long,甚至long long
  3. short用于节省空间

通常,编译器会“自动”为数字选择对应的存储类型

若想在int为16为,long为32位的系统中,将“自动”存储为int类型的数值用long类型存储,则可以在该值的末尾加上l或L后缀。一般使用L因为小写l容易弄混。类似的,8进制和16进制也可以;long long则是两个L;u或U表示unsigned

1
2
3
4
//例如:
a = 7L;
b = 10ULL;
//貌似这里识别有问题,不用管
打印short、long等类型

转换说明:

  1. unsigned int:%u

  2. long:%ld(若系统中int和long大小相同那么%d就可以)

    八进制或者十六进制就是在对应字母前加上l,如%lo、%lx

  3. short:%hd,其他进制类似long,但是前缀是h

  4. 组合unsigned:在最后加上u后缀,如%lu、%llu

使用错误的转换说明会导致输出错误的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//例子如下:
#include <stdio.h>
int main(void)
{
unsigned int un = 3000000000;
short end = 200;
long big = 65537;
long long verybig = 12345678908642;

printf("un = %u and not %d\n", un, un);
printf("end = %hd and not %d\n", end, end);
printf("big = %ld and not %hd\n", big, big);
printf("verybig = %lld and not %ld\n", verybig, verybig);

return 0;
}

/*
输出结果如下:
un = 3000000000 and not -1294967296 无符号和有符号,这两个结果在系统内存中表示完全相同
end = 200 and not 200 编译器自动把short值转换为了int,因为这样处理最高效
big = 65537 and not 1 使用%hd时,只会截取最后16位
verybig = 12345678908642 and not 1942899938 类似上一行,%ld截取最后32位
*/

整数溢出
如果整数超过了取值范围就会溢出。unsigned溢出后从0开始,int从-2147483648开始

使用字符:char类型

实际上char存储的是整数,是整数类型。所以使用ASCII编码,用特定整数代表特定字符。也有其他编码方式如Unicode

声明char类型变量

类似其他

1
char response;
字符常量和初始化

注意:C语言中单引号双引号不同!

1
2
3
4
5
6
7
char broiled;
broiled = 'T'; //正确,单引号括起来的叫做字符常量
broiled = T; //错误!此时T是一个变量
broiled = "T"; //错误!此时"T"是一个字符串

//事实上,char是以整数形式存储的,所以以下方式也可以(不妥当)
char grade = 65;
非打印字符

如退格、换行等,可以用3种方式表示这些字符

1
2
3
4
5
6
7
8
// 1.使用ASCII码,如蜂鸣字符的ASCII值是7
char beep = 7;

// 2. 使用转移序列(重要,对照表在后面)
char nerf = '\n'
//代表换行。那么打印nerf的作用就是另起一行

// 3.使用十六进制表示字符常量

转义序列部分对照

  • \a:警报
  • \b:退格,移动光标位置往前一格,不是删除
    ​ 输入时数据会替换掉后面的字符
  • \f:换页
  • \n:换行
  • \r:回车(回到本行第一个字符)
  • \t:水平制表符
  • \v:垂直制表符
  • \\:取消对\的转义
  • \‘:单引号’
  • \“:双引号’’
  • ?:问号?
  • \0:后面接0-7的数字,用八进制值ASCII码表示字符
  • \x:后面接0-f,用十六进制值ASCII码表示字符

一些需要注意的

  1. 使用ASCII码时注意数字和数字字符的区别。如字符4对应的ASCII码是52,’4’表示字符4而不是数值4
  2. 双引号中不需要再把转移序列用单引号括起来
  3. 优先使用转移序列而不是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
2
3
4
float noah, jonah;
float trouble;
float planck = 6.63e-34;
long double gnp;
浮点型常量

基本形式:有符号的数字(包括小数点),后面紧跟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四道题,目前题目依旧比较简单

简单了解了一下还没学的scanf的用法

四、字符串和格式化输入/输出