前言
C语言(英语:C Language)是一种通用的、过程式编程编程语言,支持结构化编程、词法作用域和递归,使用静态类型系统,并且广泛用于系统软件与应用软件的开发。(维基百科)
编译器
- 预处理:展开头文件、替换宏、删除注释,生成
.i文件
- 编译:将
.i文件转为汇编代码.s文件
- 汇编:将
.s文件转为机器码目标文件.o(仅包含当前源文件的机器码,无外部依赖解析)
- 链接:将当前
.o文件、依赖的系统库(如libc)、其他.o文件(若多个`.c文件一起编译)进行链接,解析所有外部符号(如printf、全局变量、跨文件函数),最终生成可执行文件
-o <file>:指定输出的文件存放路径
-std=c89:使用c89规范编译
仅预处理
- 预编译会把引入的头文件的源码直接引入
- 预编译会去除注释
仅编译
编译生成可执行文件
1
| gcc <file_1>.c <file_1>.c
|
查看库
头文件
引入头文件
系统目录下的头文件
当前目录下的头文件
查看引入什么头文件
Linux
Windows
1
| printf site:learn.microsoft.com
|
头文件存储位置
Linux
Windows
Visual Studio(MSVC)
- 标准C库:
C:\Program Files (x86)\Microsoft Visual Studio\<ver>\<Edition>\VC\Tools\MSVC\<toolset-ver>\include
- WindowsSDK:
C:\Program Files (x86)\Windows Kits\10\Include\<sdk-ver>\ucrt
MinGW(轻量GCC)
- 64位
<root>\mingw64\include
<root>\mingw64\x86_64-w64-mingw32\include
<root>\mingw64\lib\gcc\x86_64-w64-mingw32\<ver>\include
- 32位:
<root>\mingw32\i686-w64-mingw32\include
Cygwin(完整GCC)
- Cygwin根:
<cygwin-root>\usr\include
- 32位GCC:
<cygwin-root>\usr\i686-pc-cygwin\include
- 64位GCC:
<cygwin-root>\usr\x86_64-pc-cygwin\include
主函数
1 2 3 4 5 6 7
| int main() {
...
return 0; }
|
注释
- 行注释是从C++继承过来的,现在也可以在C语言中使用
执行操作系统命令
<command>:在当前操作系统下的命令
输出语句
putchar输出字符
1 2 3
| char 变量名;
putchar(变量名);
|
puts输出字符串
1 2 3
| char 变量名[数组长度] = {0};
puts(变量名);
|
fputs输出字符串
1 2 3
| char 变量名[数组长度] = {0};
fputs(变量名, stdout);
|
printf格式化输出
1 2 3
| char 变量名[数组长度] = {'1', 0};
printf("文本内容%s", 变量名);
|
| 格式说明符 |
数据类型 |
描述 |
%hd |
short int |
短整型数据 |
%hu |
unsigned short int |
无符号短整型数据 |
%d、%i |
int |
有符号十进制整型数据 |
%u |
unsigned int |
无符号十进制整型数据 |
%o |
unsigned int |
无符号八进制整型数据 |
%x |
unsigned int |
无符号十六进制整型数据,字母小写 |
%X |
unsigned int |
无符号十六进制整型数据,字母大写 |
%ld |
long |
有符号十进制长整型数据(32位或64位) |
%lu |
unsigned long |
无符号十进制长整型数据(32位或64位) |
%lo |
unsigned long |
无符号八进制长整型数据(32位或64位) |
%lx |
unsigned long |
无符号十六进制长整型数据,字母小写(32位或64位) |
%lX |
unsigned long |
无符号十六进制长整型数据,字母大写(32位或64位) |
%lld |
long long |
有符号十进制长整型数据(64位) |
%llu |
unsigned long long |
无符号十进制长整型数据(64位) |
%llo |
unsigned long long |
无符号八进制长整型数据(64位) |
%llx |
unsigned long long |
无符号十六进制长整型数据,字母小写(64位) |
%llX |
unsigned long long |
无符号十六进制长整型数据,字母大写(64位) |
%f |
float、double |
单精度浮点型数据或双精度浮点型数据 |
%e |
double |
双精度浮点型数据,科学记数法,字母小写 |
%E |
double |
双精度浮点型数据,科学记数法,字母大写 |
%c |
char |
字符型数据 |
%s |
char* |
单字节字符串型数据 |
%S |
wchar_t* |
宽字节字符串型数据 |
%p |
void* |
十六进制指针数据 |
%% |
- |
输出一个百分号 |
输入语句
输入字符
输入字符串
fgets输入字符串
- 遇到回车符(
\n)、文件结束符(EOF),达到数组长度会自动结束
- 换行符会作为输入
- 如果输入超出字符串长度,不会报错,会立即结束
1 2 3
| char 变量名[数组长度] = {0};
fgets(变量名, sizeof(变量名), stdin);
|
scanf格式化输入
- 遇到空白字符会自动结束
- 换行符不会作为输入
- 如果输入超出字符串长度,会报错
1 2 3
| char 变量名[数组长度] = {0};
scanf("%s", &变量名);
|
报错
- 在VisualStudio中,
scanf被认为是不安全的,所以使用scanf会报错
解决问题
方法一
1
| #define _CRT_SECURE_NO_WARNINGS
|
方法二
1
| #pragma warning(disable:4996)
|
变量
声明变量
定义变量
变量赋值
常量
定义宏常量
- C语言通常使用define定义常量
- 宏常量必须在文件头部定义
- 定义宏常量末尾不需要添加
;
定义const常量
- C++通常使用const定义常量
- const常量在函数内定义
基本数据类型
整型
| 类型 |
描述 |
占用空间 |
short、signed short |
有符号短整型 |
2字节 |
unsigned short |
无符号短整型 |
2字节 |
int、signed int |
有符号整型 |
4字节 |
unsigned int |
无符号整型 |
4字节 |
long、signed long |
有符号长整型 |
4字节(Windows系统、32位操作系统)、8字节(64位操作系统) |
unsigned long |
无符号长整型 |
4字节(Windows系统、32位操作系统)、8字节(64位操作系统) |
long long、signed long long |
有符号长长整型 |
8字节 |
unsigned long long |
无符号长长整型 |
8字节 |
1 2 3 4 5 6
| signed int 变量名 = 1; unsigned int 变量名 = 1U; signed long 变量名 = 1L; unsigned long 变量名 = 1UL; signed long long 变量名 = 1LL; unsigned long long 变量名 = 1ULL;
|
进制表示
1
| unsigned short binary = 0b1;
|
1
| unsigned short octal = 07;
|
1
| unsigned short hexadecimal = 0xF;
|
浮点类型
| 类型 |
描述 |
占用空间 |
float、signed float |
有符号单精度浮点型数据 |
4字节 |
unsigned float |
无符号单精度浮点型数据 |
4字节 |
double、signed double |
有符号双精度浮点型数据 |
8字节 |
unsigned double |
无符号双精度浮点型数据 |
8字节 |
long double、signed long double |
有符号长双精度浮点型数据 |
12字节 |
unsigned long double |
无符号长双精度浮点型数据 |
16字节 |
字符型
在C语言中没有1字节的整型数据类型,所以用字符型代替
获取数据占用内存空间
1
| unsigned int 变量名 = sizeof(变量)
|
字符串型(字符数组)
- 字符串在内存中是一段连续的char空间,以
\0结尾
运算符
算数运算符
+(正号)、-(负号)
+(加号)、-(减号)、*、/、%
++(自增)、--(自减)
关系运算符
返回1表示真,返回0表示假
>、<、>=、<=、==、!=
逻辑运算符
赋值运算符
=、+=、-=、*=、/=、%=
&=、|=、^=、<<=、>>=
按位运算符
内存地址运算符
逗号运算符
,(先计算,左边的表达式,后计算,右边的表达式,但最终只返回,右边的表达式结果)
分支语句
if语句
1 2 3 4
| if (布尔表达式) { 布尔表达式成立时执行的代码; }
|
1 2 3 4 5 6 7 8
| if (布尔表达式) { 布尔表达式成立时执行的代码; } else { 布尔表达式不成立时执行的代码; }
|
1 2 3 4 5 6 7 8 9 10 11 12
| if (布尔表达式1) { 布尔表达式1成立时执行的代码; } else if (布尔表达式2) { 布尔表达式2成立时执行的代码; } else { 以上布尔表达式都不成立时执行的代码; }
|
1 2 3 4 5 6 7
| if (布尔表达式1) { if (布尔表达式2) { 布尔表达式1和2都成立时执行的代码; } }
|
switch语句
- C语言的
switch语句具有穿透行为,在每个case语句中,必须使用break跳出seitch语句
1 2 3 4 5 6 7 8 9 10 11 12
| switch (变量名) { case 值1: 变量值为值1时执行的代码 break; case 值2: 变量值为值2时执行的代码 break; defautl: 变量值不为以上任何值时执行的代码 break }
|
循环语句
while 语句
1 2 3 4
| while (布尔表达式) { 布尔表达式成立时反复执行的代码; }
|
do…while 语句
1 2 3 4
| do { 布尔表达式成立时反复执行的代码; } while (布尔表达式);
|
for 语句
1 2 3 4
| for (int i = 起始值; 布尔表达式; i++) { 布尔表达式成立时反复执行的代码; }
|
break关键字
continue关键字
1 2 3 4
| while (1) { continue; }
|
三元运算
1
| 数据类型 变量名 = 布尔表达式 ? 布尔表达式成立时返回的结果 : 布尔表达式不成立时返回的结果;
|
跳转语句
终止程序
存储类型修饰符
- 类型限定符和存储类型修饰符可以同时修饰同一变量,且没有顺序要求
auto
- 通过
auto关键字修饰的变量是自动变量,自动变量在函数调用时创建,函数使用完后销毁
- 代码块内定义的变量一定都是自动变量,
auto关键字可以省略
static
- 通过
static关键字修饰的变量和函数是静态的,静态变量和静态函数只初始化一次,在程序加载到内存的时候就创建,直到程序运行结束才销毁
- 全局静态变量只能在定义它的文件内使用
- 全局静态函数只能在定义它的文件内使用
1 2 3 4 5 6 7 8 9 10 11 12
| static 数据类型 全局静态变量名 = 值;
static void 全局静态函数 { ... }
int main() { static 数据类型 局部静态变量名 = 值; return 0; }
|
register
- 通过
register关键字修饰变量,向编译器发起建议(取决于是否满足条件),将变量放到寄存器中
extern
1 2 3 4 5 6
| int num;
void fn() { ... }
|
1 2 3
| extern int num;
extern void fn();
|
类型限定符
- 类型限定符和存储类型修饰符可以同时修饰同一变量,且没有顺序要求
const
- 通过
const关键字修饰的变量是只读的,被当作常量使用
volatile
- 当变量被频繁改变时,编译器会自动优化,从寄存器中读写变量的值
- 通过
volatile关键字修饰变量,禁止编译器优化,强制访问内存
堆内存
- 一个程序的栈大小是有限的,如果一个数组特别大时,会导致栈溢出,所以不要在栈里面定义太大的数组,此时可以使用堆来存数据
- 如果一个数组的长度不能确定的时候,适合用堆而不是用栈存储数据
堆内存开辟空间
- Windows堆内存最小页为4k
- 如果用户在Windows上开辟1k空间,实际上Windows会开辟4k空间,将其中1k空间分给用户,用户再次开辟空间时,Windows不会再开辟新的空间,而是直接将剩余空间给用户使用,直到4k空间全部分配完毕,当用户仍然需要开辟空间时,Windows才会给用户开辟新的空间
- Windows上这种开辟空间的方式,优点是效率更高,缺点是会浪费内存
开辟一块空间
<num>:空间大小,单位字节
1
| 数据类型 *变量名 = malloc(<num>);
|
开辟多块空间
malloc()函数只负责开辟空间,需要通过memset()函数手动初始化(清空原始数据)
<num> * sizeof(int):一共开辟的空间大小
1 2
| int *变量名 = malloc(<num> * sizeof(int)); memset(变量名, 0, <num> * sizeof(int));
|
calloc()函数不仅负责开辟空间,还会自动进行数据的初始化
<num>:开辟的空间个数
sizeof(int):每一块空间的大小
1
| int *变量名 = calloc(<num>, sizeof(int));
|
自动变量
- 如果存储为自动变量,在手动释放后仍然可以使用,但是释放后再次使用时,是一个新的堆空间,而且使用完仍然需要手动释放。释放的不是变量,而是指针指向的堆内的空间
1
| auto 数据类型 *变量名 = malloc(<num>);
|
堆内存释放空间
堆内存重新分配开辟的空间
- 使用malloc或calloc开辟的空间需要调整时,可以使用realloc
- realloc开辟的空间也需要手动清除数据
变量名:想要修改空间的变量(返回的变量可以不相同,但通常设置为相同,因为旧的变量不需要自己维护)
1
| 变量名 = realloc(变量名, 新指定的内存大小);
|
完成