C语言学习笔记

古早笔记,当时还没有使用markdown来写,可能不太方便阅读请见谅

2026-05-09 Songline ◷ 阅读约 8 分钟 笔记 C 程序 阅读 0

#include <stdio.h> int main() { printf(“Hello C!");

//// 注释
// // 单行注释
// /* 多行注释
// */
// 加注释control + k,control + c(快捷键)
// 去除注释control + k,control + u(快捷键)

//// 特殊符号
// & 显示变量内存地址
// %d 整型(十进制)
// %f 浮点型(取小数点后6位有效数字)
// %c 字符(单一字符)
// %s 字符串
// %p 内存地址
// %g 输出时除去多余的小数点后的0(取6位有效数字)
// \n 换行
// \r 回到行首
// \r\n windows
// \r mac
// \n linux ------>现在统一'\n'一次性换行且到行首
// \t 制表符,统一缩进,保证每个长度为8个长度(汉字占2个长度)
// '\'转义字符,如("\"")是将"由原来的特殊含义转变为普通的引号
// -> 取出结构体中的某个值

//// ASCII码表 二进制,十进制,十六进制------>图像 转换表
// 文字表式基础 ASCII表文字+GBK汉字码表
// 图像表式基础 RGB(Red Green Blue,为一个像素点) 分辨率(总共像素分布,如2560*1080)
// 声音表示基础 采样率(根据音频波形进行每秒采样,如44.1kHz = 每秒采样44100次)
// 视频表示基础 图像和声音的结合

//// 常用进制(以十进制45作为转换)  --都是二进制的衍生
// 二进制 101101 (0 1) --代码中以'0b'开头表示(如int a = 0b10;表示 int a = 2;)
// 八进制 55 (0 1 2 3 4 5 6 7) --代码中以'0'开头表示(如int a = 010;表示 int a = 8;)
// 十进制 45 (0 1 2 3 4 5 6 7 8 9) --无后缀
// 十六进制 2d (0 1 2 3 4 5 6 7 8 9 a b c d e f) --代码中以'0x'开头表示(如int a = 0x10;表示 int a = 16;)

//// 内存转换
// 1bit(一个0/1位)
// 1Byte(字节) = 8bit(比特)
// 1kb = 1024Byte
// 1MB = 1024KB
// 1GB = 1024MB
// 1TB = 1024GB
// 1PB = 1024TB
// 1EB = 1024PB
// 1ZB = 1024EB
// 1YB = 1024ZB

//// 数据类型
// 整数
// 整型常量 正数,负数,0
//      short 固定占据2byte 取值范围-32768 ~ 32767
//      int 固定占据4byte 取值范围-2147483648 ~ 2147483647
//      long 固定占据4/8byte(linux中32/64位操作系统),windows固定占据4byte 取值范围-2147483648 ~ 2147483647(4byte) -可用INT_MAX代替
//                                                                    -9223372036854775808 ~ 9223372036854775807(8byte)    ↑
//                                                                                                        在<limits.h>文件中|
//          (使用long时要加'L/l'如 long a = 10000L;其特定的占位符为'%ld')
//      long long 固定占据8byte 取值范围-9223372036854775808 ~ 9223372036854775807
//          (使用long long时要加'LL/ll'如 long long a = 10000LL;其特定的占位符为'%lld')
//      signed 有符号*整数* 全体整数(如signed int a = 114514;)
//      unsigned 无符号*整数* 0,正整数(如 unsigned int a = 114;注意占位符为'%u') ---->unsigned原理是将负数部分的内存分配给正数
//                                                                              所以unsigned int的取值范围0 ~ 4294967295
// 小数
// 浮点常量 小数 (.93 = 0.93,17. = 17.0,1.2340000E7 = 1.234的10的7次方)
//      float 固定占据4byte 取值范围1.175*10^-38 ~ 3.402*10^38
//      double 固定占据8byte 取值范围2.225*10^-308 ~ 1.1797*10^308
//      long double 固定占据8byte 取值范围2.225*10^-308 ~ 1.1797*10^308 输出时有后缀(如long double = 1.114514L;)
//      '%.xf' 小数后取x位[float(精度6位)默认6位,double(精度15位)默认6位,long double(精度18~19位)默认6位]
// 字符
// 字符常量 'A','C' 字符串常量 "A","C"
//      char 固定占据1byte 适用于ASCII表中的字符
// 文件
//      FILE 文件数据类型格式

//// 标识符
// 变量,函数命名时不能由数字开头(如 int 2c;会报错 , int c2;没有问题)
// 标识符可以包含数字,字母,下划线

//// 字符串[字符数组]
// 字符串变量定义: 1.char 变量名[内存] = "内容"; ---->内容可以改变
//                2.char* 变量名 = "内容"; ----->该数组放在'只读常量区'------->1.内容不可改变
//                                                                  ------->2.字符串可复用,即同一内容可以赋值给多个变量且内存地址一致
// 其中1个字母,数字,符号占用一个字节
// 1个中文占用2个字节
// 结尾标记'\0'占用1个字节 -------->如 "你好Ya!" 占用8个字节
// 比较大小写及数字: 遍历字符串中的每一个字符 若 (该字符 >= 'a' && <= 'z') 则为小写
//                                         若 (该字符 >= 'A' && <= 'Z') 则为大写
//                                         若 (该字符 >= '0' && <= '9') 则为数字

//// 运算符
// 算术运算符 + - * / %(取余)/(取模)
//      什么类型计算得什么结果 整数 / 整数 = 整数  小数 / 小数 = 小数  注意 整数 / 小数 = 小数
//      若是小数直接计算(而不用变量计算),位数过多会有不精确问题,建议控制小数后位数
// 自增/减运算符 ++ --
//      若(int a = 10;)int b = a++;得b = 10
//                     int b = ++a;得b = 11
//      当遇到多个++ --时注意逻辑[从左到右的顺序a--/a++先赋值(累减/加)后计算,--a/++a先计算后赋值(累减/加)]
//                              所以++a - --a + a-- + --a - a--结果为10
// 赋值运算符 += -= *= /= %= =
//      将计算结果赋值给右边的数
// 关系运算符 == >= <= != > <
//      关系成立为1(非0) 不成立为0
//      数字也可以成为条件(比如 int a = 1o;
//                          if (a++){}条件为真,因为a为非0)
// 逻辑运算符 &&(与) ||(或) !(非)---->真变为假 假变为真
//      等效Python的 'and' 'or'
//      短路逻辑(在&&中若左方条件为假则直接判断为假,在||中若左方判断为真则直接判断为真,同时不进行后面的判断) --->可以节省运算效率
// 三元运算符 (判断 ? 执行1 : 执行2;)
//                   ↑成立执行 ↑不成立执行
// 分隔符 (,)
//      a++,a += 1,a += b; ==等价于== a++;
//                                 a += 1;
//                                 a += b;
// 优先级
//      () >> ++/--,!/+/-(正负号) >> *,%,/  >>  +/-  >>  <,<=,>,>=  >>  ==/!=  >>  &&  >>  ||  >>  ?:  >>  =,*=,/=,%=,+=,-=  >>  ,

////数值拆分
// 将616拆分为个位,十位,百位
// 方法:616 % 10 = 6
//     616 / 10 % 10 = 1
//     616 / 100 % 10 = 6 便可得到拆分的数

//// 回文数(正/倒序读都是一样的)
// 如 121 14564346541

//// 隐式转换
// 将取值范围小的数转换为取值范围大的数-->short-->int-->long-->long long-->float-->double
// 运算时不同数据类型会转换为同一较大的数据类型进行计算(如int + float-->float + float)
// short,char进行计算时都会默认转换为int

//// 强制转换
// 不管内存大小,转换数据类型(如int b = 10;
//                            short i =  (short)b; )

//// 流程控制
// 顺序结构(程序从上往下执行)
// 分支结构(if switch)
//     if | else if | else
//     if(条件)
//     {
//     }
//     else if (条件)
//     {
//     }
//     else
//     {
//     }
//     switch | case
//     switch(输入值)--->只能是字符和整数
//     {
//     case 值1:------>当case值相等时触发执行,case值不能是变量,且不能重复
//          执行;
//          break;----->case穿透:这里不break的话,若是case值相等,下面的case都会触发
//     case 值2:
//          执行;
//          break;
//     default:------>当所有case都不符合时触发
//          执行;
//          break;
//     }
//     if switch的使用场所(if用于取值范围,switch用于有限且数量较少的case)
//     switch的执行效率高于if
// 循环结构(for | while | do while)
//      for(初始值,条件,尾端处理){}
//      while(条件){}
//      do        --------->先施行执行后判断条件
//      {
//          执行;
//      }
//      while(条件);
//      当知道循环次数时用for 当只知道循环结束条件时用while
//      break(跳出循环)
//      continue(结束本次循环,开始下一次循环,在for中的尾端处理仍会进行)
//      goto(到达指定位置)   格式----->a: 一行代码;
//                                   goto a;

//// 变量的生命周期(局部变量,与全局变量)
// 在{}内申明的新变量出了花括号后变量不存在(如 while(1)
//                                          {
//                                          int a = 1;
//                                          }-------->a在while外不存在)
//      解决方法:加入static前缀,如:static a = 10;

//// 函数
// ---->int 函数名(插入值)
// ↑    {
// |        执行;
// |        return 函数值;
// |    }
// 此处的int指的是return的值的数据类型

//// 函数库(头文件)   具体查询:http://zh.cppreference.com/
//-------------------<stdio.h>
// printf("变量1",变量2)
// scanf("占位符",&变量名) -可以有多个占位符
//      若格式为 scanf("114%d",&a); 则键盘输入时必须输入'114'否则报错 输入成功后a的值为你所输入的值(不包含'114')
//      工作原理:用户输入一些内容后会存放在缓冲区,scanf()会根据需求(%数据类型)取走一定的内容,其余则留在缓冲区
//              注意:残留内容容易影响下一次scanf()因而在使用完scanf()后要及时清除缓冲区
// fopen("文件路径","打开模式") -打开文件,返回FILE*数据类型指针变量(第一个字符的地址)
// fgetc(文件指针) -读取char并返回,读取不到时返回-1,随后指针默认加一
// fgets(数组,使用的数组长度(还是一行),文件指针) -读取string,读取不到时返回null,随后指针默认加一行(以换行符为主)
//          ↑                                 -文件指针处也可以用stdin表示标准输入,用来读取用户在键盘输入的内容
//      用于存储获取的字符串
//          ↓
// fread(数组,数组每个数据占用的字节大小,使用的数组长度,文件指针) -读取一段,返回有效文字的字节个数
// fputc(写入的字符,文件指针) -输入一个字符,返回写出字符
// fputs(写入字符串,文件指针) -输入一个字符串,成功返回非负数
// fwrite(数组,数组每个数据占用的字节大小,写入个数,文件指针) -输入一段字符,返回写出个数
// fclose(文件指针) -关闭文件
// exit(返回值) -结束所有进程
// getchar() -从缓冲区中读取一个字符(可用于清除缓冲区),return这个字符的ASCII码(int类型),读取到文件末尾则return EOF
// strcspn(被扫描的字符串,匹配的字符) -在字符串中寻找目标字符,并return该字符在字符串中的长度(索引位置)
//-------------------<time.h>
// time(存储位置) -获取当前时间戳(为long long类型)
//-------------------<math.h>
// abs(数值) -int取绝对值
//      labs(数值) -long取绝对值
//      llabs(数值) -long long取绝对值
//      fabs(数值) -float取绝对值
// pow(底数,幂次) -幂方
// sqrt(数值) -平方根
// ceil(数值) -向上取整
// floor(数值) -向下取整
//-------------------<stdlib.h>
// srand(数值) -设置种子
// rand(void) -获取伪随机数(伪的原因是该数值是由种子经过数学公式计算得)
//      解决伪随机得方法-将种子值设置为时间戳,即:srand(time(NULL));
//      解决取值范围得问题-取余,流程:1,确定范围 5~24
//                                 2,尾部减开头 24 - 5 = 19
//                                 3,数值加一 19 + 1 = 20
//                                 4,求余数值加上开头 rand() % 20 + 5 (此时取值范围为5~24)
// malloc(字节大小) -申请内存空间,并且返回空间的void类型首地址(int* p)
// calloc(数据类型个数,单个数据类型内存大小) -申请内存 + 初始化内存空间,效率比malloc()低
// realloc(内存首位置,新的内存字节大小) -修改空间大小,并且返回空间的void类型首地址,原数据不会丢失
// free(内存首位置) -释放空间,避免内存泄漏
//-------------------<string.h>
// strlen(字符串) -获取字符串长度(不包含'\0')
// strcat(被拼接字符串,拼接字符串) -拼接两个字符串(被拼接字符串为拼接后的字符串,拼接字符串则保持不变)
// strcpy(粘贴字符串,复制字符串) -复制并覆盖字符串
// strcmp(字符串1,字符串2) -比较两个字符串是否完全一样 一样return: 0 不一样return: 非0
// _strlwr(字符串) -字符串全部变小写
// _strupr(字符串) -字符串全部变大写
// memset(数组,设置值,数据类型大小) -用来初始化数组
//-------------------<ctype.h>
// isalpha(一个字符) -判断输入的值是否为字母字符,真return非0数,假return 0
// isdigit(一个字符) -判断输入的值是否为数字字符,真return非0数,假return 0
// tolower(一个字符) -小写字符
//-------------------<windows.h>
// setCursorPosition(x,y) -用来设置光标在控制台的位置
//-------------------<conio.h>
// _kbhit() -检测是否有键盘输入 return:有为非0 ,无为0
// _getch() -获取按键值用来检测键盘的录入

//// 数组
// 数据类型 数组名[长度] = {内容,内容.....}
// 数组在内存中是连续的一组数,设定了数组的长度后内存为定值
// 未定义的空白数组部分为默认值
//      整数为: 0
//      小数为: 0.0
//      字符为: \0
//      字符串为: NULL
// 数组长度获取 总内存长度 / 数据类型长度
// 注意:当数组作为变量输入函数中时,输入的并非数组本身而是数组的内存地址,直接调用输出并不是数组的值
// 二维数组 格式:1.int 数组名[a][b] = {
//                                  {1,2,3,4.....},
//                                  {5,6,7,8.....}
//                                  };
//              2.itn 数组名1[] = {};
//                int 数组名2[] = {};
//                int 数组名3[] = {};
//                int* 数组名4[] = {数组名1,数组名2,数组名3}; ------->此时存储的数组为数组的指针
//          则二维数组的数据类型为 int(* 变量名)[一维数组个数]

//// 指针
// 64位系统是指内存地址以64bit表示(最大约17179TB)
// 32位系统是指内存地址以32bit表示(最大约4GB) -------->所以64位能表示的内存多余32位更优
// 实际内存地址以16进制表示,较为简洁
// 数组的索引是依据数据类型来进行的
//      如int为4byte 那么对于int a[4];中
//                   a[0] 与 a[1] 间首位相距4byte
//                   底层中计算机会根据4byte来加减实现索引功能
// 指针变量 数据类型 * 变量名 = 赋值;
//      示例:   int a = 10;
//              int*p = &a; ---------->则p为指针变量
//      随后:   b = *p; -------->b则为a的值
//      注意:   此处*有两个作用(在申明变量时*表示这个为指针变量,在运算时*表示解引,即指针所指引的具体内容)
// 指针作用:
//      函数中return(>=2)个值,调用其他函数中的值(因为已知内存的地址)
// 指针运算:
//      指针 + 1 指以当前指引的数据类型为单位加一,如int* a + 1 则是a的地址 + 4byte
//          注意:void类型指针,由于没有类型不能进行加减运算,且void指针不能被解引
// 野指针/悬空指针 指向无实际意义的指针
// 多级指针: 指向指针的指针(数据类型为指向的指针的数据类型,如int** a;)
// 数组指针: 当一个数组参与计算的时侯,数组会退化为指针 如: int a[] = {1,2,3,4,5};
//                                                     int* p = a; ---->此时a为指向数组第一个数的指针
//                                                           注意:sizeof(a)仍然是数组的长度
//                                                           则有 a + 1 移动一个int长度的大小
//                                                               若是用和号(&)申明,int* p = &a;
//                                                               则 a + 1 移动的长度为该数组的大小
// 函数指针: 函数数据类型 (*指针名)(函数插入值,只用写数据类型) = 函数名;
//      指针调用 指针名(函数插入值,需要写数值) -->即可调用函数
//      函数指针数组: 函数数据类型 (*指针名[])(函数插入值,只用写数据类型) = {函数名1,函数名2.....};
//               如: int(*arr[4])(int, int) = {add,subtract,multiply,divided};
//             调用: (arr[0])(输入值1, 输入值2);

//// 算法
//---------------------查找
// 顺序查找(遍历)
// 二分查找(前提:有序数列)
// 插值查找(比例查找,适合分布均匀的有序数列) 公式: mid = min + {(goal - arr[min]) / (arr[max] - arr[min])} * (max - min)
//                                              ↑
//                                          goal在数列中的占比,从而快速查询
// 分块查找(每一个板块有一些数(数量为总数的算术平方根),且每个板块间按照大小顺序排列,最后再从板块内查找)
// 哈希查找(链表 + 分块查找)
//---------------------排序
// 冒泡排序(前后比对换位排序)
// 选择排序(从0索引跟后面依依比较,随后从1索引跟后面依依比较..........)
// 二叉树(不断二分排序)

//// 结构体 一批数据组成的数据结构
// 格式: typedef struct 数据结构名字
//      {
//          数据类型 变量1;
//          数据类型 变量2;
//      }数据结构的缩称;
// 结构体赋值 struct 数据结构名字 变量名;
// 结构体使用 变量名.结构体中的变量 = "内容";
// 结构体批量赋值 结构体变量 = {按照顺序给结构体内变量赋值,{嵌套结构体内变量赋值}};
// 结构体数组 将一些结构体变量放在一个数组中
// 函数中的结构体 将结构体值输入进函数时,实际上是一个新的值,在函数内对结构体的改变并不会对输入值有改变
//      解决方案:输入值改为指针
// 结构体嵌套 在结构体内嵌套一个已定义的结构体
// 结构体内存对齐: 1.中间值补位:结构体内变量只能存放在自己类型整数倍的内存地址上(普通变量也是如此),未使用部分则填补空白字节
//                2.最后补位:  结构体的总大小,是最大数据类型的整数倍 ------>结论:结构体申明时一般将字节占用较小的写在前面(以节约内存)
// COORD结构体
//      COORD coord: 声明一个COORD类型的变量
//      COORD是Windows API中定义的结构体,用于表示控制台屏幕缓冲区中的字符坐标
//          它有两个成员:
//              X: 列坐标(水平位置)
//              Y: 行坐标(垂直位置)

//// 共用体/联合体 一种数据类型包含多种类型
// 格式: typedef union 共用体名
//      {
//          数据类型 变量名1;
//          数据类型 变量名2;
//          数据类型 变量名3;
//      }共用体缩称;
// 使用方法参上↑结构体使用(可以类比)
// 共用体内存分配: 1.所有内部变量共用一个内存(因而内存地址相同)
//                2.而所占的内存大小为内部变量中最大的一个数据类型的内存(也受到上述内存对齐的规则影响)
//                3.每次对共用体赋值时,会覆盖原本数据(因此共用体只能一次存储一个数据)
//                                                                ↑
// 结构体与共用体的区别:(mc比喻)结构体是复合工具(满足所有工具功能) 而共用体是工具背包(一次只能选择一种工具使用)

//// 内存
// malloc(字节大小) -申请内存空间,并且返回空间的void类型首地址(int* p)
//      注意: 申请过多空间会产生虚拟内存(申请内存时先放入虚拟内存中,真正赋值时则会用真的内存)
//            申请失败则会返回NULL
// calloc(数据类型个数,单个数据类型内存大小) -申请内存 + 初始化内存空间,效率比malloc()低
// realloc(内存首位置,新的内存字节大小) -修改空间大小,并且返回空间的void类型首地址,原数据不会丢失
//      注意: 申请过多空间会产生虚拟内存(申请内存时先放入虚拟内存中,真正赋值时则会用真的内存)
//            申请失败则会返回NULL
//            原来的空间不必释放,函数底层自动进行
// free(内存首位置) -释放空间,避免内存泄漏
// 分配后赋值方法: 以返回地址为int* p为例
//                  1. *(p + i) = (i + 1) * 10;
//                  2. p[i] = (i + 1) * 10; ------>类比数组
//                      ↑
//                    实际含义p + i
// 内存分布底层原理
//  ------------------  ------------------------------------------------  ------------


//  |       栈       |  |                      堆                      |  |      代码区     |
//  | 函数运行时的区域|  |                                              |  |    运行代码时   |
//  |  (包括定义函数) |  |                                              |  |    加载的区域   |
//  |   (包括主函数)  |  |                                              |  |  (只存放不运行) |
//  |                |  |                 申请的的内存                  |  |                |
//  |                |  |                                              |  |                |
//  |                |  |                                              |  |                |
//  |                |  |                                              |  |                |
//  |                |  |                                              |  |                |
//  |                |  ------------------------------------------------  |                |
//  |                |                                                    |                |
//  |                |  ------------------------------------------------  |                |
//  |                |  |  初始化静态区    | 未初始化静态区  |   常量区   |  |                |
//  |                |  |     static      |    static      |   字符串   |  |                |
//  |                |  |全局变量(已初始化)|全局变量(未初始化)| (不可修改) |  |                |
//  ------------------  ------------------------------------------------  ------------


//// 文件
// 绝对路径:严格写完出完整的路径
// 相对路径:相对某一文件的路径(有参照物)
//      注意:路径是以字符串的形式表示的,打印地址会显示缩减路径,解决方案'\\'如:C:\\Users\\Songline\\Desktop
// 打开:fopen("文件路径","打开模式") -打开文件,返回FILE*数据类型指针变量(第一个字符的地址)
// 读取:fgetc(文件指针) -读取char并返回,读取不到时返回-1,随后指针默认加一
//      fgets(数组,使用的数组长度(还是一行),文件指针) -读取string,读取不到时返回null,随后指针默认加一行(以换行符为主)
//              ↑
//          用于存储获取的字符串
//              ↓
//      fread(数组,数组每个数据占用的字节大小,使用的数组长度,文件指针) -读取一段,返回有效文字的字节个数
//          注意: 如果中文无法识别,将编码模式改为 ANSI(在保存位置)
// 输出:fputc(写入的字符,文件指针) -输入一个字符,返回写出字符
//      fputs(写入字符串,文件指针) -输入一个字符串,成功返回非负数
//      fwrite(数组,数组每个数据占用的字节大小,写入个数,文件指针) -输入一段字符,返回写出个数
//          注意: 写入的字符如'a'用ASCII表的整数表示(97)
// 关闭:fclose(文件指针) -关闭文件
// 拷贝流程: 1.打开被拷贝文件(数据源)
//          2.打开拷贝文件(目的地)
//          3.循环写入
//          4.关闭文件
// 打开模式: r 只读模式 ------纯文本
//          w 只写模式(文件若已存在,清空文件)
//          a 追加写出模式(文件已存在,不清空,续写)
//          rb 只读模式(读二进制文件) ------视频,音频,图片
//          wb 只写模式(写二进制文件,文件若已存在,清空文件)
//          ab 追加写出模式(写二进制文件,文件已存在,不清空,续写)
//          r+ 读写模式 ------纯文本
//          w+ 读写模式(文件若已存在,清空文件)
//          a+ 读和追加写出模式(文件已存在,不清空,续写)
//          rb+/r+b 读写模式(读二进制文件) -------视频,音频,图片
//          wb+/w+b 写读模式(写二进制文件,文件若已存在,清空文件)
//          ab+/a+b 读和追加写出模式(写二进制文件,文件已存在,不清空,续写)

//// 特殊前后缀
// static 使定义的变量即使在函数中生命周期也不会结束,在主程序中仍存在
// igned 有符号*整数* 全体整数(如signed int a = 114514;)
// unsigned 无符号*整数* 0,正整数(如 unsigned int a = 114;注意占位符为'%u') ---->unsigned原理是将负数部分的内存分配给正数
//                                                                         所以unsigned int的取值范围0 ~ 4294967295

//// 求最大公因数的办法
// 两数先相除 求余 将除数除与余数 求余 如此循环直到余数为0 则该除数为最大公因数

printf("~完结撒花awa~");

}

Markdown 源文件

喜欢离线保存的话,可以把这篇文章下载为 Markdown 文件。

上一篇SQL学习笔记
2026-05-09
下一篇Go学习
2026-05-09

相关文章