00.环境配置
0.1 VsCode配置C/C++环境
参考:
https://www.bilibili.com/video/BV19c411G7ey/?p=1&spm_id_from=pageDriver&vd_source=d1fd4bcc46805ab35cc8bbb5a8bf318f
(1)安装VsCode;
(2)VsCode汉化;
在这里插入图片描述
(3)安装gcc编译器MinGW;
①安装;
②配置环境变量gcc编译器环境变量:
③验证环境变量是否配置好;
(4)配置C环境
①安装C/C++扩展
②配置任务,选择编译器
配置"tasks.json"文件
{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: gcc.exe 生成活动文件",
"command": "F:\\Qt\\Qt6.4.2\\Qt6.4.2\\Tools\\mingw1120_64\\bin\\gcc.exe",
"args": [
"-fdiagnostics-color=always",
"-g",
// "${file}", //默认,注释
"*.c", //增加,使得当前文件夹下可编译多个".c"文件
"-o",
// "${fileDirname}\\${fileBasenameNoExtension}.exe" //生成的可执行文件名称命名方式
"${fileDirname}\\a.exe" //修改后生成的可执行文件命名为"a.exe"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "编译器: F:\\Qt\\Qt6.4.2\\Qt6.4.2\\Tools\\mingw1120_64\\bin\\gcc.exe"
}
]
}
(5)修改调试配置文件
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) 启动",
"type": "cppdbg",
"request": "launch",
// "program": "输入程序名称,例如 ${workspaceFolder}/a.exe",
"program": "${fileDirname}\\a.exe", //需要调试的可执行程序,与tasks.json文件保持一致
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
// "miDebuggerPath": "/path/to/gdb",
"miDebuggerPath": "F:\\Qt\\Qt6.4.2\\Qt6.4.2\\Tools\\mingw1120_64\\bin\\gdb.exe", //gdb位置,与gcc位于同一路径下
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
01.第1阶段:C语言基础
第01章 C语言概述
1.1 什么是C语言
通俗地讲,语言是人和人交流,C语言是人和机器交流。
1.2 C语言优缺点
1.2.1 C语言优点
(1)代码量小;
(2)执行速度快;
(3)功能其强大;
(4)编程自由;
1.2.2 C语言缺点
(1)写代码实现周期长;
(2)可移植性较差;
(3)过于自由,经验不足易出错;
(4)对平台库依赖较多;
1.3 C语言应用领域
1.4 C语言的简洁
C语言仅有32个关键字,9种控制语句,34种运算符,却能完成无数的功能:
1.5 第1个C语言程序:HelloWorld!
/*
*文件名称:HelloWorld.c
*编码:utf-8
*功能:输出“HelloWorld!”
*作者:xuhc
*编写日期:2024.06.28
*/
#include<stdio.h>
int main()
{
//这是第一个C语言代码
printf("HelloWorld!\n");
return 0;
}
1.5.1 通过gcc编译c代码
编译命令格式:
gcc [-option1]...<filename>
g++ [-option1]...<filename>
命令、选项和源文件之间使用空格分隔;
一行命令中可以有零个、一个或多个选项;
文件名可以包含文件的绝对路径,也可以使用相对路径;
如果命令中不包含输出可执行文件的文件名,可执行文件的文件名会自动生成一个默认名,Linux平台为a.out,Windows平台为a.exe。
gcc、g++编译常用选项说明:
选项 | 含义 |
-o file | 指定生成的输出文件名为file |
-E | 只进行预处理 |
-S(大写) | 只进行预处理和编译 |
-c(小写) | 只进行预处理、编译和汇编 |
1.5.2 代码分析
(1)include头文件
#include的意思是头文件包含,#include <stdio.h>代表包含stdio.h这个头文件;
使用C语言库函数需要提前包含库函数对应的头文件,如这里使用了printf()函数,需要包含stdio.h头文件;
可以通过man 3 printf查看printf所需的头文件;
#include< > 与 #include "“的区别:
< > :表示系统直接按系统指定的目录检索
“” :表示系统先在 “” 指定的路径(没写路径代表当前路径)查找头文件,如果找不到,再按系统指定的目录检索
(2)main函数
一个完整的C语言程序,是由一个、且只能有一个main()函数(又称主函数,必须有)和若干个其他函数结合而成(可选)。
main函数是C语言程序的入口,程序是从main函数开始执行。
(3){} 括号,程序体和代码块
{}叫代码块,一个代码块内部可以有一条或者多条语句
C语言每句可执行代码都是”;"分号结尾
所有的#开头的行,都代表预编译指令,预编译指令行结尾是没有分号的
所有的可执行语句必须是在代码块里面
(4)注释
//叫行注释,注释的内容编译器是忽略的,注释主要的作用是在代码中加一些说明和解释,这样有利于代码的阅读
/**/叫块注释
块注释是C语言标准的注释方法
行注释是从C++语言借鉴过来的
(5)printf函数
printf是C语言库函数,功能是向标准输出设备输出一个字符串。
printf(“hello world\n”);//\n的意思是回车换行
(6)return语句
return代表函数执行完毕,返回return代表函数的终止;
如果main定义的时候前面是int,那么return后面就需要写一个整数;如果main定义的时候前面是void,那么return后面什么也不需要写;
在main函数中return 0代表程序执行成功,return -1代表程序执行失败;
int main()和void main()在C语言中是一样的,但C++只接受int main这种定义方式。
1.6 system函数
1.6.1 system函数的使用
#include<stdlib.h>
int system(const char *command)
/*
功能:在已经运行的程序中执行另外一个外部程序
参数:外部可执行程序名字
返回值:
成功:0
失败:任意数字
*/
示例
/*
*文件名称:system_function.c
*编码:utf-8
*功能:测试system函数,启动计算器
*作者:xuhc
*编写日期:2024.03.25
*/
#include<stdio.h>
#include<stdlib.h>
// int system(const char *command)
/*
功能:在已经运行的程序中执行另外一个外部程序
参数:外部可执行程序名字
返回值:
成功:0
失败:任意数字
*/
int main()
{
system("calc"); //Windows平台
// system("ls"); //Linux平台,需要头文件<stdlib.h>
return 0;
}
1.7 C语言编译过程
1.7.1 C程序编译步骤
C代码编译成可执行程序经过4步:
1)预处理:宏定义展开、头文件展开、条件编译等,同时将代码中的注释删除,这里并不会检查语法
2)编译:检查语法,将预处理后文件编译生成汇编文件
3)汇编:将汇编文件生成目标文件(二进制文件)
4)链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去
1.7.2 gcc编译过程
(1)分步编译
预处理:
gcc -E hello.c -o hello.i
编译:
gcc -S hello.i -o hello.s
汇编:
gcc -c hello.s -o hello.o
链接:
gcc hello.o -o hello
选项 | 含义 |
-o file | 指定生成的输出文件名为file |
-E | 只进行预处理 |
-S(大写) | 只进行预处理和编译 |
-c(小写) | 只进行预处理、编译和汇编 |
################################################ | |
文件后缀 | 含义 |
.c | C语言文件 |
.i | 预处理后的C语言文件 |
.s | 编译后的汇编文件 |
.o | 编译后的目标文件 |
(2)一步编译
gcc hello.c -o demo(还是经过:预处理、编译、汇编、链接的过程)
1.7.3 查找程序所依赖的动态库
Windows平台下,需要相应软件(Depends.exe):
1.8 CPU内构与寄存器部结
1.8.1 64位和32位系统区别
寄存器是CPU内部最基本的存储单元
CPU对外是通过总线(地址、控制、数据)来和外部设备交互的,总线的宽度是8位,同时CPU的寄存器也是8位,那么这个CPU就叫8位CPU
如果总线是32位,寄存器也是32位的,那么这个CPU就是32位CPU
有一种CPU内部的寄存器是32位的,但总线是16位,准32为CPU
所有的64位CPU兼容32位的指令,32位要兼容16位的指令,所以在64位的CPU上是可以识别32位的指令
在64位的CPU构架上运行了64位的软件操作系统,那么这个系统是64位
在64位的CPU构架上,运行了32位的软件操作系统,那么这个系统就是32位
64位的软件不能运行在32位的CPU之上
1.8.2 寄存器名字
8位 | 16位 | 32位 | 64位 |
A | AX | EAX | RAX |
B | BX | EBX | RBX |
C | CX | ECX | RCX |
D | DX | EDX | RDX |
1.8.3 寄存器、缓存、内存三者关系
按与CPU远近来分,离得最近的是寄存器,然后缓存(CPU缓存),最后内存。
CPU计算时,先预先把要用的数据从硬盘读到内存,然后再把即将要用的数据读到寄存器。于是 CPU<—>寄存器<—>内存,这就是它们之间的信息交换。
那为什么有缓存呢?因为如果经常操作内存中的同一址地的数据,就会影响速度。于是就在寄存器与内存之间设置一个缓存。
因为从缓存提取的速度远高于内存。当然缓存的价格肯定远远高于内存,不然的话,机器里就没有内存的存在。
由此可以看出,从远近来看:CPU〈—〉寄存器〈—> 缓存 <—> 内存。
1.9汇编语言
1.9.1 VS中C语言嵌套汇编代码
#include <stdio.h>
int main()
{
//定义整型变量a, b, c
int a;
int b;
int c;
__asm
{
mov a, 3 //3的值放在a对应内存的位置
mov b, 4 //4的值放在b对应内存的位置
mov eax, a //把a内存的值放在eax寄存器
add eax, b //eax和b相加,结果放在eax
mov c, eax //eax的值放在c中
}
printf("%d\n", c);//把c的值输出
return 0;//成功完成
}
1.9.2 反汇编
#include <stdio.h>
int main()
{
//定义整型变量a, b, c
int a;
int b;
int c;
a = 3;
b = 4;
c = a + b;
printf("%d\n", c);//把c的值输出
return 0;//成功完成
}
(1)设置断点F9
(2)选择反汇编按钮
(3)根据汇编代码分析程序
1.10 集成开发环境IDE
集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。所有具备这一特性的软件或者软件套(组)都可以叫集成开发环境。
1.10.1 Qt Creator
常用快捷键
快捷键 | 含义 |
Ctrl + i | 自动格式化代码 |
Ctrl + / | 注释/取消注释 |
Alt + Enter | 自动完成类函数定义 |
F4 | .h文件和对应.cpp 文件切换 |
F9 | 设置断点 |
F5 | 调试运行 |
Ctrl + r | 编译,但不调试运行 |
Ctrl + b | 编译,不运行 |
F10 | next调试 |
F11 | step调试 |
1.10.2 Microsoft Visual Studio
(1)常用快捷键
快捷键 | 含义 |
Ctrl + k,Ctrl + f | 自动格式化代码 |
Ctrl + k,Ctrl + c | 注释代码 |
Ctrl + k,Ctrl + u | 取消注释代码 |
F9 | 设置断点 |
F5 | 调试运行 |
Ctrl + F5 | 不调试运行 |
Ctrl + Shift + b | 编译,不运行 |
F10 | next调试 |
F11 | step调试 |
(2)Visual Studio的C4996错误 | |
由于微软在Visual Studio中不建议再使用C的传统库函数scanf,strcpy,sprintf等,所以直接使用这些库函数会提示C4996错误: |
VS建议采用带_s的函数,如scanf_s、strcpy_s,但这些并不是标准C函数。
要想继续使用此函数,需要在源文件中添加以下指令就可以避免这个错误提示:
#define _CRT_SECURE_NO_WARNINGS //这个宏定义最好要放到.c文件的第一行
#pragma warning(disable:4996) //或者使用这个
第02章 数据类型
2.1 常量与变量
2.1.1 关键字
2.1.2 数据类型
数据类型的作用:编译器预算对象(变量)分配的内存空间大小。
2.1.3 常量
常量:
在程序运行过程中,其值不能被改变的量
常量一般出现在表达式或赋值语句中
整型常量 | 100,200,-100,0 |
实型常量 | 3.14 , 0.125,-3.123 |
字符型常量 | ‘a’,‘b’,‘1’,‘\n’ |
字符串常量 | “a”,“ab”,“12356” |
2.1.4 变量
(1)变量
变量:
在程序运行过程中,其值可以改变;
变量在使用前必须先定义,定义变量前必须有相应的数据类型;
标识符命名规则:
标识符不能是关键字;
标识符只能由字母、数字、下划线组成;
第一个字符必须为字母或下划线;
标识符中字母区分大小写;
变量特点:
变量在编译时为其分配相应的内存空间;
可以通过其名字和地址访问相应内存;
(2)声明和定义区别
声明变量不需要建立存储空间,如:extern int a;
定义变量需要建立存储空间,如:int b;
#include<stdio.h>
int main()
{
//extern关键字只做声明,不能做任何定义
//声明一个变量a,a在这里没有建立存储空间
extern int a;
a = 10; // err,没有空间,就不可以赋值
int b = 10; // 定义一个变量b,b的类型为int,b赋值为10
return 0;
}
从广义的角度来讲声明中包含着定义,即定义是声明的一个特例,所以并非所有的声明都是定义:
int b 它既是声明,同时又是定义
对于 extern b来讲它只是声明不是定义
一般的情况下,把建立存储空间的声明称之为“定义”,而把不需要建立存储空间的声明称之为“声明”。
2.1.5 使用示例
#include<stdio.h>
#define MAX 10 //声明了一个常量,名字叫MAX,值是10,常量的值一旦初始化不可改
int main()
{
int a; //定义了一个变量,其类型为int,名字叫a
const int b = 10; // 定义一个const常量,名为叫b,值为10
//b=11; //err,常量的值不能改变
//MAX = 100; //err,常量的值不能改变
a = MAX; //将a的值设置为MAX的值
a = 123;
printf("%d\n",a); //打印变量a的值
return 0;
}
2.2 整型:int
2.2.1 整型变量的定义和输出
打印格式 | 含义 |
%d | 输出一个有符号的10进制int类型 |
%o(字母o) | 输出8进制的int类型 |
%x | 输出16进制的int类型,字母以小写输出 |
%X | 输出16进制的int类型,字母以大写输出 |
%u | 输出一个10进制的无符号数 |
#include<stdio.h>
int main()
{
int a = 123; //定义变量a,以10进制方式赋值123
int b = 0567; //定义变量b,以8进制方式赋值0567
int c = 0xabc; //定义变量c,以16进制方式赋值为0xabc
printf("a=%d\n",a);
printf("8进制:b=%o\n",b);
printf("10进制:b=%d\n",b);
printf("16进制:c=%x\n",c);
printf("16进制:c=%X\n",c);
printf("10进制:c=%d\n",c);
unsigned int d = 0xffffffff; //定义无符号int变量d,以16进制方式赋值
printf("有符号方式打印:d=%d\n",d);
printf("无符号方式打印:d=%u\n",d);
return 0;
}
2.2.2 整型变量的输入
#include<stdio.h>
int main()
{
int a;
printf("请输入a的值:");
//不要加"\n"
scanf_s("%d",&a);
printf("a=%d\n",a); //打印a的值
return 0;
}
2.2.3 short、int、long、long long
数据类型 | 占用空间 |
short(短整型) | 2字节 |
int(整型) | 4字节 |
long(长整形) | Windows为4字节,Linux为4字节(32位),8字节(64位) |
long long(长长整形) | 8字节 |
注意:
需要注意的是,整型数据在内存中占的字节数与所选择的操作系统有关。虽然C语言标准中没有明确规定整型数据的长度,但 long 类型整数的长度不能短于 int 类型, short 类型整数的长度不能长于int类型。
当一个小的数据类型赋值给一个大的数据类型,不会出错,因为编译器会自动转化。但当一个大的类型赋值给一个小的数据类型,那么就可能丢失高位。
整型常量 | 所需类型 |
10 | 代表int类型 |
10l, 10L | 代表long类型 |
10ll, 10LL | 代表long long类型 |
10u, 10U | 代表unsigned int类型 |
10ul, 10UL | 代表unsigned long类型 |
10ull, 10ULL | 代表unsigned long long类型 |
打印格式 | 含义 |
%hd | 输出short类型 |
%d | 输出int类型 |
%ld | 输出long类型 |
%lld | 输出long long类型 |
%hu | 输出unsigned short类型 |
%u | 输出unsigned int类型 |
%lu | 输出unsigned long类型 |
%llu | 输出unsigned long long类型 |
#include<stdio.h>
int main()
{
short a = 10;
int b = 10;
long c = 10l; //或者10L
long long d = 10ll; //或者10LL
printf("sizeof(a)=%u\n",sizeof(a));
printf("sizeof(b)=%u\n",sizeof(b));
printf("sizeof(c)=%u\n",sizeof(c));
printf("sizeof(d)=%u\n",sizeof(d));
printf("short a = %hd\n",a);
printf("int b = %d\n",b);
printf("long c = %ld\n",c);
printf("long long d = % lld\n",d);
unsigned short a2 = 20u;
unsigned int b2 = 20u;
unsigned long c2 = 20ul;
unsigned long long d2 = 20ull;
printf("unsigned short a = %hu\n",a2);
printf("unsigned int b = %u\n",b2);
printf("unsigned long c = %lu\n",c2);
printf("unsigned long long d = %llu\n",d2);
return 0;
}
2.2.4 有符号数和无符号数区别
(1)有符号数
有符号数最高位为符号位,0代表正数,1代表负数。
#include<stdio.h>
int main()
{
signed int a = -1089474374; //定义有符号整型变量a
printf("%X\n",a); //结果为BF0FF0BA
//B F 0 F F 0 B A
//1011 1111 0000 1111 1111 0000 1011 1010
return 0;
}
(2)无符号数
无符号数最高位不是符号位,而就是数的一部分,无符号数不可能是负数。
1011 1111 0000 1111 1111 0000 1011 1010:
如果当无符号数看待,那么它是一个整数的源码
原码:1100 0000 1111 0000 0000 1111 0100 0110
C 0 F 0 0 F 4 6
十进制数:3236958022
#include<stdio.h>
int main()
{
unsigned int a = 3236958022; //定义无符号整型变量a
printf("%X\n",a); //结果为C0F00F46
return 0;
}
(3)有符号和无符号整型取值范围
数据类型 | 占用空间 | 取值范围 |
short | 2字节 | -32768 到 32767 (-2 |
int | 4字节 | -2147483648 到 2147483647 (-231 ~ 231-1) |
long | 4字节 | -2147483648 到 2147483647 (-231 ~ 231-1) |
unsigned short | 2字节 | 0 到 65535 (0 ~ 216-1) |
unsigned int | 4字节 | 0 到 4294967295 (0 ~ 232-1) |
unsigned long | 4字节 | 0 到 4294967295 (0 ~ 232-1) |
2.3 sizeof关键字
sizeof不是函数,所以不需要包含任何头文件,它的功能是计算一个数据类型的大小,单位为字节
sizeof的返回值为size_t
size_t类型在32位操作系统下是unsigned int,是一个无符号的整数
推荐课程:
https://xxetb.xetslk.com/s/vgpHh