C语言笔记2
发布于 2026-04-06更新于 2026-11-1414345 字98 分钟 · 阅读时间
2023-8-27 ~ 2023-9-7
因为大多数编译器等都是由 C 语言编写而成的,并且大多遵循 C 语言的语法。如C++,java,c#等
作为系统编程语言可以编写底层(系统内核,驱动等);
作为程序语言是过程语言(即要一步步写下解决问题的步骤);
作为结构化编程语言(创造函数,将程序模块化);
作为中级编程语言(既支持低层次语言(可转化为汇编代码(机器语言)),又有高级语言的特性)
注:编译语言c++与解释语言java(可类比)
编译型语言相当于提前把你的中国话翻译成一本英语书,你下次运行程序就不要源码了,直接对着英语书就好了。但是换个德国人(换个平台)就看不懂英语书了。
解释型语言相当于个多国翻译官,但是他不给你写书,每次只帮你一句一句翻译,源码不能丢,运行效率低。
| auto | continue | double | for | int | signed | struct | void |
|---|---|---|---|---|---|---|---|
| break | const | else | if | long | static | switch | while |
| case | do | enum | goto | register | sizeof | typedef | volatile |
| char | default | extern | float | return | short | union | unsigned |
| C99 新增的: | _Imaginary(数据类型) | _Complex(数据类型) | _Bool(数据类型) | inline(函数修饰符) | |||
| C11 新增的: | _Alignas | _Alignof | _Atomic | _Generic | _Noreturn | _Static_assert | |
| _Thread_local |
像是printf等是在 C 语言的标准库函数之一,是个函数
自己给变量、函数、结构等创造的名称叫标识符,其不能等于保留字
命名规则:字母/数字/下划线;其中首字母不能为数字;尽量不超过 31 个字符;最好有意义
变量:保存数据的小房子(容器)
字面量:小房子里的的数,也可直接使用(如1、2.5、'c' )
整数: 十进制: 0, -9, 22
八进制: 021, 077, 033 (八进制以 0 开头)
十六进制: 0x7f, 0x2a, 0x521 (十六进制以 0x 开头)
浮点数:小数或科学计数法的数(如-2.0、-0.22E-5)
注:E-5 = 10-5
字符:ascii 字符,要在单引号内(如:'a''2')
转义序列:不能通过键盘输入的或有特殊含义的字符(如问号,制表符)
| 转义序列 | 字符 |
|---|---|
| \b | 退格符 |
| \f | 换页符 |
| \n | 换行符 |
| \r | 回车符 |
| \t | 水平制表符 |
| \v | 垂直制表符 |
| \ | 反斜杠 |
| ' | 单引号 |
| " | 双引号 |
| ? | 问号 |
| \0 | 空字符 |
"good")常量:不能更改的量;(习惯将常量用大写字母定义,用以区分)(const 常量)
const+ 变量的保留字 (如:const double PI = 3.14;)
| 类型 | 数据类型 |
|---|---|
| 基本数据类型 | int, char, float, double |
| 派生数据类型 | array, pointer, structure, union |
| 枚举数据类型 | enum |
| Void 数据类型 | void |
用于定义小房子的类型与大小
| 数据类型 | 存储大小 | 范围 | 数据类型 | 存储大小 | 范围 |
|---|---|---|---|---|---|
| char | 1 字节 | -128 到 127 | short int | 2 字节 | -32,768 到 32,767 |
| signed char | 1 字节 | -128 到 127 | signed short int | 2 字节 | -32,768 到 32,767 |
| unsigned char | 1 字节 | 0 到 255 | unsigned short int | 2 字节 | 0 到 65,535 |
| short | 2 字节 | -32,768 到 32,767 | long int | 4 字节 | -2,147,483,648 到 2,147,483,647 |
| signed short | 2 字节 | -32,768 到 32,767 | signed long int | 4 字节 | -2,147,483,648 到 2,147,483,647 |
| unsigned short | 2 字节 | 0 到 65,535 | unsigned long int | 4 字节 | 0 到 4,294,967,295 |
| int | 2 或 4 字节 | -32,768 到 32,767 | float | 4 字节 | |
| signed int | 2 或 4 字节 | -32,768 到 32,767 | double | 8 字节 | |
| unsigned int | 2 或 4 字节 | 0 到 65,535 | long double | 10 字节 |
int(integer 整数) 用以声明一个或多个整数变量
int a,b,aaa;float 和 double(float 单精度浮点数)(double float 双精度浮点数)(float 浮动 ==> 浮点数,即小数)
用于保存实数与小数
float normalizationFactor = 22.442e2;double price;//注:e可大写也可小写char声明字符类型(character 字符)
char test = 'h';void无类型或空类型;一般用于表示函数的返回类型,用以表示函数什么都不返回
用于以下三种情况:
函数返回为空:
C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);
函数参数为空:
C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);
指针指向 void:
类型为 void *的指针代表对象的地址,而不是类型。例如:内存分配函数 void *malloc( size_t size );返回指向void 的指针,可以转换为任何数据类型。
short和longshort 和 long 用于表示较大的数字与较较大的数字
long a; /*储存大整数*/ | short d; /*储存小整数*/long long b; /*储存大整数*/ |long double c; /*储存浮点数*/ |signed和unsignedsigned 和 unsigned (有符号的(整数,正数也可以有加号) 和 没符号的(非负数))
unsigned int x;其他数据类型:枚举型呀等等
派生数据类型:数组,指针,函数类型,结构等等
(I/O)是“输入/输出”的缩写
printf()(print 打印;输出)
//头文件,用于引入外部库 [炒菜工具]#include<iostream>//主函数(main 主要的)[炒菜动作]int main() //可以被解释为“最后返回一个整数”的意思{ //用于输出语句 printf("I love Sakurakugu"); //返回一个0,说明程序正常运行结束,它是可选的 return 0;}printf("Number = %d", testInteger);//引号内的%d将替换为testInteger的值输出浮点数:
要打印float,我们使用%f格式说明符。同样,我们还是要了%lf打印double值。
(注:lf=long float)
输出字符:
要打印 char,我们使用%c 格式说明符。
scanf()(scan 扫描;输入(一定不要忘记加&))
输入整数:scanf("%d", &a);
其中的&是取址符,
用于获取小房子 a 的地址,让scanf以%d(int格式)将输入储存进去
输入浮点数:
float 和 double 我们分别使用%f和%lf格式说明符
当然%lf也可以用于float的输入输出
输入字符:
用%c 格式说明符
字符本身不会被存储。而是存储一个整数值(ASCII 值)。
当我们使用%c,将显示输入的字符。如果用%d,则将其 ASCII 值打印出来。
输入多个值:
scanf("%d%f", &a, &b); //输入整数a、浮点数bscanf("%d %f", &a, &b); //输入整数a、空格、浮点数bauto存储类(用于局部变量(代码块{}中)
已被废弃,但作为保留字,auto 仍然存在于 C 语言的语法规范中。以防止用户将其作为标识符来使用。
int a; <==> auto int a; //因为加不加auto都不影响,所以被废弃了register存储类(用于局部变量)
将变量放在寄存器(cpu 中的内存)中,而非 rom(内存)中,所以不用取址符&(因为它没有内存位置)
在现代的编译器中,很多情况下,编译器会自动进行寄存器分配和优化,将频繁使用的变量存储在寄存器中,因此手动使用 register 存储类的效果有限。
还有编译器是否实际将变量存储在寄存器中是由编译器决定的,它可能会根据具体情况忽略 register 关键字。
register int miles;static存储类(用于全局变量(外部变量)、局部变量、静态变量)
static int count = 12;extern存储类(用于全局变量)
只能定义,用于告诉编译器,这有个小房子,但不能初始化小房子中的数;
extern int globalVar;单行注释:“双斜杠//”同一行内以后的内容都是注释
多行注释: “斜杠星号/*......*/”中都是注释,可以多行,但不能嵌套
//打印信息/*打印信息*/| 转换说明 | 输 出 | 转换说明 | 输 出 |
|---|---|---|---|
| %a | 浮点数、十六进制数字和 p-记数法 (C99) | %G | 根据数值不同自动选择%f 或者%E。%E 格式在指数小于-4 或者大于等于精度时使用 |
| %A | 浮点数、十六进制数字和 P-记数法 (C99) | %i | 有符号十进制整数 (与%d 相同) |
| %c | 一个字符 | %o | 无符号八进制整数 |
| %d | 有符号十进制整数 | %p | 指针(/地址) |
| %e | 浮点数、e-记数法 | %s | 字符串 |
| %E | 浮点数、E-记数法 | %u | 无符号十进制整数 |
| %f | 浮点数,十进制记数法 | %x | 使用十六进制数字 0f 的无符号十六进制整数 |
| %g | 根据数值不同自动选择%f 或者%e。%e 格式在指数小于-4 或者大于等于精度时使用 | %X | 使用十六进制数字 0F 的无符号十六进制整数 |
| %% | 打印一个百分号 |
printf()修饰符| 修饰符 | 意 义 |
|---|---|
| 标志 | 五种标志 (-、+、空格、# 和 0) 都将在表三中描述,可以使用零个或多个标志 |
| digit(s) | 字段宽度的最小值。如果该字段不能容纳要打印的数或者字符串,系统会使用更宽的字段。示例:“%4d” |
| .digit(s) | 精度。对于%e、%E 和%f 转换,是将要在小数点的右边打印的数字的位数。对于%g 和%G 转换,是有效数字的最大位数。对于%s 转换,是将要打印的字符的最大数目。对于整数转换,是将要打印的数字的最小位数;如果必要,要使用前导零来达到这个位数。只使用“.”表示其后跟随一个零,所以%.f 与%.0f 相同。示例:“%5.2f”打印一个浮点数,他的字段宽度为 5 个字符,小数点后有两个数字。 |
| h | 和整数转换说明符一起使用,表示一个 short int 或者 unsigned short int 类型数值。示例:“%hu”、“%hx”和“%6.4hd” |
| hh | 和整数转换说明符一起使用,表示一个 signed char 或者 unsigned char 类型数值。示例:“%hhu”、“%hhx”和“%6.4hhd” |
| j | 和整数转换说明符一起使用,表示一个 intmax_t 或 uintmax_t 值。示例:“%jd”和“%8jX” |
| l | 和整数说明符一起使用,表示一个 long int 或者 unsigned long int 类型值。示例:“%ld”和“%8lu” |
| ll | 和整数说明符一起使用,表示一个 long long int 或 unsigned long long int 类型值 (C99)。示例:“%lld”和“%8llu” |
| L | 和浮点转换说明符一起使用,表示一个 long double 值。示例:“%Lf”和“%10.4Le” |
| t | 和整数转换说明符一起使用,表示一个 ptrdiff_t 值(与两个指针之间的差相对应的类型) (C99)示例:“%td”和“%12ti” |
| z | 和整数转换说明符一起使用,表示一个 size_t 值(sizeof 返回的类型) (C99)。示例:“%zd”和“%12zx” |
它们可以同时使用
| 修饰符 | 意 义 |
|---|---|
| - | 项目是左对齐的,也就是说,会把项目打印在字段的左侧开始处。示例:“%-20s” |
| + | 有符号的值若为正,则显示带加号的符号;若为负,则带减号的符号。示例:“%+6.2f” |
| (空格) | 有符号的值若为正,则显示时带前导空格(但是不显示符号);若为负,则带减号符号。+标志会覆盖空格标志。示例:“% 6.2f” |
| # | 使用转换说明的可选形式。若为%o 格式,则以 0 开始;若为%x 和%X 格式,则以 0x 或 0X 开始,对于所有的浮点形式,#保证了即使不限任何数字,也打印一个小数点字符。对于%g 和%G 格式,它防止尾随零被删除。示例:“%#o”、“%#8.0f”和“%+#10.3E” |
| 0 | 对于所有的数字格式,用前导零而不是用空格填充字段宽度。如果出现-标志或者指定了精度(对于整数)则忽略该标志。示例:“%010d”和“%08.3f” |
// 如: printf("%5d%5d\n",123,456); printf("%-5d%-5d\n",123,456); // 输出: 123 456 // 共要有五个字符,右对齐123 456 // 共要有五个字符,左对齐 scanf()的转换说明符| 转换说明符 | 意 义 | 转换说明符 | 意 义 |
|---|---|---|---|
| %c | 把输入解释成一个字符 | %o | 把输入解释成一个有符号八进制数 |
| %d | 把输入解释成一个有符号十进制整数 | %p | 把输入解释成一个指针(地址) |
| %e,%f,%g, %a | 把输入解释成一个浮点数 (%a 是 C99 标准) | %s | 把输入解释成一个字符串;输入的内容以一个非空白字符作为开始,并且包含知道下一个空白字符的全部字符 |
| %E,%F,%G,%A | 把输入解释成一个浮点数 (%A 是 C99 标准) | %u | 把输入解释成一个无符号十进制整数 |
| %i | 把输入解释成一个有符号十进制整数 | %x,%X | 把输入解释成一个有符号十六进制整数 |
scanf()的转换修饰符| 修饰符 | 意 义 |
|---|---|
| * | 滞后赋值。示例:“%*d” |
| digit(s) | 最大字段宽度;在达到最大字段宽度或者遇到第一个空白字符时(不管哪一个先发生都一样)停止对输入项的读取。示例:“%10s” |
| hh | 把整数读作 signed char 或 unsigned char 。示例:“%hhd”“%hhu” |
| ll | 把整数读作 long long 或者 unsigned long long (C99)。示例:“%lld”“%llu” |
| h,l 或 L | “%hd”和“hi”指示该值将会存储在一个 short int 中。“%ho”“%hx”和“%hu”指示该值将会存储在一个 unsigned short int 中。“%ld”和“%li”指示该值将会存储在一个 long 中。“%lo”“%lx”和“%lu”指示该值将会存储在一个 unsigned long 中。“%le”“%lf”和“%lg”指示该值以 double 类型存储。将 L(而非 l)与 e、f 和 g 一起使用指示该值以 long double 类型存储。如果没有这些修饰符,d、i、o 和 x 指示 int 类型,而 e、f 和 g 指示 float 类型。 |
将一种数据类型转换成另一种
语法:(type)value;
例子:
float a=9/4; //a=2.250000int b= (int)a; //b=2if...elseif( ){ }//如果小括号中的判别式为真(或非零值),则执行大括号中的语句else{ }//否则执行else中的语句注:可以不加 else(否则)部分;也可以嵌套 if 和 else;
如果if...else语句的主体只有一个语句,可以不需要使用方括号{ }。但最好加。
for循环for( ){ }(
初始化语句(仅执行一次);
评估测试表达式(如果命题为假,则终止循环);
更新表达式(一般用于改变循环控制变量的值)
)
{先执行测试完为真后,执行大括号中的语句;之后才执行更新表达式(如 i++)}
while和do...while循环while( ){ }//当小括号中为真,执行大括号中的内容,否则不执行 //其中int i=1;while (i<=100) <==> for(int i=1;i<=100;i++ ){ { 内容; 内容; i++; }}do{ }whlie( )//先执行,再判断真假,看看是否执行下一次//因此,它与while的区别是它至少会执行一次break和continue语句当读到break,立刻跳出循环(立刻结束当前所在的循环)
当读到continue,跳过当前迭代(的剩余代码),立刻进行下一个迭代(循环)
switch语句switch...case的语法:
switch (day){ case 1: printf("Monday\n"); // 语句 break; case 2: printf("Tuesday\n"); // 语句 break; ...... default: printf("Invalid day\n");// 默认语句(Invalid day无效的日期)}先检测小括号“day”的值,若 day=1(小括号的值与 case 的值匹配),
则选择 case 为“1”的代码块来执行,以此类推。
但如果都没有,则会执行默认的代码块(default 下的)。
当然,default 语句是可选的。
若 case 的语句最后没加 break;则会按顺序继续执行下一个 case 的代码块,
直到结束,这可能会产生意想不到的结果。
goto语句goto abc;……abc:通过goto,将直接跳转到标签abc处,并让程序从abc处开始执行,
标签abc可在goto前,也可在后;
一般用于打破嵌套循环,用到其他地方后,可能会让代码很难理解,最好少用
包含声明函数,调用函数,定义函数三个方面,其中声明和定义可以一起,只要在前面
标准库函数:即在 C 的头文件中声明的函数,如scanf(),gets(),floor()等等
用户定义的函数:字面意思,可用于缩减程序复杂性
若不想返回任何值,则用void
void hello(){ printf("hello c");}若是 int 则返回整数,若是 float 则返回浮点数,若是 char 则返回字符等等
| 序号 | 头文件 | 描述 |
|---|---|---|
| 1 | stdio.h | 这是一个标准的输入/输出头文件。它包含有关标准输入/输出的所有库函数。 |
| 2 | conio.h | 这是一个控制台输入/输出头文件。 |
| 3 | string.h | 它包含所有与字符串相关的库函数,例如gets(),puts()等。 |
| 4 | stdlib.h | 该头文件包含所有通用库函数,例如malloc(),calloc(),exit()等。 |
| 5 | math.h | 该头文件包含所有与数学运算相关的函数,例如sqrt(),pow()等。 |
| 6 | time.h | 该头文件包含所有与时间有关的函数。 |
| 7 | ctype.h | 该头文件包含所有字符处理函数。 |
| 8 | stdarg.h | 变量参数函数在此头文件中定义。 |
| 9 | signal.h | 所有信号处理函数都在此头文件中定义。 |
| 10 | setjmp.h | 该文件包含所有跳转函数。 |
| 11 | locale.h | 该文件包含语言环境函数。 |
| 12 | errno.h | 该文件包含错误处理函数。 |
| 13 | assert.h | 该文件包含诊断函数。 |
注:函数原型就是函数声明
函数声明就是函数定义的第一行
如:
int C(int a;int b){函数体} 这段中大括号前面的部分
调用函数的小括号中的将传给对应的函数,其中对应的函数中的参数叫形式参数(就是那个int a;int b;)
当然,若函数不需要参数,也就不用传递参数了
return语句return语句用于将函数(该程序)停止执行,并将“return”后的值返回给调用函数
return语句的语法:return 例子;
如:
return (a+b); return 0;注:返回值的类型也要匹配
调用自身的函数叫递归函数。并且,这种技术称为递归。
例:
void recurse(){ …… recurse(); ……} int main(){ …… recurse(); ……}//递归将继续进行,直到满足某些条件让递归终止递归使程序更优雅,但更慢,并常用于数据结构与算法中
数组是可以储存多个值的变量
(即多个小房子 如a[5]={1,100,2,5,44}等等)
| a[5]分为 | a[0] | a[1] | a[2] | a[3] | a[4] |
|---|---|---|---|---|---|
| \ |
数组的第一个索引(值)为 0,所以要访问最后一个元素,要使用 n-1;
假设起始地址mark[0]是 2120d,则mark[1]的地址将是 2124d,同样的,mark[2]的地址是 2128d 以此类推
初始化数组:
在声明期间可以初始化数组,
如:
int mark[5] = {19, 10, 8, 17, 9};//初始前五个数组,若数值(中括号中的)为5个就初始五个。若大括号的数多余五个,则五个后面的都省略或:
int mark[] = {19, 10, 8, 17, 9};//虽然没指定大小,但编译器会知道大小是5更改数组元素中的值:
用赋值号(等于号)
访问元素超过范围:
若定义a[10](即定义了 a[0]~a[9]),但尝试访问了 a[10]呀、a[12]呀等等,就会导致访问错误(栈错误)
二位数组:如:a[x][y]; 例:
a[4][3] |
a[x][0] |
a[x][1] |
a[x][2] |
a[x][3] |
|---|---|---|---|---|
a[0][x] |
a[0][0] |
a[0][1] |
a[0][2] |
a[0][3] |
a[1][x] |
a[1][0] |
a[1][1] |
a[1][2] |
a[1][3] |
a[2][x] |
a[2][0] |
a[2][1] |
a[2][2] |
a[2][3] |
三维数组:如:a[x][y][z];
初始二维数组:
int c[2][3] = {{1, 3, 0}, {-1, 5, 9}};int c[][3] = {{1, 3, 0}, {-1, 5, 9}};int c[2][3] = {1, 3, 0, -1, 5, 9};初始三维数组:
int test[2][3][4] = { {{03, 4, 02, 3}, {0, -3, 9, 11}, {23, 12, 23, 2}} , {{13, 4, 56, 3}, {5, 09, 3, 05}, {03, 01, 04, 9}} }; int test[2][3][4] = { {{03, 4, 02, 3}, {0, -3, 9, 11}, {23, 12, 23, 2}} ,
{{13, 4, 56, 3}, {5,09, 3, 05}, {03, 01, 04, 9}} };
例:a[10]
若要传递数组的一部分元素,只要传递“数组加中括号内的数”(传a[1]、a[3]等)
若要传递整个数组,直接传递数组名(传a)
当然,函数要的值也要是数组,如float calculateSum(float age[])
若是多维数组,也同理
但是,是不能从函数中返回数组的,除非用指针或结构体
指针是 C 语言和 C++中强大的功能 (ptr:pointer:指针)
对于变量 a,则用&a 给 a 赋予一个地址
(取址符&用于获取变量的地址,并返回指向该对象变量的指针)
指针(指针变量)是用于储存地址而非值的特殊变量
(用于储存别的小房子的门牌号而非小房子中的数的小房子,所以指针也可以储存指针)
指针语法:
int *ptr1, *ptr2;int* p; int *p;//int* p1, p2;声明的是一个指针p1和一个普通变量p2int *a,b=5; a=&b; //记住这第二个a没有星号int c; c=*a; //c的值就是5了//星号*相当于让程序获取地址指向的小房子的值而非地址的值*a=10; 或b=10; //这时b的值为10了//几种错误用法:int c, *pc;pc = c; //pc是地址,但c不是*pc = &c; //&c是地址,但* pc不是pc = &c; //&c和pc都是地址*pc = c; //c和*pc值 //但这样是可以的:int *p = &c; //这相当于:int *p;p = &c; //所以,为了避免这种情况,我们常用:int* p=&c;数组是一个顺序数据块
其中,a[n]的数组中,a与a[0]的地址相等,因为变量名x指向数组的第一个元素,即x[0]
所以,x[i]的地址等于x+i的
即&x[i]等同于x+i ; x[i]等同于*(x+i)。
例:
#include <stdio.h> //运行该程序时,输出为:int main() //输入6个数字: 2 3 4 4 12 4{ //总和 = 29 int i, x[6], sum = 0; printf("输入6个数字:"); for( =0;i<6 ++i) { scanf("%d", x+i); //等效于scanf("%d", &x[i]); sum += *(x+i); //等同于 sum += x[i] } printf("总和 = %d", sum); return 0;}例:
#include <stdio.h> //运行该程序时,输出为:int main() //*ptr = 3{ //*(ptr+1) = 4 int x[5] = {1,2,3,4,5}; //*(ptr-1) = 2 int* ptr;*(ptr-1) = 2 ptr = &x[2]; //ptr被分配了第三个元素的地址 printf("*ptr = %d \n", *ptr); //3 printf("*(ptr+1) = %d \n", *(ptr+1)); //4 printf("*(ptr-1) = %d", *(ptr-1)); //2 return 0;}所以说,*(x-i)也可以
将地址作为参数传递给函数,有叫“引用调用”
在主函数中的函数中用&x,在函数中接收*x
如:
#include<stdio.h> void aaaaa(int *n1){ printf("%d",*n1);} int main(){ int num1=5; aaaaa( &num1); return 0;} 输出为:5这个就是直接传递地址
如:
#include<stdio.h> void aaaaa(int *n1){ printf("%d",*n1);} int main(){ int num1=5; int* p=&num1; aaaaa( p); return 0;} 输出为:5这个就是传递指针变量
sizeof()运输符sizeof(size of:~的大小)
用于获取变量,数据类型,数组的大小(以字节为单位)
如:
int a=sizeof(x);printf(“%d”, sizeof(int) );sizeof(a[10]);注意:输出在不同的机器上可能会有所不同,例如在 32 位操作系统上将显示不同的输出,在 64 位操作系统上将显示相同数据类型的不同输出。
void指针指向void的指针便是可以指向任何数据类型的通用指针
因此可以将任何数据类型的地址分配给void指针,并且可以将void指针分配给任何类 型的指针,而无需执行任何显式的类型转换。
malloc和relloc函数返回的指针都是void*类型
由于void指针没有指定的类型信息,因此在使用它时需要进行类型转换,以确保正确地访问指针所指向的内存内容。如可以用(int*等等强制转换)
当使用void指针访问内存时需要小心类型错误和越界访问等问题。
为什么我们使用void指针?
如果函数需要接受多种类型的指针作为参数,可以使用 void 指针来实现
例:
#include<stdio.h>int main() //输出结果:{ //a的值为 : 56 int a=56; //初始化一个整数变量'a'。 //b的值为 : 4.500000 float b=4.5; //浮点变量'b'的初始化。 //c的值为 : k char c='k'; //字符变量'c'的初始化。 void *ptr; //声明空指针。 //为变量“a”分配地址。 ptr=&a; printf("a的值为 : %d",*((int*)ptr)); //为变量'b'分配地址。 ptr=&b; printf("\nb的值为 : %f",*((float*)ptr)); //为变量'c'分配地址。 ptr=&c; printf("\n c的值为 : %c",*((char*)ptr)); return 0;}用<stdlib.h>中的malloc(),calloc(),free(),realloc()
当编译器不能自动分配好内存时,用手动分配好内存就是动态分配内存
malloc()(主要用于分配单个元素)
malloc(memory allocation:内存分配)
使用 malloc()分配内存的一般步骤如下:
确定需要分配的内存大小。
调用 malloc() 函数,并传入要分配的内存大小作为参数。
检查分配是否成功,即检查返回的指针是否为 NULL。如果返回的指针为 NULL,则说明内存分配失败。
如果分配成功,则可以使用返回的指针来访问和操作分配的内存。
在不再需要使用分配的内存时,使用 free() 函数释放该内存,以便系统可以重新利用它。
例:
#include <stdio.h>#include <stdlib.h> int main() { int size = 5; int* numbers = (int*)malloc(size * sizeof(int)); //分配20字节的内存 (5x4(int四字节)) if (numbers == NULL){ //分配失败返回NULL指针 printf("内存分配失败\n"); return 1; } for (int i = 0; i < size; i++) { numbers[i] = i + 1; } for (int i = 0; i < size; i++) { printf("%d ", numbers[i]); } free(numbers); //释放内存 return 0;}在上述示例中,我们分配了一个可以容纳 5 个 int 类型元素的内存空间(1 个 20 字节)(或叫 1 个占用 5 个地址的内存空间)(或 n 个占用 4 个字节的内存空间),并通过指针 numbers 进行访问。
在使用完毕后,我们使用 free() 函数释放了这块动态分配的内存。
注:在使用 malloc() 或calloc()分配内存后,一定记得及时释放内存,以避免内存泄漏问题。主要是它们都不会自己释放内存
calloc()(主要用于分配数组)
calloc(contiguous allocation: 连续分配)
它会将内容初始化为 0
例:
ptr = (float*) calloc(25, sizeof(float)); //ptr为指针//这分配了25个可以容纳1个float类型元素的内存空间(25个4字节的空间)ptr = (float*) calloc(25, 3*sizeof(float)); //(25个12字节的空间)//若分配的ptr不是数组的起始地址,而是一个指针,那么后面将是连续的内存空间,可供您自由使用。具体内容取决于您在代码中对 ptr 以及后续内存空间的操作free()free(free:释放 -->释放内存)
语法:
free(ptr);//因为malloc,calloc都是返回指针变量,所以free清空的也是指针变量的内存指针可以是任何类型的变量realloc()realloc(reallocate:重新分配;再指派)
用于重新分配内存
语法:
ptr = realloc(ptr, x);例:
ptr = realloc(ptr, 5* sizeof(int));C 语言中,字符串是以null字符(即字符\0)结尾(\0的 ASCII 码为0)
char s[5]; //就是声明一个有五个字符的字符串,包括“\0”char c[] = "abcd";char c[50] = "abcd";char c[] = {'a','b','c','d','\0'};char c[5] = {'a','b','c','d','\0'};//若输入char c[4] = "abcd";则会数据溢出C 语言中不支持用赋值运算符(等于号)赋值
但可以用 strcpy,ctrncpy 来复制字符串
用scanf():
char name[20];scanf(“%s”,name);//当读到空格(空格,换行符,制表符等)就不读了若要读一整行,用fgets(),并可以用puts(),printf(“%s”,str)来显示字符串
也可以用gets(),但可能让数据溢出,所以不推荐使用(现已被删除)
fgets() (get string from file:从文件中获取字符串)
语法:
fgets(str, sizeof(str), stdin);puts() (puts: 字符串输出)
语法:
put (str);//它在输出后会返回”字符+\0”的数目,即字符数+1如:
char str[] = "Hello,world!";int count = puts(str); //count=13像数组一样直接传(即直接传字符串名字),接收时也像数组一样用str[]
如:
hanshu(char str[];)类似数组,字符串名字就是指针,用法也和数组一样
<string.h>中:
strlen() -计算字符串的长度
strcpy() -将字符串复制到另一个
strcmp() -比较两个字符串
strcat() -连接两个字符串
strlwr() 将字符串转换为小写
strupr() 将字符串转换为大写
C 语言结构体:
结构是 C 语言中另一种用户自定义的一种数据类型,可用于储存不同数据类型的数据
定义结构:
要用 struct 关键字,使用方法如下:
语法:
一、 | 二、struct 结构体名称 | struct 结构体名称{ | { 数据类型 成员1; | 数据类型 成员1; 数据类型 成员2; | 数据类型 成员2; // ... | // ...}; | }变量名1,变量名2;//这是只声明有一个叫“结构体名称” | //这是同时声明结构体与变量//之后才声明一个叫xxx的结构体变量 |struct 结构体名称 变量名1, 变量名2;|当然结构体中可以嵌套结构体,或指向自己的指针
嵌套结构体用于如:
可能需要在结构中存储实体员工的地址。而地址也可以包含其他信 息,例如街道编号,城市,地区和密码。
如下使用方法:
//此结构体的声明包含了其他的结构体 | 或以下,等方法struct COMPLEX | struct Employee{ | { char string[100]; | int id; struct SIMPLE a; | char name[20];}; | struct Date | {/*此结构体的声明包含了指向自己类型的指针*/| int dd;struct NODE | int mm;{ | int yyyy; char string[100]; | }doj; struct NODE *next_node; | }e1;}; |如果两个结构体相互包含,则要对其中一个结构体进行不完整声明
如:
struct B; //对结构体B进行不完整声明 //结构体A中包含指向结构体B的指针struct A{ struct B *partner; //通过此方法创建结构体指针 //other members;}; //结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明struct B{ struct A *partner; //other members;};有点像数组的方法
#include <stdio.h>struct Books{ char title[50]; char author[50]; char subject[100]; int book_id;} book = {"C 语言", "nhooo", "编程语言", 123456};int main(){//若要在函数中声明,按如下来//struct Books book = {"C语言","nhooo","编程语言",123456}; printf(...);}为了访问结构成员,要使用“成员访问运算符(.)”(就是一个英文句号)
使用方法:
结构体变量名+.+成员名
这样就可以访问其中的成员了
#include <stdio.h>#include <string.h>struct Books{ char title[50]; char author[50]; char subject[100]; int book_id;};int main( ){//方法一: struct Books Book[2];/* 声明 Book数组,类型为 Books */ /* Book 详述 */ strcpy( Book[1].title, "C语言"); strcpy( Book[1].author, "Nuha Ali"); strcpy( Book[1].subject, "C语言教程"); Book1.book_id = 6495407; strcpy( Book[2].title, "JAVA语言"); strcpy( Book[2].author, "Seagull Ali"); strcpy( Book[2].subject, "JAVA语言教程"); Book[2].book_id = 6495700; //或者: struct Books Book[2]= { {"C语言", "Nuha Ali", "C语言教程",6495407}, {"JAVA语言", "Seagull Ali", "JAVA语言教程",6495700}; return 0;}如:
void printBook( struct Books book ){......};int main(){struct Books Book1;printBook( Book1 );}指向结构的指针:
定义方法与其他类型相似
如:
//复用的是上上个的代码struct Books *sp; //定义指针变量sp = &Book1; //赋予结构体Book1的地址sp->title; //让指针访问Book1中的”title”这个成员使用“->”运算符访问结构体中的成员,如上
例:
printBook( &Book1 ); //主函数中传递地址 void printBook( struct Books *book ) //定义指针并接受地址{ printf( "Book 书名: %s\n", book->title); printf( "Book 编号: %d\n", book->book_id);}又叫“位段”,是一种在结构体中以位为单位分配存储空间的特性,用于节省空间
struct 位域结构名{ 类型说明符 位域名:位域长度;}如:
struct bs{ int a:8; // 占8个位(位(bit),一般8个位一字节(byte)) int b:2; // 占2个位 int c:6; // 占6个位}data; //data变量,共占两个字节(8+2+6=16)一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs{ unsigned a:4; unsigned :4; /* 空域 或叫 无名位域,只用来作填充或调整位置,是不能使用的 */ unsigned b:5; /* 从下一单元开始存放 */ unsigned c:3;}在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 5 位,c 占用 3 位。
由于位域不允许跨两个字节(最长为 8 位,一个字节),如unsigned int flag3 : 10;占 10 各位就不行,因为可能发生溢出错误
从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。
如:
int main(){ struct bs{ unsigned a:1; unsigned b:3; unsigned c:4; } bit,*pbit; bit.a=1; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */ bit.b=7; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */ bit.c=15; /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */ printf("%d,%d,%d\n",bit.a,bit.b,bit.c); /* 以整型量格式输出三个域的内容 */ pbit=&bit; /* 把位域变量 bit 的地址送给指针变量 pbit */ pbit->a=0; /* 用指针方式给位域 a 重新赋值,赋为 0 */ pbit->b&=3; /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */ pbit->c|=1; /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */ printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c); /* 用指针方式输出了这三个域的值 */}//上例程序中定义了位域结构 bs,三个位域为 a、b、c。说明了 bs 类型的变量 bit 和指向 bs 类型的指针变量 pbit。这表示位域也是可以使用指针的主要用于简化结构的语法
如下:
struct Distance{ | typedef struct Distance{ int feet; | int feet; float inch; 等同于 | float inch;}; | } distances; |int main(){ | int main(){ struct Distance d1, d2; | distances d1, d2;} | }结构嵌套结构:
struct complex{ int imag; float real;}; struct number{ struct complex comp; int integers;} num1, num2;若要设置 num2 的变量 imag 值为 11,可如下做:
num2.comp.imag = 11;创建指针,分配内存与其他的变量一样,用变量名
从函数中返回结构就直接“return 结构变量名”即可
或通过引用的方法(传递地址接收指针)
共用体又称联合体,和结构体共同属于 C 语言数据类型中的结构类型
union car{ char name[50]; //名字 int price; //价格};union car{……}; int main(){ union car car1,car2,*car3;//创建变量car1,car2,和指针car3}对于 car1 的价格(price),用car1.price
对于 car3 的价格(price),用(*car3).price 或 car3->price
注:结构体的指针同理这两种方法,但->更常用一些
结构体的内存是所有的总和,而共用体是取最大的那个
因为共用体是所有数据类型都共用一块内存空间,而结构体是每一种用一块空间
也因此,更改共用体中的一种数据,另一种也会更改
比如:
#include <stdio.h> union Data { int num; char letter;}; int main() { union Data data; data.num = 65; printf("Letter: %c\n", data.letter); //输出“A” 此时num=65 lettere=”A” data.letter = 'B'; printf("Number: %d\n", data.num); //输出“66”此时num=66 lettere=”B” return 0;}更改整数,浮点数,字符等的数据类型呀
在嵌入式系统中,更方便的操作硬件寄存器
等等
当然,我们也可以将 typedef 用于共用体,或者指针等等
结构体和共用体也可以用数组,如下:
#include <stdio.h>#include <strings.h>// 定义一个包含不同类型的共用体union Data { int i; float f; char str[20]; };int main() { // 定义一个共用体数组 union Data dataArr[3]; // 第一个元素为整数 dataArr[0].i = 10; printf("dataArr[0].i = %d\n", dataArr[0].i); // 第二个元素为浮点数 dataArr[1].f = 3.14; printf("dataArr[1].f = %.2f\n", dataArr[1].f); // 第三个元素为字符串 strcpy(dataArr[2].str, "Hello"); printf("dataArr[2].str = %s\n", dataArr[2].str); return 0;} //在vscode中调试一下就知道了共用体也像结构体一样可以嵌套,如下:
#include <stdio.h>// 定义一个嵌套共用体union InnerData { int i; float f; };// 定义一个外部共用体,其中包含嵌套共用体作为成员之一union OuterData { char c; union InnerData inner; };int main() { // 定义一个外部共用体变量 union OuterData data; // 使用嵌套共用体的成员 data.c = 'A'; printf("data.c = %c\n", data.c); data.inner.i = 10; printf("data.inner.i = %d\n", data.inner.i); data.inner.f = 3.14; printf("data.inner.f = %.2f\n", data.inner.f); return 0;} //同上试试就知道了如fprintf()、fscanf()、fwrite()、fseek()等
因为可以保留数据,以便更方便的储存和读取
文本文件
二进制文件
(1) 就是.txt文件
(1) 就是计算机中的.bin文件
(2) 并非以纯文本格式,而是以二进制格式(0 和 1)存储数据
(3) 它相比于文本文件,可以容纳更多的数据,但不易读取,因此安全性也高
也是用指针
文件与程序之间的通信需要此声明:
FILE *fptr; (这个fptr指针变量表示文件对象)
fopen()函数可以打开文件。
fptr (文件指针)=fopen(“路径+文件名.后缀”,“打开方式(模式)”);
路径如:E:/cprogram/newprogram.txt E:/cprogram/oldprogram.bin
若不写路径,则是打开与这一 C 文件同一件夹下的文件
当然也可以把“双引号+文件路径、名称”先以一个char或string的字符串记 下,然后直接用那个字符串就好了
| 打开方式 | 模式的含义 | 文件不存在期间 |
|---|---|---|
| r | 以只读方式打开 | 如果文件不存在,则fopen()返回 NULL |
| rb | 打开以二进制模式读取 | 如果文件不存在,则fopen()返回 NULL |
| w | 打开文本文件,允许写入文件 | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件在这里,您的程序会从文件的开头写入内容如果文件存在,则该会被截断为零长度,重新写入 |
| wb | 打开文件以二进制方式写入 | 如果文件存在,其内容将被覆盖 如果文件不存在,将创建它 |
| a | 打开进行追加。 数据添加到文件末尾 | 如果文件不存在,将创建它 |
| ab | 打开以二进制方式追加数据添加到文件末尾。 | 如果文件不存在,将创建它 |
| r+ | 开放供阅读和写作。 | 如果文件不存在,则fopen()返回 NULL |
| rb+ | 打开以二进制方式读取和写入 | 如果文件不存在,则fopen()返回 NULL |
| w+ | 打开一个文本文件,允许读写文件 | 如果文件存在,其内容将被覆盖如果文件不存在,将创建它 |
| wb+ | 打开以二进制方式读取和写入 | 如果文件存在,其内容将被覆盖 如果文件不存在,将创建它 |
| a+ | 打开以阅读和追加 | 如果该文件不存在,它将被创建 |
| ab+ | 打开以二进制模式读取和追加 | 如果文件不存在,将创建它 |
读取/写入后,应关闭文件
fclose()函数可以关闭文件。
fclose(fptr(文件指针));使用fprintf()、fscanf()函数来读取和写入文件
用法:
fprintf ( fptr, "%d", a );fscanf ( fptr, "%d", &a );当然还有fgetchar()、fget()、fputc()等等
使用fread()、fwrite()来读取二进制文件
fscanf()作用:从文件中按照指定格式读取数据并赋值给变量。
示例:
int num;fscanf(file, "%d", &num); //从文件中按照%d的格式读取数据并存储在num变量fprintf()作用:按照指定格式将数据写入文件。
示例:
int num = 123;fprintf(file, "The number is: %d\n", num); // 将字符串和num的值写入文件fgetc() (注:fgetchar 是从键盘中读取一个字符)作用:从指定文件中读取一个字符并返回其整数表示。
示例:
int ch;ch = fgetchar(file); // 从文件中读取一个字符并赋值给变量chfgets()作用:从指定文件中读取一行文本(包括换行符),并将其存储在字符串数组中。
示例:
char buffer[100];fgets(buffer, sizeof(buffer), file); // 从文件中读取一行文本并存储在buffer中fputc() (file put character:文件存放字符)作用:将字符写入指定文件。
示例:
int ch = 'A';fputc(ch, file); // 将字符'A'写入文件fputs()作用:向文件中写入一个字符串。
示例:
char s[] = "Hello, world!";fputs(s, file); // 将字符串"Hello, world!"写入文件fread()作用:从文件中读取二进制数据,并将其存储在指定的内存块中。
示例:
int buffer[100];fread(buffer, sizeof(int), 100, file); // 从文件中读取100个整数到buffer中以二进制的形式fwrite()作用:将二进制数据从指定的内存块写入文件。
示例:
int buffer[100];fwrite(buffer, sizeof(int), 100, file); // 将buffer中的100个整数写入文件以二进制的形式fseek()获取数据为了不浪费大量时间,内存获取文件中特定位置的记录,可以使用fseek来访问
fseek()函数用于设置文件指针的位置。它可以将文件指针移动到文件中的特定位置,以便对文件进行随机访问
使用方法:
int fseek(fptr, long offset, int origin);int fseek(指向文件的指针,要查找的记录的位置,起始位置)| 位置 | 含义 |
|---|---|
| SEEK_SET | 起始位置为文件开头(顺序) |
| SEEK_END | 起始位置为文件末尾(倒序) |
| SEEK_CUR | 起始位置为文件指针当前位置(读完后停下来的位置) |
fseek(fptr, 10, SEEK_SET);// 将文件指针从开头向后偏移10个字节fseek(fptr, -2*sizeof(struct threeNum), SEEK_CUR);//将文件指针fptr从当前位置向前移动两个struct threeNum结构体的大小//若是2,则是向后移动两个结构体的大小//若是加二+2,而非乘二*2,则是多向后移动两个字节的大小,当然也有除2 等等rewind()函数用于将文件指针倒回到文件开头(将文件位置指示器重新定位到文件的起始位置)
用法:
rewind(fptr);用于获取文件位置指示器的当前位置(从前往后数)
用法:
int len= ftell (fptr);enum)枚举类型是 int 整数常量的数据类型
enum 名称 //这个名称又称变量的蓝图{ 第一个选项, 第二个选项, 第三个选项//默认每个选项的值为从小到大,从0,1,2,...开始排//但可以人为定义,如对于第二个选项club:“club=0,”};//和结构体一样, enum boolean {false, true}; enum boolean check; // 声明一个枚举变量//或 enum boolean {false, true} check;#include <stdio.h> enum week {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; int main(){ //创建枚举week类型的today变量 enum week today; today = Wednesday; printf("Day %d",today+1); return 0;}让维护性和可读性提高,文字总比数字清晰呀
(C Preprocessor,简写为 CPP)
就是开头的那些#xxxx
| 指令 | 描述 |
|---|---|
| #define | 定义宏 |
| #include | 包含一个源代码文件 |
| #undef | 取消已定义的宏 |
| #ifdef | 如果宏已经定义,则返回真 |
| #ifndef | 如果宏没有定义,则返回真 (if not define) |
| #if | 如果给定条件为真,则编译下面代码 |
| #else | #if 的代替方案 |
| #elif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
| #endif | 结束一个 #if……#else 条件编译块 |
| #error | 当遇到标准错误时,输出错误消息 |
| #pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
有:
| #endif | 结束一个 #if……#else 条件编译块 |
|---|---|
| #error | 当遇到标准错误时,输出错误消息 |
| #pragma | 使用标准化方法,向编译器发布特殊的命令到编译器中 |
如:
#define#define PI 3.14159//定义一个作用于全局的常量,用于方便之后修改和提高可读性#define MAX(a, b) ((a) > (b) ? (a) : (b))//宏定义中可以包含表达式和逻辑运算符。#define PRINT_MESSAGE printf("Hello, World!")//将标准输出函数printf()的调用作为替代文本,在代码中使用PRINT_MESSAGE时,相当于插入了printf("Hello, World!")这段代码。#include#include <stdio.h> //尖括号用于引入系统级头文件#include "myheader.h" //双引号用于引入自定义的,包括自己定义打包的函数,在当前目录//其实就是搜索顺序不同了,用错没问题,但不符合规范,可能也会有冲突#undef#undef FILE_SIZE#define FILE_SIZE 42//取消已定义的 FILE_SIZE,并定义它为 42#if类#ifndef MESSAGE #define MESSAGE "You wish!"#endif//这个指令告诉 CPP 只有当 MESSAGE 未定义时,才定义 MESSAGE就是被预定义的宏
| 宏 | 描述 |
|---|---|
__DATE__ |
当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。 |
__TIME__ |
当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。 |
__FILE__ |
这会包含当前文件名,一个字符串常量。 |
__LINE__ |
这会包含当前行号,一个十进制常量。 |
__STDC__ |
当编译器以 ANSI 标准编译时,则定义为 1。 |
例:
//在文件test.c中:#include <stdio.h>main(){ printf("File :%s\n", __FILE__ ); printf("Date :%s\n", __DATE__ ); printf("Time :%s\n", __TIME__ ); printf("Line :%d\n", __LINE__ ); printf("ANSI :%d\n", __STDC__ );}返回:File :test.c //文件名Date :Jun 2 2012 //当前日期Time :03:36:24 //当前时间Line :7 //这条语句在第几行ANSI :1 //看是按什么来编译的就是宏专用的运算符
\用于将一行写不下的宏分成两行
#define message_for(a, b) \ printf(#a " and " #b ": We love you!\n")就是将#后面的参数转换为字符串常量
#include <stdio.h>#define message_for(a, b) printf(#a " and " #b ": We love you!\n")int main(void){ message_for(Carole, Debra); return 0;}##):用于将两个标记合并为一个标记,但只能在宏定义中使用
#include <stdio.h>#define tokenpaster(n) printf ("token" #n " = %d", token##n)int main(void){ int token34 = 40; tokenpaster(34); //寻找token与34合并在一起的单词,并输出 //等于printf ("token34 = %d", token34); return 0;}defined() 运算符常与#if一起用于检查宏是否已被定义,当然了,已经有了简化的#ifdef和#ifndef
就是宏定义中可以包含表达式和逻辑运算符啦
int square(int x){ return x * x;}//等于#define square(x) ((x) * (x))头文件与源文件的区别:
头文件就是#include后的内容,源文件就是你写的xxx.c的代码
C 标准库 -<assert.h>
C 标准库 -<ctype.h>:
| 序号 | 函数 & 描述 |
|---|---|
| 1 | int isalnum(int c) 该函数检查所传的字符是否是字母和数字。 |
| 2 | int isalpha(int c) 该函数检查所传的字符是否是字母。 |
| 3 | int iscntrl(int c) 该函数检查所传的字符是否是控制字符。 |
| 4 | int isdigit(int c) 该函数检查所传的字符是否是十进制数字。 |
| 5 | int isgraph(int c) 该函数检查所传的字符是否有图形表示法。 |
| 6 | int islower(int c) 该函数检查所传的字符是否是小写字母。 |
| 7 | int isprint(int c) 该函数检查所传的字符是否是可打印的。 |
| 8 | int ispunct(int c) 该函数检查所传的字符是否是标点符号字符。 |
| 9 | int isspace(int c) 该函数检查所传的字符是否是空白字符。 |
| 10 | int isupper(int c) 该函数检查所传的字符是否是大写字母。 |
| 11 | int isxdigit(int c) 该函数检查所传的字符是否是十六进制数字。 |
标准库还包含了两个转换函数,它们接受并返回一个 "int"
| 序号 | 函数 & 描述 |
|---|---|
| 1 | int tolower(int c) 该函数把大写字母转换为小写字母。 |
| 2 | int toupper(int c) 该函数把小写字母转换为大写字母。 |
字符类
| 序号 | 字符类 & 描述 |
|---|---|
| 1 | 数字 完整的数字集合 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } |
| 2 | 十六进制数字 集合 { 0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f } |
| 3 | 小写字母 集合 { a b c d e f g h i j k l m n o p q r s t u v w x y z } |
| 4 | 大写字母 集合 {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z } |
| 5 | 字母 小写字母和大写字母的集合 |
| 6 | 字母数字字符 数字、小写字母和大写字母的集合 |
| 7 | 标点符号字符 集合 ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ |
| 8 | 图形字符 字母数字字符和标点符号字符的集合 |
| 9 | 空格字符 制表符、换行符、垂直制表符、换页符、回车符、空格符的集合。 |
| 10 | 可打印字符 字母数字字符、标点符号字符和空格字符的集合。 |
| 11 | 控制字符 在 ASCII 编码中,这些字符的八进制代码是从 000 到 037,以及 177(DEL)。 |
| 12 | 空白字符 包括空格符和制表符。 |
| 13 | 字母字符 小写字母和大写字母的集合。 |
C 标准库 -<errno.h>
C 标准库 -<float.h>
C 标准库 -<limits.h>
C 标准库 -<locale.h>
C 标准库 -<math.h>
| 序号 | 函数 & 描述 |
|---|---|
| 1 | double acos(double x) 返回以弧度表示的 x 的反余弦。 |
| 2 | double asin(double x) 返回以弧度表示的 x 的反正弦。 |
| 3 | double atan(double x) 返回以弧度表示的 x 的反正切。 |
| 4 | double atan2(double y, double x) 返回以弧度表示的 y/x 的反正切。y 和 x 的值的符号决定了正确的象限。 |
| 5 | double cos(double x) 返回弧度角 x 的余弦。 |
| 6 | double cosh(double x) 返回 x 的双曲余弦。 |
| 7 | double sin(double x) 返回弧度角 x 的正弦。 |
| 8 | double sinh(double x) 返回 x 的双曲正弦。 |
| 9 | double tanh(double x) 返回 x 的双曲正切。 |
| 10 | double exp(double x) 返回 e 的 x 次幂的值。 |
| 11 | double frexp(double x, int *exponent) 把浮点数 x 分解成尾数和指数。返回值是尾数,并将指数存入 exponent 中。所得的值是 x = mantissa * 2 ^ exponent。 |
| 12 | double ldexp(double x, int exponent) 返回 x 乘以 2 的 exponent 次幂。 |
| 13 | double log(double x) 返回 x 的自然对数(基数为 e 的对数)。 |
| 14 | double log10(double x) 返回 x 的常用对数(基数为 10 的对数)。 |
| 15 | double modf(double x, double *integer) 返回值为小数部分(小数点后的部分),并设置 integer 为整数部分。 |
| 16 | double pow(double x, double y) 返回 x 的 y 次幂。 |
| 17 | double sqrt(double x) 返回 x 的平方根。 |
| 18 | double ceil(double x) 返回大于或等于 x 的最小的整数值。 |
| 19 | double fabs(double x) 返回 x 的绝对值。 |
| 20 | double floor(double x) 返回小于或等于 x 的最大的整数值。 |
| 21 | double fmod(double x, double y) 返回 x 除以 y 的余数。 |
C 标准库 -<setjmp.h>
C 标准库 -<signal.h>
C 标准库 -<stdarg.h>
C 标准库 - <stddef.h>
C 标准库 -<stdio.h>
| 序号 | 函数 & 描述 |
|---|---|
| 1 | int fclose(FILE *stream) 关闭流 stream。刷新所有的缓冲区。 |
| 2 | void clearerr(FILE *stream) 清除给定流 stream 的文件结束和错误标识符。 |
| 3 | int feof(FILE *stream) 测试给定流 stream 的文件结束标识符。 |
| 4 | int ferror(FILE *stream) 测试给定流 stream 的错误标识符。 |
| 5 | int fflush(FILE *stream) 刷新流 stream 的输出缓冲区。 |
| 6 | int fgetpos(FILE *stream, fpos_t *pos) 获取流 stream 的当前文件位置,并把它写入到 pos。 |
| 7 | FILE *fopen(const char *filename, const char *mode) 使用给定的模式 mode 打开 filename 所指向的文件。 |
| 8 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从给定流 stream 读取数据到 ptr 所指向的数组中。 |
| 9 | FILE *freopen(const char *filename, const char *mode, FILE *stream) 把一个新的文件名 filename 与给定的打开的流 stream 关联,同时关闭流中的旧文件。 |
| 10 | int fseek(FILE *stream, long int offset, int whence) 设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。 |
| 11 | int fsetpos(FILE *stream, const fpos_t *pos) 设置给定流 stream 的文件位置为给定的位置。参数 pos 是由函数 fgetpos 给定的位置。 |
| 12 | long int ftell(FILE *stream) 返回给定流 stream 的当前文件位置。 |
| 13 | size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 把 ptr 所指向的数组中的数据写入到给定流 stream 中。 |
| 14 | int remove(const char *filename) 删除给定的文件名 filename,以便它不再被访问。 |
| 15 | int rename(const char *old_filename, const char *new_filename) 把 old_filename 所指向的文件名改为 new_filename。 |
| 16 | void rewind(FILE *stream) 设置文件位置为给定流 stream 的文件的开头。 |
| 17 | void setbuf(FILE *stream, char *buffer) 定义流 stream 应如何缓冲。 |
| 18 | int setvbuf(FILE *stream, char *buffer, int mode, size_t size) 另一个定义流 stream 应如何缓冲的函数。 |
| 19 | FILE *tmpfile(void)创建临时文件。 |
| 20 | char *tmpnam(char *str) 生成并返回一个有效的临时文件名,该文件名之前是不存在的。 |
| 21 | int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中。 |
| 22 | int printf(const char *format, ...) 发送格式化输出到标准输出 stdout。 |
| 23 | int sprintf(char *str, const char *format, ...) 发送格式化输出到字符串。 |
| 24 | int vfprintf(FILE *stream, const char *format, va_list arg) 使用参数列表发送格式化输出到流 stream 中。 |
| 25 | int vprintf(const char *format, va_list arg) 使用参数列表发送格式化输出到标准输出 stdout。 |
| 26 | int vsprintf(char *str, const char *format, va_list arg) 使用参数列表发送格式化输出到字符串。 |
| 27 | int fscanf(FILE *stream, const char *format, ...) 从流 stream 读取格式化输入。 |
| 28 | int scanf(const char *format, ...) 从标准输入 stdin 读取格式化输入。 |
| 29 | int sscanf(const char *str, const char *format, ...) 从字符串读取格式化输入。 |
| 30 | int fgetc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。 |
| 31 | char *fgets(char *str, int n, FILE *stream) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。 |
| 32 | int fputc(int char, FILE *stream) 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。 |
| 33 | int fputs(const char *str, FILE *stream) 把字符串写入到指定的流 stream 中,但不包括空字符。 |
| 34 | int getc(FILE *stream) 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。 |
| 35 | int getchar(void) 从标准输入 stdin 获取一个字符(一个无符号字符)。 |
| 36 | char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。 |
| 37 | int putc(int char, FILE *stream) 把参数 char 指定的字符(一个无符号字符)写入到指定的流 stream 中,并把位置标识符往前移动。 |
| 38 | int putchar(int char) 把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中。 |
| 39 | int puts(const char *str) 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。 |
| 40 | int ungetc(int char, FILE *stream) 把字符 char(一个无符号字符)推入到指定的流 stream 中,以便它是下一个被读取到的字符。 |
| 41 | void perror(const char *str) 把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格。 |
| 42 | int snprintf(char *str, size_t size, const char *format, ...) 格式字符串到 str 中。 |
其中还有库宏,宏变量等等没有写
C 标准库 -<stdlib.h>
| 序号 | 函数 & 描述 |
|---|---|
| 1 | double atof(const char *str) 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)。 |
| 2 | int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。 |
| 3 | long int atol(const char *str) 把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)。 |
| 4 | double strtod(const char *str, char **endptr) 把参数 str 所指向的字符串转换为一个浮点数(类型为 double 型)。 |
| 5 | long int strtol(const char *str, char **endptr, int base) 把参数 str 所指向的字符串转换为一个长整数(类型为 long int 型)。 |
| 6 | unsigned long int strtoul(const char *str, char **endptr, int base) 把参数 str 所指向的字符串转换为一个无符号长整数(类型为 unsigned long int 型)。 |
| 7 | void *calloc(size_t nitems, size_t size) 分配所需的内存空间,并返回一个指向它的指针。 |
| 8 | void free(void *ptr) 释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。 |
| 9 | void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。 |
| 10 | void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。 |
| 11 | void abort(void) 使一个异常程序终止。 |
| 12 | int atexit(void (*func)(void)) 当程序正常终止时,调用指定的函数 func。 |
| 13 | void exit(int status) 使程序正常终止。 |
| 14 | char *getenv(const char *name) 搜索 name 所指向的环境字符串,并返回相关的值给字符串。 |
| 15 | int system(const char *string) 由 string 指定的命令传给要被命令处理器执行的主机环境。 |
| 16 | void *bsearch(const void *key, const void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *)) 执行二分查找。 |
| 17 | void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*)) 数组排序。 |
| 18 | int abs(int x) 返回 x 的绝对值。 |
| 19 | div_t div(int numer, int denom) 分子除以分母。 |
| 20 | long int labs(long int x) 返回 x 的绝对值。 |
| 21 | ldiv_t ldiv(long int numer, long int denom) 分子除以分母。 |
| 22 | int rand(void) 返回一个范围在 0 到 RAND_MAX 之间的伪随机数。 |
| 23 | void srand(unsigned int seed) 该函数播种由函数 rand 使用的随机数发生器。 |
| 24 | int mblen(const char *str, size_t n) 返回参数 str 所指向的多字节字符的长度。 |
| 25 | size_t mbstowcs(schar_t *pwcs, const char *str, size_t n) 把参数 str 所指向的多字节字符的字符串转换为参数 pwcs 所指向的数组。 |
| 26 | int mbtowc(whcar_t *pwc, const char *str, size_t n) 检查参数 str 所指向的多字节字符。 |
| 27 | size_t wcstombs(char *str, const wchar_t *pwcs, size_t n) 把数组 pwcs 中存储的编码转换为多字节字符,并把它们存储在字符串 str 中。 |
| 28 | int wctomb(char *str, wchar_t wchar) 检查对应于参数 wchar 所给出的多字节字符的编码。 |
C 标准库 - <string.h>
| 序号 | 函数 & 描述 |
|---|---|
| 1 | void *memchr(const void *str, int c, size_t n) 在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置。 |
| 2 | int memcmp(const void *str1, const void *str2, size_t n) 把 str1 和 str2 的前 n 个字节进行比较。 |
| 3 | void *memcpy(void *dest, const void *src, size_t n) 从 src 复制 n 个字符到 dest。 |
| 4 | void *memmove(void *dest, const void *src, size_t n) 另一个用于从 src 复制 n 个字符到 dest 的函数。 |
| 5 | void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。 |
| 6 | char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。 |
| 7 | char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。 |
| 8 | char *strchr(const char *str, int c) 在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置。 |
| 9 | int strcmp(const char *str1, const char *str2) 把 str1 所指向的字符串和 str2 所指向的字符串进行比较。 |
| 10 | int strncmp(const char *str1, const char *str2, size_t n) 把 str1 和 str2 进行比较,最多比较前 n 个字节。 |
| 11 | int strcoll(const char *str1, const char *str2) 把 str1 和 str2 进行比较,结果取决于 LC_COLLATE 的位置设置。 |
| 12 | char *strcpy(char *dest, const char *src) 把 src 所指向的字符串复制到 dest。 |
| 13 | char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。 |
| 14 | size_t strcspn(const char *str1, const char *str2) 检索字符串 str1 开头连续有几个字符都不含字符串 str2 中的字符。 |
| 15 | char *strerror(int errnum) 从内部数组中搜索错误号 errnum,并返回一个指向错误消息字符串的指针。 |
| 16 | size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。 |
| 17 | char *strpbrk(const char *str1, const char *str2) 检索字符串 str1 中第一个匹配字符串 str2 中字符的字符,不包含空结束字符。也就是说,依次检验字符串 str1 中的字符,当被检验字符在字符串 str2 中也包含时,则停止检验,并返回该字符位置。 |
| 18 | char *strrchr(const char *str, int c) 在参数 str 所指向的字符串中搜索最后一次出现字符 c(一个无符号字符)的位置。 |
| 19 | size_t strspn(const char *str1, const char *str2) 检索字符串 str1 中第一个不在字符串 str2 中出现的字符下标。 |
| 20 | char *strstr(const char *haystack, const char *needle) 在字符串 haystack 中查找第一次出现字符串 needle(不包含空结束字符)的位置。 |
| 21 | char *strtok(char *str, const char *delim) 分解字符串 str 为一组字符串,delim 为分隔符。 |
| 22 | size_t strxfrm(char *dest, const char *src, size_t n) 根据程序当前的区域选项中的 LC_COLLATE 来转换字符串 src 的前 n 个字符,并把它们放置在字符串 dest 中。 |
C 标准库 -<time.h>
| 序号 | 函数 & 描述 |
|---|---|
| 1 | char *asctime(const struct tm *timeptr) 返回一个指向字符串的指针,它代表了结构 timeptr 的日期和时间。 |
| 2 | clock_t clock(void) 返回程序执行起(一般为程序的开头),处理器时钟所使用的时间。 |
| 3 | char *ctime(const time_t *timer) 返回一个表示当地时间的字符串,当地时间是基于参数 timer。 |
| 4 | double difftime(time_t time1, time_t time2)。 |
| 5 | struct tm *gmtime(const time_t *timer) timer 的值被分解为 tm 结构,并用协调世界时(UTC)也被称为格林尼治标准时间(GMT)表示。 |
| 6 | struct tm *localtime(const time_t *timer) timer 的值被分解为 tm 结构,并用本地时区表示。 |
| 7 | time_t mktime(struct tm *timeptr) 把 timeptr 所指向的结构转换为一个依据本地时区的 time_t 值。 |
| 8 | size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr) 根据 format 中定义的格式化规则,格式化结构 timeptr 表示的时间,并把它存储在 str 中。 |
| 9 | time_t time(time_t *timer) 计算当前日历时间,并把它编码成 time_t 格式。 |
有返回值的意思是它可以放在表达式中
scanf的返回值是输入数据的个数(就是几个%)
API:应用程序编程接口
win32API:win 的 api 接口,纯 c 的函数库,可用于 c/c++,和头文件一样使用
main 成为 C 语言的入口是因为操作系统通过启动程序在内存中寻找调用你的程序的 main 函数来运行
但是如果要调用 WinAPI 的话(要编写一个 win 程序的话),就要用 WinMain 做入口,因为要进行一些初始化的工作,如创建窗口、注册信息、初始化系统资源等等
WinMain 函数的原型如下:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);如何链接多个 cpp 文件(如将函数放到一个文件中,将主函数 main 放到一个文件中)
#include <iostream>using namespace std;int max(int a, int b);int main() { int a = 5, b = 6; cout << max(a, b); return 0;}int max(int a, int b) { return (a > b) ? a : b;}#include <iostream>using namespace std;int max(int a, int b);int main() { int a = 5, b = 6; cout << max(a, b); return 0;}int max(int a, int b) { return (a > b) ? a : b;}头文件:
就是在不声明函数的情况下(主要是因为声明时会告诉编译器要传入传出的类型),准确的使用函数(保证传入传出的是正确的类型)
一般把函数原型放到一个头文件中(.h 结尾)(head)(c++也是可以用.h 的头文件的,也可以不用,因为 c++没有规定头文件一定要用.h 结尾,当然了,为了区分,也可以加上)
VS 中头文件中的#pragma once是让头文件只被编译一次,防止错误
#pragma once是一个预处理器指令,用于确保头文件只被编译一次,以避免重复定义。当一个源文件包含一个头文件时,预处理器会将头文件的内容插入到源文件中。如果多个源文件都包含了同一个头文件,那么头文件的内容就会被插入多次,从而导致重复定义的错误。为了避免这种情况发生,可以在头文件的开头加上
#pragma once指令,告诉编译器只需要编译一次该头文件。使用
#pragma once比使用传统的宏定义方式来防止头文件重复包含更加简洁和直观,而且也更容易理解和维护。但需要注意的是,#pragma once并不是 C++语言标准中所规定的标准指令,因此并不是所有编译器都支持它。
在使用和定义这个函数的地方一般都应该#include 这个头文件
一般.c 都要有对应的.h,把所有对外公开的函数原型和全局变量的声明都放进去(全局变量是可以在多个.c 之间共享的,前提是也要用方法声明)
若只想让函数/全局变量只在当前.c/cpp 文件(编译单元)中被使用,则要在它们面前加上 static
#include
是一个编译预处理指令,和宏一样,在编译之前就处理了。
因此它会将那个文件的全部文本内容插入到它所在的地方,
所有也不一定要在.c 文件最前面使用#include
还有,它后面的<>或""中的是路径,可以是如:#include "../ExpressionCommandInterface.h"这样的
<>是在指定目录找,""是在当前目录找,""中也可以包含路径
<>在 path 环境变量中和编译器自带的路径中(如 MinGW 文件夹)中去找
一些常见的误区:
.lib和.a文件是静态库文件,它们是一组已编译好的二进制目标代码的集合,可以在链接时与其他目标文件一起链接成为可执行文件或动态链接库。声明全局变量:extern extern int a;
另一个文件若打出它,就是告诉那个文件,在另一个文件中已经声明了 a,现在你也可以用了
当然也可以在头文件中声明
函数的定义是定义,函数的原型是声明
变量的定义是定义,extern 变量的定义是声明(int i;(定义)和 extern int i;(声明))
声明是不产生代码的东西,如:
定义是产生代码的东西
对于头文件有以下规则
只有声明可以被放在头文件中
这是规则不是法律
否则会造成一个项目中多个编译单元中有重名的实体(比如可能会重复定义一个东西)
可以通过#pragma once(上面有写),或
#ifndef __xxx__ // 前后两个下划线是为了防止重复(因为一般不会用这种方式来命名) #define __xxx__ ... #endif // 以上就是一个标准头文件结构通过上述代码来防止重复定义
在 C 语言里面,每一个函数也像变量等一样有个地址,如printf("%p",main); --> 00007ff70a2216c1(十六进制数前面加不加 0x 都可以的,如这样也是可以的0x00007ff70a2216c1)(0000000000401530这是 DevC 给出的数,比 VSC 给出的小好多呀)
对于一个函数,不带后面的()就是一个地址,如:
void f(){ printf("abc\n");}int main(){ int i=0; int *p=&i; *p=20; printf("%p\n",main); printf("%p\n",f); // 它们输出的都是它们的地址,但不能直接用 *p=f 来赋值(获取函数的地址) // 要这样: void (*pf)() = f; // f像数组一样,本身,或者说它的名称就是地址,因此不用加&,当然加也可以 // 通过这种方法来获取函数的地址 // 感觉它像定义指针变量一样,定义了一个指针函数的方式定义了指针 printf("%p\n",*pf); // 因此可以使用如下的方式调用这个f函数 f(); (*pf)(); return 0;}void f(int i){printf("%d\n",i);} void ff(int i,int j){printf("%d %d\n",i,j);} int main(){ void (*pf)(int) = f; void (*pff)(int,int) = ff; f(10); // 输出:10 (*pf)(20); // 输出:20 ff(10,20); // 输出:10 20 (*pff)(20,30); // 输出:20 30 return 0;}void f(int i){printf("%d\n",i);} void g(int i){printf("%d\n",i);} int main(){ int i; cin>>i; void (*fa[])(int)={f,g}; // 这里创建了一个函数指针数组// 1. if( i>=0 && i<sizeof(fa)/sizeof(fa[0]) ) {(*fa[i])(0);}// 2. switch(i) { case 0:f(0);break; case 1:g(0);break; }// 第一个和第二个是同样的效果 return 0;} #include <stdio.h> int plus(int a,int b) // 创建了一个加法函数{ return a+b; } int minus(int a,int b) // 创建了一个减法函数{ return a-b; } void cal(int (*f)(int,int)) // 创建了一个计算机函数,输入的内容是函数的地址(函数名){ printf("%d\n",(*f)(2,3)); // 引用四则运算函数,并输出} int main(){ cal(plus); // 使用计算器函数中的加法函数 cal(minus); return 0;}MVC
MVC 是一种常见的软件架构模式,它将一个应用程序分成三个主要的部分:模型(Model)、视图(View)和控制器(Controller(Ctrl))。每个部分各自处理不同的任务,共同协作完成整个应用程序的工作。
以下是 MVC 各个部分的基础功能:
- 模型(Model):负责数据存储和业务逻辑处理。数据可以来自各种来源,例如数据库、文件系统、网络等。模型通常实现了一些数据操作方法,比如增删改查等。
- 视图(View):负责展示用户界面,并将用户的输入事件传递给控制器进行处理。视图通常会将模型中的数据呈现给用户,并接收用户对数据的操作。
- 控制器(Controller):处理用户的输入事件,并根据事件的类型选择相应的模型和视图来响应用户请求。控制器负责协调模型和视图之间的交互,以确保应用程序的行为符合预期。
使用 MVC 架构能够带来许多好处,例如:
- 分离关注点:MVC 将应用程序分成独立的组件,使得不同部分的代码可以更加清晰地理解和维护。这种分离还有助于使得应用程序更易于测试和扩展。
- 提高代码复用:由于每个部分的任务都清晰定义,所以不同的应用程序可以共享通用的模型、视图和控制器。
- 支持并行开发:MVC 可以让不同的团队或开发者同时进行开发,因为不同部分之间的接口是明确定义的。
- 提高灵活性:通过使用 MVC,应用程序的各个部分可以松散耦合,从而更容易地进行修改和维护。这使得应用程序能够更好地适应变化的需求和技术环境。
总体来说,MVC 是一种非常有用的软件架构模式,许多现代的应用程序都采用了这种模式。它可以使得开发者更加专注于应用程序的核心功能,同时提高代码质量、可维护性和可扩展性。
分享你的想法,与大家交流讨论
Twikoo 加载失败
Twikoo 脚本加载失败