Ox00 开篇

  1. 原码、反码和补码
  • 原码:原码的最高位保存的是它的符号位,负数的最高位为1
  • 反码:反码就是将原码除符号位外按位取反
  • 补码:补码就是反码+1.
  1. 二进制、十进制和十六进制
  • 二进制:0 1
  • 十进制:0 1 2 3 4 5 6 7 8 9
  • 十六进制:0 1 2 3 4 5 6 7 8 9 A B C D E F(方便计算)
  1. 进制间的转换方式
  • 不同进制到十进制的转换
    • 权位相加法 (0xFF -> 15* (161) 15*(160))
  • 十进制到不同进制的转换
    • 辗转相除法,最终逆向获取余数
      • 10 -> bin
      • 10 / 2 >   5 0
      • 5 / 2 >    2 1
      • 2 / 2 >    1 0
      • 1 / 2 >    0 1
  • 十六进制、八进制和二进制间的转换
    • 十六进制到二进制: 4个二进制 == 一个十六进制位   c(1100) f(1111)
    • 八进制到二进制:3个二进制 == 一个八进制位    5(101)6(110) 7(111)  "\o567"
  1. 数据在计算机中的存储形式
  • 任何数据在计算机中都是以二进制补码方式存储的,正数三码合一
  • 补码的意义在于将减法转换成加法

Ox01 C语言部分

  1. C语言的诞生
    • 丹尼斯里奇,1972
  2. 编译型语言和解释型语言
  • 编译型语言: C/C++
    • 指已将源代码完全转译为可执行程序代码,编译完的程序可以脱离语言环境独立运行,但修改则需修改源码
  • 解释型语言: JS/Python/Java
    • 源码仅转译为中间代码,运行需依赖语言解释器,跨平台性能好,修改较为便捷

Ox10 基础语法

  1. 关键字
      1. 宏的使用 define
      • 有参宏:
        • #define show(a) printf(a);    // 接受字符串并输出
      • 无参宏:
        • 定义常量:  
          • #define PI 3.14
        • 定义一组有意义的数值(不需要关注具体的值):
          • #define UP 1
          • #define DWON 10
      1. 宏的缺陷
        • 有参宏: 没有类型检查,注意优先级(内联函数)
          • show(1);  // 没有类型检查的例子
          • // 使用宏记得加括号 #define calc(n) (n+n)
          • #define calc(n) n+n
          • // int number = 4 + 4 * 5 = 24;
          • int number = clac(4) * 5;
        • 定义常量的时候
          • 常量没有类型检查,C++推荐使用const
          • 定义一组有意义的数据时,数据之间没有产生联系,推荐使用枚举
    1. 变量
      • auto int number;      // 表示这是一个自动变量
      • register int number; // 寄存器变量,通常将使用很多的数据作为寄存器变量
      • static int number;      // 静态变量,指挥初始化一次
      • extern int number;     // 声明一个其它(当前)文件已经定义的变量,不能初始化
  2. 数据类型
    1. 常量
      • 字符型
        • 直接字符
          • ''单引号包含的字符
        • 转义字符
          • ''转义
          • '\x00'16进制
          • '\o00'8进制
      • 整型
        • 八进制
          • 数字0开头
        • 十六进制
          • 0x开头
        • 后缀
          • B :二进制数。   binary  
          • Q (O):八进制数。  octonary
          • D :十进制数。  decimal
          • H :十六进制数。  hexadecimal
          • U :无符号
          • L : 长整型
          • UL:无符号长整型
          • LL:longlong
      • 浮点型
        • 小数
      • 字符串型
        • ""双引号包含的字符
    2. 变量
      • 字符型
        • char;wchar_t;
      • 整型
        • short;int;long;long long;
      • 浮点型
        • float;double;
    3. 进制转换
  3. 变量使用
    • 赋值符号= 左右两边类型必须匹配 ,如果是基本类型,可以不匹配, 编译器可以自动隐式转换
    • 类型不匹配时, 可以使用强制类型转换进行赋值
      • 显式转换(强制转换)
        • 显示转换就是人工的进行一个转换
        • int f = (int)123.123;
        • // 浮点数转整数会进行小数截断
      • 隐式转换
        • char a = 'a';
        • // a 被自动转换为 int 类型
        • int b = a;
        • // 在进行计算的时候,表达式的类型和表达式中最大类型相同
        • auto number = 'a' + 123.0 + 456;
        • // 函数传参的时候进行转换
        • void show(int);
        • show('a');       // movsx eax, 97
        • // mov 参数, eax
    • 字符串输入输出
      • 格式化控制符(占位符)
        • %c %d %f %lf %u %hd %s %S(wchar_t*) 
      • 使用 printf 函数
        • printf("%-*.*lf", 10, 2, 1.1);
        • printf("%-10.2lf", 1.1);
        • %-10.2lf => %lf  => double
        • %-10.2lf => 不足10个字符宽度补全,否则不变
        • %-10.2lf => 小数部分精度为2
        • %**-**10.2lf => 左对齐
        • 1.10XXXXXX  X 表示空格
      • 使用 scanf 函数
        • 安全版本: 主要用于防止缓冲区溢出
        • scanf_s(): 需要在字符(串)的后面加上缓冲区大小
  4. 运算符和表达式
    • 运算符的种类
      • 算数运算符(从左到右)
        • [ + - * / % ++ --]
          • 前置++是先自增再运算
        • 为了避免不同编译器的不同解释,不应该在表达式中使用自增\减
    • 赋值运算符(从右到左)
      • a = b = c = d = 0;
      • [ += -= *= /= %= =]
      • a += b * c + d; => a = a + (b * c + d);
    • 逻辑运算符
      • && || !
        • 短路运算:先执行表达式的一部分,根据执行的结果确定是否执行另一部分
      • && 短路运算符:  左边失败就不执行右边
        • 任意一边为假就为假       a10 b10       a==b || a++
      • ||  短路运算符:  左边成功就不执行右边
        • 任意一边为真就为真
        • 关系运算符
          • [ > < = >= < == !=]
    • 三目运算符
      • [表达式 ? 语句一 : 语句二; ]
      • 作用if else 相同,表达式成立执行语句一、否则执行语句二
    • 位运算符
      • [ |(or) &(and) ^(xor) ~(not) >>(ror shr sar) <<]
    • 逗号运算符
      • ,  逗号运算符表达式的值是最后的,但是前面的所有语句都会执行
      • (10 ,1.1, 100, "123") -> "123"
      • (a=10, b=a,c=a+b,++b) -> a(10) b(11) c(20) 表达式 11
    • sizeof 运算符
      • 获取类型的大小
      • sizeof 表达式
      • sizeof(类型)  推荐都加括号
    • 运算符的优先级和结合性
    • 表达式的种类:
      • 赋值表达式: a= 10;  10
      • 算数表达式: 1 + 1; 2
      • 关系表达式: 1 > 2; false
      • 逻辑表达式: 1 && 0: false
      • 函数表达式: func(1) 结果是返回值
  5. 顺序结构
    • 特点:自上而下
  6. 选择结构
  • 特点:多个分支只会执行一个
    1. B ? C : D
      • 优先级
    2. if else if else
      • 单IF
      • 多else结构
      • 通常用于指定的范围的判定
      • 如果if不成立就执行elseif,如果都不成立就执行else
    3. switch
      • case  标签的顺序要求 : 顺序不分先后
      • default 标签表示默认
      • break关键字用于跳出switch结构
      • 通常用于指定值的判定
      • case: default: 
        • case成立就执行case,否则执行default
        • 分支的执行和位置没有任何关系
      • break; 如果在case中没有编写break,那么会顺序往下执行,
        • 直到switch块结束或者遇到break
  1. 循环结构
  • 特点:根据条件循环一定次数
  • 循环的种类:
    • 入口条件循环:在进入循环的时候首先进行判断
      • for:for(语句1; 语句2; 语句3) { 代码块4 }
        • 语句1: 通常用于初始化,只会执行1次
        • 语句2:循环条件,没执行一次循环体,就要判断一次,判断次数比循环次数+1,肯定是最后一个被执行,条件一旦省略就是死循环
        • 语句3:通常用于自增,执行次数和循环次数相同
        • 执行顺序: 1 2 4 3 2 4 3 2 4 3 2....
      • while
      • while(表达式) {代码块}
    • 出口条件循环: 先执行语句块再进行判断
      • do while
        • do { 代码块}while(条件)
        • 先执行代码,在判断条件
    • break和continue
      • break: 可以用于 switch 和循环内,用于直接结束当前最内层的循环
      • continue: 只能用于循环内,用于跳过当前最内层循环的剩下部分,进入下次循环
  • 循环的嵌套
    • 示范
for (int i = 0; i < 10; ++i)
{
    for (int j = i; j < 10; ++j)
    {
        printf("*");
    }
    printf("\n");
}
// 杨辉三角
// 菱形
// 九九乘法表
// 水仙花
  • 死循环的形成条件
    • for(;;): 如果中间的表达式不填写数据,就是死循环
    • while(1): 表达式永远为真,只能通过break(goto return)之类的跳转语句结束
  • while 与 for 的转换
    • 代码
for (int i = 0; i < 3; ++i)
{
    printf("%d ", i);
}
int i = 0;
while (i < 3)
{
    printf("%d ", i);
	++i;
}

Ox20 中级语法

  1. 一维数组
    • 是用连续的空间存放一组类型相同的数据,数组的大小不可变
    1. 字符数组
      • strlen 求长
      • strcpy 拷贝    strcpy_s(VS)   strncpy(CRT)  strncpy_s(VS)
      • strcat 拼接
      • sprintf 字符串输出
      • sscanf 字符串输入
      • strstr 字符串查找
      • gets\puts 输入\输出一行,换行
    2. 一般数组
      • memcpy 内存拷贝
      • memset 内存赋值
      • memmove 内存移动
      • memcmp 内存比较
    3. 数组的初始化
      1. 整数
        • int number[] = {1,2,3}; 大小是由初始化的数据决定的
        • int number[10];   // 没有给值,具体的值与所在位置相关
        • int number[10] = {}; // 写不写0都是0
        • int number[10] = {1,2,3,4}; // 结果是1234000000
      2. 字符串
        • const char* buff1 = "abcde";
        • const char buff2[] = "abcde";
        • const char buff3[] = { 'a','b','c','d','e' };
        • const char* buff4[] = { "abc","def" };
        • int len1 = sizeof(buff1);   //内存占用
        • int len2 = strlen(buff1);   //字串占用
        • int len3 = _countof(buff1);   //单元素占用
    4. 数组的访问
      • 方括号运算符:[]
      • 越界问题: 数组下标从0开始, 下标大于等于数组元素个数就会越界
        • number[9]; 下标从0开始,越界访问可能产生问题
        • number[10] = 0; 可能会产生问题(GS)
          • 可能会修改其它变量的数据
          • 在堆空间中越界修改一定出错
    5. 数组的输入输出
      • 字符串到数字的转换
        • atoi: 只能转十进制
        • strtol: 推荐使用,"\x64"->100  
        • StrToInt:  只能转十进制
        • sscanf: 不推荐
      • 字符串和字符串数组
        • const char *str = "123454678";
          • str 保存在栈区,是一个指针, "12345678"在常量区,str指向了常量区
        • char str[] = "123435678";
          • str在栈区,是一个数组,"12345678"在常量区,执行完毕,str保存的是"12345678";
    6. 数组的遍历
  2. 二位数组
    • 定义和初始化方式(定义时,数组的低维的最大元素个数不能省略.)
      • int number[][10] = {{1, 2}, {3, 4}, {5, 6}};
      • int number[10][10] = {{1, 2}, {3, 4}, {5, 6}};
      • 花括号中嵌套了几个花括号就是给几行进行了赋值
      • 保存的内容如下
    • 二维数组的遍历
    • 二维字符数组
    • 辨别字符串数组的维度
      • {"1", "2", "3", "4"}
        • const char* str[4] = { 0 };   // 存储的是字符串的地址,不能修改
        • char str[4][2];    // 二维数组,保存了所有的字符串,能够修改
  3. 枚举
  • 枚举的定义
    • enum DIR { UP, DOWN, LEFT, RIGHT};
    • 如果不指定起始的值,值从0开始
    • 下一个值是根据当前的值决定的,比如当前是LEFT =2,RIGHT 就是3
  1. 结构体
  • typedef 关键字
    • typedef用于定义别名,通常和结构体以前使用
    • 由于C语言的规定,不支持直接使用结构体的名字进行变量的定义所以,使用typedef 关键字取别名可以简化类型的定义
struct s { int n; };
s obj = { 1 };          // 只能在 C++ 中使用
struct s obj = { 1 };
  • 结构体变量的大小等于结构体每个字段占用大小之和(但是会受到对齐粒度的影响)
  • 结构体的定义
    • 代码
// 结构体通常用于存储有关联但是类型不同的一组数据
struct _TEST{
	int a;
	char b;
};

// 定义了一个结构体并直接产生了一个变量
struct _TEST {
	int a;
	char b;
}abc = {1, 'a'};

// 定义了结构体并且取别名,
typedef struct _TEST {
	int a;
	char b;
}TEST, *PTEST;

// 加了 typedef 就是别名,没加就是变量

TEST test; // 没有初始化
TEST test = { 0 }; // 全都初始化为0
TEST test = { 1 }; // 除第一个全都初始化为0
  • 访问和修改
    • 使用指针: PTEST pTest = &test; pTest->a = 10;
    • 使用结构体本身:Test.a = 10;
  • 大小计算
    • 结构体的总大小是结构体内最大类型的倍数
      • 例如最大的是double,那么结构体大小肯定是8的倍数
    • 结构体的成员所在的偏移和成员本身的类型相关
      • 成员类型为 float, 那么它所在的便宜必须  sizoef(float) 4的倍数
struct _TEST{
	char a;		
	int b;
	char c;
	double d;
	char e;
};

sizeof(_TEST) == 32;
// a X X X b b b b 
// c X X X X X X X 
// d d d d d d d d
// e X X X X X X X

#pragma pack(1)  内核编程和网络传输都可能用到
  1. 联合体
  • 联合体的定义:
    • union u {int n; doube d; char c};
    • 联合体的大小是占用空间最大的字段的大小
    • 每个字段都共用一块内存. 每个字段的首地址是一样的, 但是字段的类型和大小不一样
  • 访问和修改
    • 同一时刻,谁都能访问,但是只有一个数据是有意义的
  • 大小的计算
    • 联合体的大小由最大的类型决定,所以u的大小是8
  1. 联合结构体
  • 组成各个数据结构
  1. 函数
    1. 函数的声明和定义
      • 函数的组成
        • 函数头
          • 返回值: 就是函数调用表达式的结果,返回值必须和返回值的类型相同
          • 函数名: 标识当前的函数,命名规则和变量相同
          • 形参列表: 当前函数接收的参数个数和类型
        • 函数体
      • 声明和定义
        • 声明: void func(int); 声明一个函数没有返回值并且接受一个int参数
          • 声明必须要在使用前,然后函数可以没有声明
          • 当函数的定义位置在调用之前就不需要声明了
          • 声明通常写于头文件,实现 通常位于CPP
        • 定义(实现): 通常是函数原型后加上花括号编写函数体
    2. 函数的调用
      • 函数传参
        • 形参:形式参数,函数内的局部变量,是实参的拷贝
          • 直接修改形参,实参不会改变,可以使用指针间接改变
        • 实参:实际参数,传入的具体的值
void show(int n)
{
    // 这里的n是一个形参
    print("%d\n", n);
}

// 实参的名字和形参没有关系
int number = 10;
// number 就是一个实参
show(number);
show(10);

// 传参结束后,函数内实际
void show(int n)
{
    // 将形参赋值为实参
    // n = number(10);
    // 这里的n是一个形参
    print("%d\n", n);
}

push dword ptr:[number]   // 栈保存的是 number 的值
     - 不同类型的参数传递
        - 数组的传参
           - int arr[] => void show(int *) => void show(int []) 
           - int arr[10][10]
              - void show(int (*a)[10], int size)
              - void show(int [][10], int size)
              - arr[10][10] 每一个元素是一个数组,数组的类型是 int[10]
                 - int(*p)[10]
     - 基本类型的传参
        - void show(想要传入的类型)
     - 结构体的传参
        - void show(结构体的名称 变量名)
        - 不推荐使用结构体的值传递,占位置,其次不好逆向
     - 函数的传参
        - 考验的就是函数指针,通常用于windows编程和STL编程
     - 传参方式
        - 值传递
           - 实参和形参的内存空间是独立的, 在函数内部修改形参不会影响实参.
        - 地址传递
           - 通过指针的方式来传递实参的地址, 因为是指针, 在函数内部可以通过指针的语法来修改实参.
     - 传参时参数个数,类型,顺序需要匹配
  - 函数返回值
     - 函数的返回值类型是void时说明函数没有返回值
     - 函数的返回值也属于将函数内部的值传递到函数的外部. 也有传递方式
        - 值传递
        - 址传递
  1. 函数的递归
      • 函数之间的互相调用就是函数递归
      • 递归必须有结束条件,否则会导致栈溢出
      • 能使用循环解决的问题,尽量不要用递归
  2. 内联函数
      • inline: 在函数前使用inline定义内联函数
      • 优点: 不会产生堆栈,加快执行速度
      • 缺点: 如果内联函数内的代码过多,会导致所在函数的代码膨胀
  3. 作用域与生命周期
    1. 作用域
      • 作用域, 限制了一个变量名的使用范围 , 离开作用域之后,变量就不能再被使用 . 不同作用域的变量名可以同名. 同名时, 默认使用小作用域的变量名.
        • 复合语句作用域(例如for循环的大括号,if语句的大括号等)
        • 函数作用域
        • 文件作用域
    2. 生存周期
      • 自动生存周期
        • 进入作用域就被分配内存空间
        • 离开作用域之后内存空间就被回收
      • 手动生存周期
        • 使用堆空间分配函数:malloc来分配内存空间
        • 使用堆空间释放函数free来释放内存空间
    3. 变量类型
      • 局部变量(定义在函数内部的变量)
      • 全局变量(定义在函数外部的变量)
      • 静态变量, 定义变量时加了static关键字的局部或全局变量
        • 进入作用域后,静态局部变量的内存空间早已被分配, 且再重复进入作用域也不会再次分配空间. 离开作用域后,静态局部变量的内存空间不会被销毁
  4. 堆空间
    • 申请堆空间: malloc
      • malloc: 直接申请空间,没有初始化
      • realloc: 重新申请空间,把原空间的数据拷贝到新的空间
      • calloc : 申请空间并初始化   new int[10]{0}  多了初始化
    • 释放堆空间: free
      • free() 如果不释放会导致内存泄漏

Ox30 高级语法

  1. 一级指针
    1. 定义
      • 在 C 语言中指针之间可以随意赋值,C语言对类型的要求并不严格
      • 指向的类型* 指针的名字 = &指向的变量;
      • 如果目标是常量那么指针必须也是常量
        • char* c;   const char* cc = c;  C++ 中常量可以保存非常量,非常量不能保存常量值的
      • 常量指针和指针常量,const 在*前面还是后面
        • 常量指针:  是一个指针,指向了常量,指针可变,指向内容不可变
          • const int* ptr;    int const * ptr;
        • 指针常量: 是一个常量,类型是指针,指向内容可变,指针本身不可以指向其他东西
          • int * const ptr;
    2. 类型
      • 悬空指针: 指向了被释放的空间
      • 空指针: 指向了 0 的指针
      • 野指针:未初始化的指针
    3. 运算
      • 算术运算
        • 指针 +或- 一个整数
        • +: 指针+1,加的是 sizoef(指针类型)
          • 被加的整数表示一个单位,  这个单位具体是多少字节取决于指针的类型 , 表达式的结果是指针类型
          • pDosHeader + pDosHeader.e_lfanew   错误的写法
        • 指针 - 指针
          • 得到两个地址相隔的元素个数, 不是字节数. 表达式的结果是整型
          • -:指针-1,数组中计算的是它们之间的元素个数,指针减法一般没有意义。
      • 解引用/取内容
        • *运算符
          • 用指针保存的地址作为首地址. 用指针的类型作为字节数, 取出这个地址上的指定大小的内容.
  2. 数组指针
  • 一个指针,指向了数组
    • 保存数组首地址的指针
      • 指针加1时, 1指的是1个数组
    • 定义格式1:  int (*p)[10] ; 表示指向一个一维数组的指针.  p+1时, 1指的是10个int型元素.
    • 定义格式2: int (p)[3][4]; 表示指向一个二维数组的指针, p+1时, 1指的是34个整型.  而 p[0] +1 中的1指的是4个int型元素
  1. 指针数组
  • 一个数组,保存了指针
    • char* arr[10];       // 一个数组,保存的是字符串的首地址
    • const int* arr[10];  // 一个数组,保存的是const int类型数据的地址
  1. 函数指针
    • 保存函数地址的指针. 使得指针可以当成函数一样被调用
    • void show(int);  // 函数原型
      • void (*)(int);
WINBASEAPI
BOOL
WINAPI
CreateProcessA(
    _In_opt_ LPCSTR lpApplicationName,
    _Inout_opt_ LPSTR lpCommandLine,
    _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ BOOL bInheritHandles,
    _In_ DWORD dwCreationFlags,
    _In_opt_ LPVOID lpEnvironment,
    _In_opt_ LPCSTR lpCurrentDirectory,
    _In_ LPSTARTUPINFOA lpStartupInfo,
    _Out_ LPPROCESS_INFORMATION lpProcessInformation
    );

// 指针不区分导入或者导出
typedef BOOL
// WINAPI 表示调用方式 * 表示是指针  PFUNC 是一个函数指针类型
(WINAPI* PFUNC)(
    _In_opt_ LPCSTR lpApplicationName,
    _Inout_opt_ LPSTR lpCommandLine,
    _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ BOOL bInheritHandles,
    _In_ DWORD dwCreationFlags,
    _In_opt_ LPVOID lpEnvironment,
    _In_opt_ LPCSTR lpCurrentDirectory,
    _In_ LPSTARTUPINFOA lpStartupInfo,
    _Out_ LPPROCESS_INFORMATION lpProcessInformation
    );

VOID* test(int n, double* m);
VOID* (*test)(int, double*);
  1. 预处理指令
    • 文件包含
      • #include 实际上进行的是赋值粘贴的操作
      • 有两种方式可以防止头文件的重复包含
        • #pragma(once)
        • #ifdef XXX \ #define XXX \ #endif
    • 宏定义
      • #define 用于定义常量,有参宏或一组有意义的名字
    • 条件编译
      • #if #else #endif # ifdef #ifndef
      • 通常用于根据不同的环境生成代码
    • 特殊的宏
      • LINE
      • FUNC
  2. 文件操作
    1. 打开文件
      • 以只读方式打开文件:r
      • 以只写方式打开文件:w
      • 以可读可写方式打开文件:r+ / w+
      • 文件必须存在才打开文件:r / r+
      • 总是以创建新文件的方式打开文件:w / w+
      • 以续写的方式打开文件 : a+
      • 以覆盖的方式打开文件 : w / w+
      • 以二进制形式打开文件 : b
    2. 读文件
      • 从文件读取一个字符:fgetc
      • 从文件读取一串字符串:fgets
      • 格式化读取: fscanf
      • 从文件读取二进制数据:fread
    3. 写文件
      • 将字符写入到文件:fputc
      • 将字符串写入到文件:fputs
      • 格式化写入: fprintf
      • 将二进制数据写入到文件:fwrite
    4. 文件读写位置操作
      •  获取文件读写位置:ftell
      •  设置文件读写位置:fseek
        • SEEK_SET:开始位置
        • SEEK_CUR:当前位置
        • SEEK_END:结束位置
    5. 关闭文件
      • fclose
    6. 绝对路径和相对路径
      • 绝对路径: 从根目录开始进行查找: 通常是磁盘盘符开始的
      • 相对路径: 相对于当前的工作路径进行的寻址(exe所在的路径)
        • . 代表当前目录
        • .. 代表上层目录
  3. 生存周期和作用域:
    • 局部变量(栈)
    • 全局变量(静态数据区)   .data
    • 静态变量(静态数据区)  .data
    • 堆变量(堆空间)
    • 常量数据区,保存的是所有的常量 .rdata
  4. 其它标准函数
    • clock(time): 获取当前的时间
    • rand/srand: (rand\stdlib)
      • srand通常在初始化的时候调用一次, srand((unsigned int) time(0));
// 使用当前时间初始化随机数种子
srand((unsigned int)time(0));

for (int i = 0; i < 10; ++i)
{
    // 使用当前时间初始化随机数种子
    // srand((unsigned int)time(0));

    // 输出生成的随机数
    printf("%d ", rand());
}
  • _getch/ _kbhit(conio.h)
    • _getch(): 无回显输入
    • _kbhit(): 检查键盘击键,不会接受缓冲区内的输入
if (_kbhit())
    charch = _getch();
  • ctype.h
    • islower: 判断是不是小写
    • tolower: 将字母转换为小写
    • isalpha: 判断是不是字母
    • isdigit: 判断是不是数
  • limits.h 跨平台编程    process.h _beginthreadex