首先,小编要来讲讲为什么要写追忆篇,编程本就需要日复一日练习,如果长时间手不碰键盘,最厉害的程序员都要从复习语法开始,所以为了让小编在未来忘记c语法的时候快速重拾起c语言的语法,也为了为现在在学习的C语言做个小小的笔记,小编这才下定决心开启这一篇章,所以如果这个篇章对大家有帮助那当然是最好,如果觉得太简略,还留小编一条狗命,毕竟这个只需要小编看懂就行(狗头保命),大家有问题直接评论区留言就好,小编会一一解答。
在这一章我们先来粗略了解一下C语言,更深入的留在后面几章进行讲解。
目录
一. 第一个C语言程序
#include <stdio.h>
int main()
{
printf("hello bit\n");
printf("he he\n");
return 0;
}
//解释:
//main函数是程序的入口
//一个工程中main函数有且仅有一个
二. 数据类型
在我们深入理解之前,我们先来看看C语言中有哪些类型(注意:下面的B是字节单位的简称,全称为Byte):
类型 | 含义 | 占据空间大小 |
char | 字符数据类型 | 1B |
short | 短整型 | 2B |
int | 整形 | 4B |
long | 长整型 | 4或8 B(取决于编译器和系统) |
long long | 更长的整形 | 8B |
float | 单精度浮点数 | 4B |
double | 双精度浮点数 | 8B |
三. 变量、常量
生活中的有些值是不变的(比如:圆周率,性别,身份证号码,血型等等)
有些值是可变的(比如:年龄,体重,薪资)。
不变的值,C语言中用常量的概念来表示,变得值C语言中用变量来表示。
3.1 变量的分类和定义
常见的变量分为两类:
(1)局部变量
(2)全局变量
定义例程如下:
#include <stdio.h>
int global = 2019;//全局变量
int main()
{
int local = 2018;//局部变量
//下面定义的global会不会有问题?
int global = 2020;//局部变量
printf("global = %d\n", global);
return 0;
}
3.2 变量的作用域和生命周期
(1)作用域:
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用
的
而限定这个名字的可用性的代码范围就是这个名字的作用域。
1. 局部变量的作用域是变量所在的局部范围。
2. 全局变量的作用域是整个工程。
(2)生命周期:
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段。
1. 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
2. 全局变量的生命周期是:整个程序的生命周期。
3.3 常量
C语言中的常量和变量的定义的形式有所差异。
C语言中的常量分为以下以下几种:
1.字面常量
2.const 修饰的常变量
3.#define 定义的标识符常量
4.枚举常量
例:
#include <stdio.h>
//举例
enum Sex
{
MALE,
FEMALE,
SECRET
};
//括号中的MALE,FEMALE,SECRET是枚举常量
int main()
{
//字面常量演示
3.14;//字面常量
1000;//字面常量
//const 修饰的常变量
const float pai = 3.14f; //这里的pai是const修饰的常变量
pai = 5.14;//是不能直接修改的!
//#define的标识符常量 演示
#define MAX 100
printf("max = %d\n", MAX);
//枚举常量演示
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET);
//注:枚举常量的默认是从0开始,依次向下递增1的
return 0;
}
注:
上面例子上的 pai 被称为 const 修饰的常变量, const 修饰的常变量在C语言中只是在语法层面限制了变量 pai 不能直接被改变,但是 pai 本质上还是一个变量的,所以叫常变量。
四. 字符串
4.1 字符串
"hello bit.\n"
这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。
注:字符串的结束标志是一个 \0 的转义字符。在计算字符串长度的时候 \0 是结束标志,不算作字符串内容。
五. 符号
5.1 转义字符
转义字符顾名思义就是转变意思,常常用于printf打印函数中,常规方法下无法打印就通过这种方式来打印。
转义字符 | 释义 |
\? | 在书写连续多个问号时使用,防止他们被解析成三字母词 |
\' | 用于表示字符常量' |
\“ | 用于表示一个字符串内部的双引号 |
\\ | 用于表示一个反斜杠,防止它被解释为一个转义序列符。 |
\a | 警告字符,蜂鸣 |
\b | 退格符 |
\f | 进纸符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | ddd表示1~3个八进制的数字。 如: \130 X |
\xdd | dd表示2个十六进制数字。 如: \x30 0 |
5.2 注释
1. 代码中有不需要的代码可以直接删除,也可以注释掉
2. 代码中有些代码比较难懂,可以加一下注释文字
注释有两种风格:
(1)C语言风格的注释 /*xxxxxx*/
缺陷:不能嵌套注释
(2)C++风格的注释 //xxxxxxxx
可以注释一行也可以注释多行
六. 选择(分支语句)语句
5.1 if....else
两种结果执行其一,if括号中条件为真执行if,为假执行else。
if(表达式)
{
语句1;
...
}
else{
语句2;
...
}
注意:若多个if...else语句套用,else是和它离的最近的if匹配的。if...else可以不加大括号,但当且仅当跟随执行的语句只有一条时。
5.2 switch
本语句也可称为多分支语句,根据给予switch小括号的值,选择大括号内的不同情况进行执行。
switch(整型表达式)
{
分支项1;
分支项2;
分支项3;
分支项4;
}
以上定义的示例不是很容易理解,我们直接上代码示例:
#include <stdio.h>
int main()
{
int day = 0;
scanf("%d",&day);
switch(day)
{
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期天\n");
break;
default:
printf("输入错误");
}
return 0;
}
(1)case
相当于执行的入口,当给day赋值时,程序会按case后方跟随的入口标志进入该分支开始执行程序。
(2)break
break语句用于提前结束代码块的执行,可以终止循环或switch语句中的一个case。在嵌套循环中,break语句只能跳出最内层循环。
在这里,我要重点说明一下break,由特定case的入口进入分支之后,程序不遇到break,是不会停止执行的,也就是说如果一个分支后面如果没有break,会接着执行下一个分支,直至遇到break为止或switch中剩余所有语句都执行完,default也会被执行,所以case才被我称为入口而非选项。
注意:break是C语言中的一个关键字。
(3)default
这个语句就相当于除了switch中指定的几个case分支情况其余所有情况,该语句可以省略,非必要出现,值得注意的是没有break,其后语句也和正常case后语句一样会被执行。
注意:switch的()中的变量只能使用整形(int)或字符型(char)变量,其余类型都不行。
六. 循环语句
有些事必须一直做,比如我日复一日的讲课,比如大家,日复一日的学习,这些要重复执行的就是循环。
前面我们学了if,当条件满足的情况下,if语句后的语句执行,否则不执行。
但是这个语句只会执行一次,为了实现更多次,下面我就来为大家介绍C语言实现循环的三种语句。
6.1 while
while是C语言中的一个循环语句,用于在满足特定条件时重复执行一段代码。
当条件表达式为真(非零)时,循环体内的代码将被执行。每次执行完循环体后,条件表达式会被重新计算,如果仍然为真,则继续执行循环体内的代码。当条件表达式变为假(零)时,循环结束。
//while 语法结构
while(表达式)
{
循环语句;
...
}
使用范例代码:
//在屏幕上打印1-10的数字。
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
printf("%d ", i);
i = i+1;
}
return 0;
}
6.1.1 while语句中的break和continue
(1) break
break在while循环中的作用:
其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。
所以:while中的break是用于永久终止循环的。
(2) continue
continue在while循环中的作用就是:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,
而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
注意:continue跳过一次循环,break终止循环,且break可在switch中使用用以终止执行,而continue在switch使用无意义。
6.1.1 while循环的执行流程:
6.2 for
for同样是是C语言中的一个循环语句,用于在满足特定条件时重复执行一段代码。
其基本语法如下:
for (初始化表达式; 条件表达式; 更新表达式) {
// 需要重复执行的代码块
}
其中,初始化表达式是在进入循环之前执行一次的表达式,用于设置循环变量的初始值;条件表达式是一个布尔表达式,用于判断是否继续执行循环体内的代码;更新表达式是在每次循环结束时执行的表达式,用于更新循环变量的值。
需要注意的是,for循环中的条件表达式和更新表达式也可以省略,此时循环将一直执行,直到遇到break语句或程序结束。
使用示范:
#include <stdio.h>
int main()
{
int i = 0;
//for(i=1/*初始化*/; i<=10/*判断部分*/; i++/*调整部分*/)
for(i=1; i<=10; i++)
{
printf("%d ", i);
}
return 0;
}
6.2.1 break和continue在for循环中
我们发现在for循环中也可以出现break和continue,他们的意义和在while循环中是一样的。
上方我已介绍过了,这里就不在详述。
6.2.2 for循环的执行流程图:
注意:
1. 尽量不要在for 循环体内修改循环变量,防止 for 循环失去控制。
2. 建议for语句的循环控制变量的取值采用“前闭后开区间”写法。
6.3 do...while
do...while同样是C语言中的一个循环语句,用于在满足特定条件时重复执行一段代码。
其基本语法如下:
do {
// 需要重复执行的代码块
} while (条件表达式);
6.3.1 do while循环中的break和continue
他们的意义和在while循环中是一样的,上方我已介绍过了,这里就不在详述。
6.3.2 do...while的执行流程图:
注意:与for和while循环不同的是,do...while循环至少会执行一次循环体内的代码,然后再根据条件表达式判断是否继续执行。如果条件表达式为真(非零),则继续执行循环体内的代码;如果条件表达式为假(零),则跳出循环。
七. 函数
这里我们只大致讲一下函数概念,更具体的还是要大家去自己查找和学习,其中奥妙只有自行体悟才行有所收获。
7.1 函数定义
数学中我们常见到函数的概念。但是你了解C语言中的函数吗?
维基百科中对函数的定义:子程序
(1)在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,
subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
(2)一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
7.2 函数分类
(1)库函数
库函数是把一些常用到的函数编完放到一个文件里,供不同的人进行调用的一种方式。这些函数一般会被放到lib文件里。在C语言中,库函数的使用非常普遍和重要。例如,ctype、time、stdio、stdlib、math、string等都是常用的库函数。当程序员需要使用某个库函数时,只需要在其源文件中用#include<>指令包含相应的头文件即可,如要使用数学库函数,就需要在代码中加入#include<math.h>。
以下是一个C语言库函数查找网站,大家可以去学习一下。
这里我就不在详细叙述。
(2)自定义函数
简单来说,就是自己写的或他人写的非库函数。
7.3 函数的参数
常用的有两类
(1)实际参数(实参):
有确定值
(2)形式参数(形参):
无确定值
7.4 函数调用
(1)传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
(2)传址调用
1.传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
2.这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
7.5 函数的声明和定义
(1)声明
函数要保证先声明后使用
(2)定义
函数的定义是指函数的具体实现,交待函数的功能实现。
7.6函数的嵌套调用和链式访问
函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
(1)嵌套调用
一个函数中调用另一个函数。
例:
#include <stdio.h>
void new_line()
{
printf("hehe\n");
}
void three_line()
{
int i = 0;
for(i=0; i<3; i++)
{
new_line();
}
}
int main()
{
three_line();
return 0;
}
注:函数可以嵌套调用,但是不能嵌套定义。
(2)链式访问
把一个函数的返回值作为另外一个函数的参数。
例:
#include <stdio.h>
#include <string.h>
int main()
{
char arr[20] = "hello";
int ret = strlen(strcat(arr,"bit"));//这里介绍一下strlen函数
printf("%d\n", ret);
return 0;
}
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
7.7 函数递归
程序调用自身的编程技巧称为递归( recursion)。
递归的主要思考方式在于:把大事化小
递归的必要条件:
(1) 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
(2) 每次递归调用之后越来越接近这个限制条件。
递归示例:
/*
接受一个整型值(无符号),按照顺序打印它的每一位。
例如:
输入:1234,输出 1 2 3 4
*/
#include <stdio.h>
void print(int n)
{
if(n>9)
{
print(n/10);
}
printf("%d ", n%10);
}
int main()
{
int num = 1234;
print(num);
return 0;
}
八. 数组
数组是一组相同类型元素的集合。
8.1 一维数组
创建方式:
type_t arr_name [const_n];
//type_t 是指数组的元素类型
//const_n 是一个常量表达式,用来指定数组的大小
例:
int arr1[10];
8.2 二维数组
例:
/数组初始化
int arr[3][4] = {1,2,3,4};
int arr[3][4] = {{1,2},{4,5}};
int arr[][4] = {{2,3},{4,5}};//二维数组如果有初始化,行可以省略,列不能省略
8.3 数组使用
例:
for(i=0; i<10; i++)//这里写10,好不好?
{
arr[i] = i;
}
1. 数组是使用下标来访问的,下标是从0开始。
2. 数组的大小可以通过计算得到。
8.4 数组越界
数组的下标是有范围限制的。
数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查。
九.操作符
9.1 操作符优先级表:
操作 符 | 描述 | 用法示例 | 结果类 型 | 结合 性 | 是否控制求值 顺序 |
() | 聚组 | (表达式) | 与表达 式同 | N/A | 否 |
() | 函数调用 | rexp( rexp ,...,rexp) | rexp | L-R | 否 |
[ ] | 下标引用 | rexp[rexp] | lexp | L-R | 否 |
. | 访问结构成员 | lexp.member_name | lexp | L-R | 否 |
-> | 访问结构指针成员 | rexp->member_name | lexp | L-R | 否 |
++ | 后缀自增 | lexp ++ | rexp | L-R | 否 |
-- | 后缀自减 | lexp -- | rexp | L-R | 否 |
! | 逻辑反 | ! rexp | rexp | R-L | 否 |
~ | 按位取反 | ~ rexp | rexp | R-L | 否 |
+ | 单目,表示正值 | + rexp | rexp | R-L | 否 |
- | 单目,表示负值 | - rexp | rexp | R-L | 否 |
++ | 前缀自增 | ++ lexp | rexp | R-L | 否 |
-- | 前缀自减 | -- lexp | rexp | R-L | 否 |
* | 间接访问 | * rexp | lexp | R-L | 否 |
& | 取地址 | & lexp | rexp | R-L | 否 |
sizeof | 取其长度,以字节 表示 | sizeof rexp sizeof(类 型) | rexp | R-L | 否 |
(类 型) | 类型转换 | (类型) rexp | rexp | R-L | 否 |
* | 乘法 | rexp * rexp | rexp | L-R | 否 |
/ | 除法 | rexp / rexp | rexp | L-R | 否 |
% | 整数取余 | rexp % rexp | rexp | L-R | 否 |
+ | 加法 | rexp + rexp | rexp | L-R | 否 |
- | 减法 | rexp - rexp | rexp | L-R | 否 |
<< | 左移位 | rexp << rexp | rexp | L-R | 否 |
>> | 右移位 | rexp >> rexp | rexp | L-R | 否 |
> | 大于 | rexp > rexp | rexp | L-R | 否 |
>= | 大于等于 | rexp >= rexp | rexp | L-R | 否 |
< | 小于 | rexp < rexp | rexp | L-R | 否 |
<= | 小于等于 | rexp <= rexp | rexp | L-R | 否 |
操作 符 | 描述 | 用法示例 | 结果类 型 | 结合 性 | 是否控制求值 顺序 |
== | 等于 | rexp == rexp | rexp | L-R | 否 |
!= | 不等于 | rexp != rexp | rexp | L-R | 否 |
& | 位与 | rexp & rexp | rexp | L-R | 否 |
^ | 位异或 | rexp ^ rexp | rexp | L-R | 否 |
| | 位或 | rexp | rexp | rexp | L-R | 否 |
&& | 逻辑与 | rexp && rexp | rexp | L-R | 是 |
|| | 逻辑或 | rexp || rexp | rexp | L-R | 是 |
? : | 条件操作符 | rexp? rexp : rexp | rexp | N/A | 是 |
= | 赋值 | lexp = rexp | rexp | R-L | 否 |
+= | 以...加 | lexp += rexp | rexp | R-L | 否 |
-= | 以...减 | lexp -= rexp | rexp | R-L | 否 |
*= | 以...乘 | lexp *= rexp | rexp | R-L | 否 |
/= | 以...除 | lexp /= rexp | rexp | R-L | 否 |
%= | 以...取模 | lexp %= rexp | rexp | R-L | 否 |
<<= | 以...左移 | lexp <<= rexp | rexp | R-L | 否 |
>>= | 以...右移 | lexp >>= rexp | rexp | R-L | 否 |
&= | 以...与 | lexp &= rexp | rexp | R-L | 否 |
^= | 以...异或 | lexp ^= rexp | rexp | R-L | 否 |
|= | 以...或 | lexp |= rexp | rexp | R-L | 否 |
, | 逗号 | rexp , rexp | rexp | L-R | 是 |
十. 常见关键字
11.1 常见关键字速览
auto break case char const continue default do double else enum
extern float for goto if int long register return short signed
sizeof static struct switch typedef union unsigned void volatile while
11.2 typedef
类型重命名
11.3 static
static是用来修饰变量和函数的
1. 修饰局部变量-称为静态局部变量
2. 修饰全局变量-称为静态全局变量
3. 修饰函数-称为静态函数
十一. define 定义常量和宏
//define定义标识符常量
#define MAX 1000
//define定义宏
#define ADD(x, y) ((x)+(y))
#include <stdio.h>
......
十二. 指针
指针理解的2个要点:
1. 指针是内存中一个最小单元的编号,也就是地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
总结:1.指针就是地址,口语中说的指针通常指的是指针变量
2.指针是用来存放地址的,地址是唯一标示一块地址空间的。
3.指针的大小在32位平台是4个字节,在64位平台是8个字节。
12.1 常见指针类型
char *pc ;
int *pi ;
short *ps ;
long *pl ;
float *pf ;
double *pd ;
还有不少像结构体指针,数组指针,二级指针等等我就不在此详细展开。
12.2 指针的操作
(1)指针+- 整数
指针指向地址改变
(2)指针-指针
获得两指针在在地址上相差多少。
(3)指针的关系运算
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
十三. 结构体
结构体使得C语言有能力描述复杂类型。
比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。
定义案例例:
struct Stu
{
char name[20];//名字
int age; //年龄
char sex[5]; //性别
char id[15]; //学号
};
结构体初始化案例:
/打印结构体信息
struct Stu s = {"张三", 20, "男", "20180101"};
//.为结构成员访问操作符
printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);
//->操作符
struct Stu *ps = &s;
printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps-
>id);
到这c语言的知识就算整体的看了一遍,当然,这些讲的还不够细致,只不过给各位大佬回顾一下,作为绪论,任务已经完成的很出色了,更多细节我会在后续篇章中详述。