Featured image of post C语言基础学习笔记

C语言基础学习笔记


main函数

main函数的基本信息

数顾名思义其实与数学的函数类似,只不过C语言的函数是更为广义的函数,表示为一种处理问题的方法。下面是函数的常见形式,且下面的函数是主函数。

1
2
3
4
int main(void)
{
	return 0
}

其中: int部分是返回类型。 main部分是函数名,类似数学函数中的f、g。 void部分是参数,类似于数学函数中的自变量,如f(x,y)。 return 0;表示返回一个0的数值。

关于返回类型,是用于告诉计算机函数返回值的类型,如:整型、浮点型、字符型、无返回(实际上无返回不代表真的无返回,知道即可)。类似于数学的整数、小数。更详细的解释看下面的变量。

在此,只需了解到此即可。重点是记住主函数的基本形式。缺斤少两不一定不能运行,但这是一个不好的习惯。

main函数的作用

对于一个程序,我们通常会存放很多函数。如果我们直接将一个程序从头到尾执行,必然会导致很多问题,比如:我们无法多次使用同一个函数,他的逻辑会非常混乱,难以移植等等。

因此C语言规定,在初始化完成后,程序只执行main函数里的内容,如果其中调用到其他函数,则再跳到其他函数执行里面的内容,并将返回值返回给主函数或其他函数。


变量与赋值

变量的定义

在程序中,我们通常需要一些变量来存储一些数值,来计算。这与数学中的变量类似。

变量的类型

由于计算机的存储的原理,我们无法像现实一般,将变量表示成为任何类型。计算机存储的变量通常是有界的。因此我们将变量分为不同类型,方便计算机识别。

变量类型 定义符 范围 例子
整型 int -32,768 到 32,767 2
长整型 long,long int -2,147,483,648 到 2,147,483,647 666666
单浮点型 float 1.2E-38 到 3.4E+38 3.14
双浮点型 double 2.3E-308 到 1.7E+308
长双浮点型 long double 3.4E-4932 到 1.1E+4932
字符型 char -128 到 127 哈哈6

在此简单的叙述一下变量的类型,实际上还能更加细分,剩下的内容,如:细分类型、范围、存储原理等,请自行查询。

变量的存储类型

默认为auto。

变量的初始化

下面为变量的初始化。

1
2
3
unsigned int number = 0;
char character = "Hello World!";
float little, small;

通常,初始化一个变量的步骤为: (修饰)+ 定义符 + 变量名 + (初始化的数值)

值得注意的是,我们可以单独定义变量而不用初始化。 即:int number;

我们也可以定义多个变量,变量中间用,隔开。

对于字符型,我们规定使用""表示一句话或单个字符,用''表示单个字符。

我们还可以用unsigned修饰定义符,表示无符号,即非负。用unsigned修饰时,变量类型的范围也会发生变化。

变量的赋值

变量的赋值是自右向左的,即右边的值(以后简称右值)赋予给左边的值(以后简称左值)。

1
2
int num = 0;
num = 1;

运算

与数学类似的我们用+表示相加,-表示相减,*表示相乘,/表示相除,%表示求余。

注意事项
一、类型转换

不同类型变量进行运算,低级类型的变量会先转换成高级类型的变量,再进行运算,最后再转换成要赋值的变量的类型。字符串无法转换,但单个字符会根据ASCII码转换成相应的整型。

在C语言中,浮点数比整数高级,范围越大的越高级,但低于int类型的都会转换成int类型再计算。

常量的小数默认是double型。

1
2
3
4
5
int num1 = 66;
float num2 = 0;
num2 = 1.0 * num1;
//在这里num1会转换成double型,再相乘
//最后转换成float型赋予给num2
二、运算符+等于号

对于运算符加等于号,表示左值与右值先运算,然后再赋予给左值。

1
2
3
4
5
6
7
8
9
int num = 0;
while(1)
{
	num++;
	num %= 3;
}

//在此num先求其除3的余数,再将余数赋值给num
//常用于限制num的自增
三、求余的限制

在C语言中,只能对整数求余,如果需要对小数求余,则需要调用math.h 中的fmod函数。

四、自增/自减符

++表示变量递增1,–表示变量递减1。自增/自减可以放于变量前,表示先加/减再用;放于变量变量后,先用再加/减。我们常用自增和自减优化算法。

1
2
3
4
int num1 = 6, num2 = 7;
printf("%d, %d", num1++, --num2);
/**输出结果**/
6, 6

stdio.h

库文件

C语言规定.h文件为库文件,库之所以叫库,是因为其中有别人写好的函数,你调用便可以使用。达到一定水平后,我们也可以自己写自己的库。

调用函数

之前我们认识到函数会有返回值,函数名及参数。对于定义一个函数或写主函数,这些都是必不可少的。

但在调用函数时,我们只用像数学一般,直接调用,输入参数即可使用。如果我们要获取其返回值,我们只用将其赋值给变量即可。假如该函数无参数需要输入,那也要带上括号,让编译器知道这是个函数。

1
2
a = pow(10, 6);
printf("Hello World");

printf

printf的作用

printf的字面意思就是打印,即将文字输出到输出缓冲区(一段系统特定的内存)中,表现为输出文字。

格式控制符

printf拥有多个参数,具体数目根据格式控制符所定。如下所示:

1
2
3
printf("result = %+d", sum);
printf("average = %#4.2lf, pow = %#d", avg, num);
printf("该方程无解");

观察就可以知道所谓的格式控制符,就是 %??? , 一般而言,格式控制符的格式如下:

%[标志][输出最小宽度][.精度][类型长度]类型

方框[]的部分表示可选。

之所以要有标识符,其实是告诉程序应该在哪打印变量,没有标识符他就会打印出字符,而非变量的值。

标志符
标 志意义
-结果左对齐,右边填空格
0输出的字符长度不够时,用0补全
+输出符号(正号或负号)
空格输出值为正时冠以空格,为负时冠以负号
#对c、s、d、u类无影响; 对o类,在输出时加前缀o; 对x类,在输出时加前缀0x; 对e、g、f 类当结果有小数时才给出小数点。

对于标志符,现实中少用到,了解即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**标志符 - 和 0 放在第二部分最小宽度处演示**/

/**标志符 + 演示**/
int num1 = 666, num2 = -233;
printf("|%+d|%+d|", num1, num2);
/**输出结果**/
|+666|-233|

/**标志符 空格 演示**/
int num1 = 666, num2 = -233;
printf("|% d|% d|", num1, num2);
/**输出结果**/
| 666|-233|

/**标志符 ## 演示**/
int num1 = 170; 
float num2 = 66.233, num3 = 77;
printf("|%x|%f|%f|\n");
printf("|%#x|%#f|%#f|\n");
/**输出结果**/
|aa|66.233002|115|
|0xaa|66.233002|0115|
//对于num2为什么不是66.233,这是由于浮点型存储方式造成的,更多内容请自行搜索。
最小宽度

用于控制变量输出的长度,对于长度不足的字符,用空格补全。但在标志符中用 0 可以使其用0补全。

1
2
3
4
int num = 666;
printf("|%5d|%-5d|%05d|", num, num, num);
/**输出结果**/
|  666|666  |00666|
精度

精度主要用于控制小数的位数,遵循四舍五入的原则。

1
2
3
4
float num = 66.3456;
printf("|%1.1f|%.2f|", num, num);
/**输出结果**/
|66.3|66.35|
类型长度

长度格式符为 hl 两种,h表示按短型量输出,l表示按长型量输出。如果长度格式符与变量类型不匹配,则可能会导致错误。如:%ld 表示输出long型, %lf 表示输出double型。

1
2
3
4
5
long num = 6666666;
printf("|%d|%hd|%ld|", num, num, num);
/**输出结果**/
|6666666|-18006|6666666|
//可以观察到中间的数值发生了数据溢出
类型

类型表示一种输出格式,但其也需要与变量类型对应,否则会出错。如:char 对应 c ,字符串对应 s ,整形对应 d ,浮点数对应 f

格式字符意义
d以十进制形式输出带符号整数(正数不输出符号)
o以八进制形式输出无符号整数(不输出前缀0)
x,X以十六进制形式输出无符号整数(不输出前缀Ox)
u以十进制形式输出无符号整数
f以小数形式输出单、双精度实数
e,E以指数形式输出单、双精度实数
g,G以%f或%e中较短的输出宽度输出单、双精度实数
c输出单个字符
s输出字符串

这里仅需记住常用的即可,如:整型、小数、字符串。用法相信经过上面的例子演示,大家也会使用了。这里就不再演示。

转义字符

转义字符的存在,是因为C语言占用了一些字符来表示特定含义,也包括一些无法通过键盘输入的字符或命令。值得注意的是,转义字符也是字符。

转义字符 意义 ASCLL码值(十进制)
\a 响铃(BEL) 007
\b 退格(BS) ,将当前位置移到前一列 008
\f 换页(FF),将当前位置移到下页开头 012
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置) 009
\v 垂直制表(VT) 011
\' 代表一个单引号 039
\" 代表一个双引号字符 034
\ 代表一个反斜线字符''' 092
? 代表一个问号 063
\0 空字符(NUL) 000
\ddd 1到3位八进制数所代表的任意字符 三位八进制
\xhh 十六进制所代表的任意字符 十六进制

scanf

scanf的作用

scanf是用于读取输入缓冲区的函数,表现为读取输入的内容,包括字符串和数字等。是较为常用的读取函数。

scanf的用法

与printf类似,有多个参数,相同的格式。但不同的是,读取数值或单个字符时,需要加上取地址符 &

1
2
3
4
5
int num1 = 0;
char *string1;
char string2[10] = {0};
scanf("%d", &num1);
scanf("%s%s", string1, string2);

还有一个需要注意的地方是,scanf是严格的,即scanf会严格匹配输入的内容。

而且scanf不会读取空格,一旦出现空格,它会认为读取完成(对于读取字符串也是如此)。因此对于读取字符串,我们常用gets而不是scanf,除非是单个单词或者单个字符。

1
2
3
4
5
6
scanf("%d,%d", &num1, &num2);
//在此应该输入:13,14
//而不能输入:66 233
scanf("%s", string1);
//在这里不能输入:I Love C Language
//否则scanf只会读取I

对于读取多个数据时,scanf会以空格,TAB(缩进),回车,或者非法字符(即与数据类型不相符的数据)作为该数据结束标志。

1
2
3
4
5
scanf("%d%d%d", &num1, &num2, &num3);
printf("%d, %d, %d", num1, num2, num3);
//输入:123 666A233
/**输出结果**/
123, 666, 0

格式控制符

对于scanf的格式控制符如下:

%[标志][输入最大宽度][类型长度]类型

方框 [] 的部分表示可选。

标志符

scanf的标志符据我了解,只有 * ,用于表示该输入项,读入后不赋予相应的变量,即跳过该输入值。

1
2
3
4
5
scanf("%d%*d%d", &num1, &num2);
printf("%d, %d", num1, num2);
//分别输入:12 13, 14
/**输出结果**/
12, 14
输入最大宽度

限制输入的数字或字符串的最大长度。

1
2
3
4
5
6
7
8
scanf("%2d", &num1);
while(getchar() != '\n');
scanf("%3s", string1);
printf("%d, %s", num1, string1);
//输入:12345
//再输入:Hello!
/**输出结果**/
12, Hel
类型长度和类型

这两项与printf完全相同,在此就再重复了。

高级格式控制符

持续更新中…

gets 与 puts

gets 与 puts 也是一组读取输出的函数,他们与scanf和printf不同的是,他们只能处理字符串,参数只有一个,即字符串。值得注意的是gets与scanf不同,gets会读取空格,同时也不用清空输入缓冲区。

1
2
gets(string1);
puts("666");

getchar 与 putchar

getchar 与 putchar 也是一组读取输出的函数,但只能读取和输出单个字符。getchar无参数(不要忘了括号),putchar的参数是一个字符。

1
2
3
4
char ch1 = 0;
ch1 = getchar();
putchar(ch1);
putchar('6');

需要知道的是,虽然我们不常用这两个函数读取或输出字符,但我们常用getchar清空输入缓冲区。

清空缓存区主要是因为用户有时候可能会多输入,不仅如此,对于scanf它读取完内容后会遗留一个回车。如果不清空缓存区,下一次就会直接读取缓冲区的内容,而不会暂停等待用户输入。

所以每使用一次scanf,尽量清空一次缓冲区,这是一个良好的习惯。

1
2
3
4
//读取到文件结尾后才结束,可以用Ctrl + Z 手动生成文件结尾标志
while(getchar() != EOF);
//读取到回车后结束
while(getchar() != '\n');

选择判断与布尔运算

布尔运算

布尔运算也称逻辑运算,即只有“真”与“假”的运算。左值与右值满足条件则为真,否则为假。

运算符 意义
== 等于
!= 不等于
>= 大于等于
<= 小于等于
> 大于
< 小于
&&
||
!
^ 异或

值得注意的是,布尔运算也有优先级。

运算优先级总结

优先级

运算符

名称或含义

使用形式

结合方向

说明

1

[]

数组下标

数组名[常量表达式]

左到右

--

()

圆括号

(表达式)/函数名(形参表)

--

.

成员选择(对象)

对象.成员名

--

->

成员选择(指针)

对象指针->成员名

--

 

2

-

负号运算符

-表达式

右到左

单目运算符

~

按位取反运算符

~表达式

++

自增运算符

++变量名/变量名++

--

自减运算符

--变量名/变量名--

*

取值运算符

*指针变量

&

取地址运算符

&变量名

!

逻辑非运算符

!表达式

(类型)

强制类型转换

(数据类型)表达式

--

sizeof

长度运算符

sizeof(表达式)

--

 

3

/

表达式/表达式

左到右

双目运算符

*

表达式*表达式

%

余数(取模)

整型表达式%整型表达式

4

+

表达式+表达式

左到右

双目运算符

-

表达式-表达式

5

<< 

左移

变量<<表达式

左到右

双目运算符

>> 

右移

变量>>表达式

 

6

大于

表达式>表达式

左到右

双目运算符

>=

大于等于

表达式>=表达式

小于

表达式<表达式

<=

小于等于

表达式<=表达式

7

==

等于

表达式==表达式

左到右

双目运算符

=

不等于

表达式!= 表达式

 

8

&

按位与

表达式&表达式

左到右

双目运算符

9

^

按位异或

表达式^表达式

左到右

双目运算符

10

|

按位或

表达式|表达式

左到右

双目运算符

11

&&

逻辑与

表达式&&表达式

左到右

双目运算符

12

||

逻辑或

表达式||表达式

左到右

双目运算符

 

13

?:

条件运算符

表达式1?

表达式2: 表达式3

右到左

三目运算符

 

14

=

赋值运算符

变量=表达式

右到左

--

/=

除后赋值

变量/=表达式

--

*=

乘后赋值

变量*=表达式

--

%=

取模后赋值

变量%=表达式

--

+=

加后赋值

变量+=表达式

--

-=

减后赋值

变量-=表达式

--

<<=

左移后赋值

变量<<=表达式

--

>>=

右移后赋值

变量>>=表达式

--

&=

按位与后赋值

变量&=表达式

--

^=

按位异或后赋值

变量^=表达式

--

|=

按位或后赋值

变量|=表达式

--

 

15

逗号运算符

表达式,表达式,…

左到右

--

 总结:  同一优先级的运算符,运算次序由结合方向所决定。  简单而言:  ! > 算术运算符 > 关系运算符 > && > || > 赋值运算符

选择结构

if 选择结构

对于 if 选择结构由基本的三个语句构成,分别是:if(条件)else if(条件)else 。选择结构必须有 if,但其他两个视情况而定。选择语句可以无限嵌套,简称套娃。但如果 if 选择结构超过了五层,请你好好思考,你的程序设计得合不合理。

以下是常见的 if 选择结构语句。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
if (条件)//条件即布尔运算
{
	代码
}
else if (条件)
{
	代码
}
...//n个else if语句
else
{
	代码
}

程序先判断 if 的条件,如果满足则执行 if 中的代码,并跳出选择结构执行选择结构后的代码;如果不满足 if 的条件,则再判断第一个 else if 的条件,满足则执行其中的代码并跳出;如此判断下去,如果 ifelse if 的条件都不满足,则直接执行 else 的代码。else 其实就相当于程序的保底。

对于选择结构,如果只有单句代码需要执行,则可以去掉大括号,但别忘了加上分隔符。

1
2
if (i > 0) printf("6");
else printf("233");

switch 选择结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
switch(判断的整型变量)
{
	case 对应的值1:
		代码
		break;
	case 对应的值2:
		代码
		break;
	...//n个case后
	default:
		代码
		break;
}

循环语句与结构

循环是程序之所以广泛应用的根本,毫不夸张的说,循环把现代人从许多重复的工作中解脱,极大解放了生产力。现代的编程语言,基本都有选择结构和循环结构,并且他们的语句无比惊人的相同,基本每个语言都有 if 判断、for 循环、while 循环。

但循环虽好,他也有一些需要注意的问题。如果掉以轻心,你可能会写出一个死循环,无限运行循环内的代码,导致计算机死机。

for 循环

对于一个循环,我们很容易想出,要想循环不进入死循环,只需要初始化一个计数变量,用于记录触发的次数,再对计数变量进行判断即可。

下面是 for 循环的基本结构

1
2
3
4
5
6
7
8
9
for (初始化代码;循环条件;单次循环结束执行的代码)
{
	所要重复执行的代码
}

//例子
for (int i = 0; i < 6; i++)
	printf("6");
//该代码会重复执行六次输出6的代码

同样的,for 循环如果只需执行单个语句,也可以把大括号去掉。

初始化代码

顾名思义就是就是在进行for循环前,执行的代码,通常用于初始化计数变量。在例子中,我便初始化了一个i变量用于计数,当然你也可以像平常初始化变量那样初始化多个变量用于计数,你也可以什么都不写,但不能少了分隔符。

1
2
3
4
for (int i = 0, j = 0; i < 6; i++)
{
	printf("6");
}

值得注意的是,对于部分老式编译器,不支持在for循环中进行变量的声明,尤其是在学校的机房里~~(说的就是你大工)~~。这个时候就要将变量的声明放在main函数的下面。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
int main(void)
{
	int i;

	printf("233\n");
	
	for (; i < 6; i++)
	{
		printf("6");
	}
	
	for (i = 0; i < 233; i++)
	{
		printf("9");
	}
}

循环条件

循环条件同选择结构的条件一般,即布尔运算。满足条件则继续循环,否则结束循环。

单次循环结束执行的代码

之所以要有这一步,主要是用于防止循环进入死循环。

观察上述例子我们可以发现,我们通常将计数变量递增作为该部分的代码,用来限制循环的次数。

当然啦,你也可以不写,直接空出来,但你就要在循环体内的代码的结尾手动写上。除非你很清楚自己在干什么。

while 选择结构

了解了for循环,那学会while循环更是易如反掌。他其实就相当于一个不填"初始化代码"和"单次循环结束要执行代码"的for循环。

其实就相当于自己要另外实现这两个功能罢了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
while (循环条件)
{
	所要重复执行的代码
}

//例子
int i = 0;
while (i < 6)
{
	printf("6");
	i++;
}

do...while... 循环

do...while... 循环其实就是先执行一边再判断的 while 循环,与while循环并无太大不同。

别忘了 while 后有分号

1
2
3
4
do
{
	所要重复执行的代码
}while(循环条件);

数组

对于一维声明数组时初始化,可以不用标明元素个数。对于方括号内的值,仅能为正整数常量及其表达式。

多维数组

对于n维数组,至少要定义n-1维的长度,且定义的维度必须为低维度(即排后面的维度),否则无法初始化。

数组与字符串


函数

对于函数而言,它可以嵌套调用,但不能嵌套定义。

返回值

不论return所返回的值的类型是什么,返回值都会转化为函数类型相同的类型。


字符串处理

strcpy函数

strcat函数


文件处理

fopen函数

FILE *fopen(const char *filename, const char *mode)

模式 描述
“r” 打开一个用于读取的文件。该文件必须存在。
“w” 创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
“a” 追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。
“r+” 打开一个用于更新的文件,可读取也可写入。该文件必须存在。
“w+” 创建一个用于读写的空文件。
“a+” 打开一个用于读取和追加的文件。
“rb” 打开一个用于读取的二进制文件,该文件必须存在

参考资料

C语言输出格式控制符汇总

转义字符对照表

C语言运算符优先级(超详细)

C语言格式输入函数scanf()详解

scanf()函数中高级格式控制符用法

菜鸟教程——C语言

Licensed under CC BY-NC-SA 4.0
使用 Hugo 构建
主题 StackJimmy 设计