C语言基本数据类型

C语言常量

  • 什么是常量: 固定的值

  • 常量的分类:

    • 整形常量
    //整数
    //1,2,3,4  十进制数
    //特殊格式的整数
    //0123   八进制:0开头
    //08---> 不算
    //0x123  十六进制整数: 0x开头
    //0b10111 2019以下的编译器不支持
    //特殊后缀的整数
    //1u     无符号整数: 没有负数的意思
    //1L     长整形
    
    • 浮点常量
    //实数(包含整数包含小数)
    //1.333  默认是双精度(double)浮点型常量
    //1.33f  单精度浮点常量
    //特殊的表示方式
    //科学计数法
    //1234E-5或者1234e-5,即使0.12345
    //  .E-4;  错误的写法
    
    • 字符常量

      • 转义字符: 是具有特定含义字符,一般是由\和特殊字符组成的具有特殊含义的字符

    //字符边界符 ''
    'A'
    //一般情况只有一个字符
    //转义字符
    //'\t'
    //'\n'
    
    • 字符串

      • 字符串边界符 “”
      • 每一个字符串都会存在看不见的结束符(‘\0’);

C语言变量

  • 什么是变量,可以改变量(其实在C语言充当数据容器的作用)

  • 定义变量的语法: 类型 标识符;

    • 类型

    • 标识符: 就是一个名字

      • 起名字: 见名知意
      • 标识符不能用数字当做开头
      • 标识符不能是C语言中的关键字(if,case,switch,int)
      • 标识符大小是区分
    • 起名字的方式

      • 帕斯卡:大驼峰 PlayMusicByUser —>MFC
      • 驼峰 :playMusicByUser—> 使用这一种命名方式
      • 匈牙利: 特定前缀组成的命名方式
        • g_ : 全局变量 g_iNum;
        • c_: 常量 c_iNum;
    • 数据类型

      • 基本的数据类型

        • int : 整数类型
        • float: 单精度浮点类型
        • double: 双精度浮点类型
        • char: 字符类型
      • 修饰词

        • short(短)和 long(长): 体现数据占用内存(容量的大小)的基础上)
        short iNum=0;
        short int sNum=0;
        long lNum;
        long int liNum;
        //有修饰和没有修饰词是两种不同的数据类型
        //什么是用短的: 想要节省内存
        //什么是用长的: 数据超出了原来表示范围,用long
        
        • unsigned(无符号) 和signed(有符号)
        //无符号:表示不能表示负数
        //一般默认的类型,表示的有符号
        //年龄没有负数-->这种需求
        
        • const(常属性)

          • 常属性的变量定义的时候必须初始化
          • 常属性的变量不能被修改
          • 小提示: 一个程序不能大量出现常量的数—>幻数
      • 注意点:

        • short 和long 只能修饰整形
        • unsigned(无符号) 和signed(有符号) 修饰整数数和字符

    auto/extern/static (后续再说)

进制与数据存储

其他进制与进制转换

  • 十进制: 1 2 3 4

  • 二进制: 3: 0:0 1:1 10:2 11:3

    • 二进制转十进制: 短除法

  • 八进制:8: 10

  • 十六进制

  • 转换的小知识: 二进制转八进制 在十六进制

    • 16:10000 8: 1000 4: 100 2:10 1:1
    • 三个二进制表示一个八进制位
    • 四二进制位一个十六进制位

  • 其他进制转十进制: 加权法

二进制与数据存储的关系

  • 二进制与存储单位

    存储单位 表示含义
    bit :位 一个二进制位,能表示的数字只有0和1
    byte:字节 1B=8b,8个二进制位表示一个字节
    KB: 千字节 1KB=1024B =2^10
    MB:兆字节 1MB=1024KB=2^10KB
    GB: 吉字节 1GB=1024MB=2^10MB
    TB: 太字节 1TB=1024Gb=2^10GB
  • 数据的二进制存储

    • 所有数据在计算中都是用补码来存储
    • 补码: 反码+1
    • 反码: 原码取反(但是符号位不变)
    • 原码: 负数: 引入了符号位,用第一位表示整数,0:正数,1表示负数
    • 正数的二进制码 都是三码合一
    正数:3:(三码合一)
    原码:0000 0000 0000 0000 0000 0000 0000 0011
    反码:0000 0000 0000 0000 0000 0000 0000 0011
    补码:0000 0000 0000 0000 0000 0000 0000 0011
    负数:-3
    原码:1000 0000 0000 0000 0000 0000 0000 0011   //引入符号,最高位表示位
    反码:1111 1111 1111 1111 1111 1111 1111 1100   //原码取反(但是符号位不变)
    补码:1111 1111 1111 1111 1111 1111 1111 1101   //反码+1
    //3-3
    0000 0000 0000 0000 0000 0000 0000 0011
    1111 1111 1111 1111 1111 1111 1111 1101
    0000 0000 0000 0000 0000 0000 0000 0000        //进制位处理(寄存器有标志位处理)
    //注意点: 补码要算出十进制数,必须先转换为原码,再去用加权法
    
  • C语言数据的范围

范围计算: 以char类型为例

char: 1—> 0000 0000

最大值: 0111 1111

1000 0000-1

2^7-1=128-1 =127

-2^7=-128

int: 32–> -231-231-1

C语言基本输入和输出

printf函数

  • #include <stdio.h> C语言标准输入输出头文件

  • printf做原样打印

    //函数
    //printf("你要打印的内容");
    
  • 转义字符

    常用的转义字符 效果
    \n 换行
    \r 换行(当前行首),判断按键是否是回车键
    \t 水平制表

    制表的原理: 通过控制数据的输出宽度,不足填充空格

  • 打印特殊符号

    • 通过\加上特殊符号打印符号
  • 数据的输出

    • 格式控制字符: 占位符
    int  %d
    char %c
    float %f
    double %lf
    字符串  %s
    unsigned int %u
    printf("a=%d,b=%c",1,'A');
    a=%d,b=%c1,  'A'
    a=1,b=A
    //一般情况: 占位符的个数是要和后面数据个数是相同
    
    • 浮点型的格式问题
    %+a.bf
    %-a.blf
    a:输出数据的宽度,不足补空格
    b:小数位
    +: 右对齐 ,不写,默认是右对齐
    -: 左对齐
    

scanf函数

  • scanf基本用法

    • &(shif+7键按出来)取地址符
    • scanf工作原理
      • scanf(“格式控制字符”,“变量的地址”);
      • 输入的格式控制字符不需要转义字符
    • error C4996: ‘scanf’: This function or variable may be unsafe. Consider using scanf_s instead
    • 项目属性–>SDL改为否
  • 字符输入
    • 清除缓冲区—> 在字符或者字符串输入前 做了输入,就需要清空
  • scanf_s: vs给scanf做的增强版本
    • 输入数字类是没什么区别
    • 输入字符类或者字符串必须要加入长度
    • 其他编译器没有scanf_s 只有scanf
  • scanf一次输入多个不同类型的数据
#include <stdio.h>
int main()
{//No.4 不同数据类型输入//数字和字符组合在一起int iNum;char xNum;double dNum;printf("input int,char,double:");//scanf自动区分数据,不需要认为区分scanf("%d %c%lf", &iNum, &xNum, &dNum);printf("iNum=%d,xNum=%c,dNum=%lf\n", iNum, xNum, dNum);printf("input int,char,double:");//%d,%c,%lf //scanf("%d,%c,%lf", &iNum, &xNum, &dNum);//printf("iNum=%d,xNum=%c,dNum=%lf\n", iNum, xNum, dNum);scanf_s("%d %c%lf", &iNum, &xNum,1, &dNum);printf("iNum=%d,xNum=%c,dNum=%lf\n", iNum, xNum, dNum);//No.1 输入单个数据printf("input a num:");//习惯:定义变量的时候给变量初始化(赋初始值)int num = 0;       //定义变量赋值为0int result = 0;result=scanf("%d", &num); //等待键盘输入printf("num=%d\n", num);//No.2 输入多个数据int a, b;printf("input a,b:");result=scanf("%d%d", &a, &b);printf("a=%d,b=%d\n",a,b);//scanf("%d\n", &a);  没有这种学法float fNum;//scanf("%.3f", &fNum); 没有这种学法//No.3 字符输入char cNum;char x;printf("input a char:");//getchar();             //----> 常用scanf("%c", &x);         //清除缓冲区的\n x等于\n//fflush(stdin);            //被淘汰//setbuf(stdin);           //会影响文件操作//这个两个等效上面scanf的作用scanf("%c", &cNum);printf("cNum=%c\n", cNum);printf("input a char:");scanf("%c", &x);scanf_s("%c", &cNum, 1); //长度一般用变量能够存储的数据长度printf("cNum=%c\n", cNum);//字符串--字符数组;return 0;
}

字符处理函数

getchar函数

  • 输入一个字符 ,getchar() 表示输入的字符
  • #include <stdio.h>

putchar函数

  • 输出一个字符 ,putchar(‘A’) 要输出的字符放在括号中
  • #include <stdio.h>

getch函数

  • 输入一个字符,getch() 表示输入的字符,不需要回车确认输入
  • #include <conio.h>
  • 在vs中,getch()要改为 _getch()

ASCII码的知识

  • ASCII码 做整数和字符的转换

C语言运算符和表达式

运算的基本概念

  • 左值和右值以及操作数的概念

    • a=1 a:左值 1:右值

      • error C2166: 左值指定 const 对象 左值一般是变量
      • 右值: 没有什么太多要求
    • 操作:=需要两个数字运算符,操作数2
    • 单目,双目, 三目
  • 优先级:算法: 算表达式的顺序(先算*/ 再算+ -)

  • 结合性: 读法: a=1; 正确应该是把1赋值给a

逗号–>赋值–>逻辑–>条件–>算术–>位–>括号

基本运算符

  • 赋值运算符
a=1;
  • 算术运算符
//+ -
//* /                在写表达式的时候乘法不能省略
//2(3+4)
int a=2*(3+4);        // 在写表达式的时候乘法不能省略
//除法
1/3=0;             //当除法的两遍都是整数的时候,会自动取整
//1/2+2/3+3/4;
double result=1/2.0+2/3.0+3/4.0
//取余:%
int num=1%3         //1  x%n  [0,n-1]--->随机数
//余数的符号
num=1%-2;          //1 余数只和被取余数的正负有关
  • 复合赋值运算符
int a=1;
a+=2;    //a=a+2
a/=2;    //a=a/2
a*=2;    //a=a*2
a=2;
a*=1+2*3; //表达式右边有没有括号,都没关系,都是一个整体
//a=a*(1+2*3); //14
  • 条件运算符

    • 条件表达式只有两个结果: 0(表示不成立) 或者 1(成立)
    • 在计算机中非零值表示成立,只有0(\0)或者NULL 表示不成立
    • 不存在连续操作,例如描述 a大于1并且小于2 1<a<2 错误
      • 1<a<2是永远成立的 1<a 结果[0,1] , [0,1]<2 永远成立
//> <
//不等于 !=  中间没空格,有空格就是错误
//等于 ==
//>=  <=
  • 逻辑运算符
&&: 逻辑与运算--->并且
||:逻辑或运算--->或者
!: 否定      --->成立变成不成立,不成立变成成立
//!3  --> 0
//!0  --> 1
a b a&&b a||b
0 0 0 0
1 0 0 1
1 1 1 1
0 1 0 1

综上:

a&&b : a和b都成立它才能成立 ,其他情况都是不成立

a||b : a和b都不成立它才能不成立 ,其他情况都是成立

逻辑与运算和逻辑或运算都会存在短路现象(计算机偷懒现象)

int a = 2;
a > 1 || (a = 4);  //   a > 1成立 所以a > 1 || (a = 4)成立
a < 1 && (a = 5);  //   a < 1不成立 所以a < 1 && (a = 5)不成立
printf("%d\n", a);

位运算符

目前: 学会运算符方式即可

&: 按位与
|: 安位或运算
~: 按位取反
^: 异或
>>:左移
<<: 右移

左移: 正负数都是右边补0

右移: 正数: 左边补0 负数: 左边补1 不要去纠结为什么,就是一个前辈发现的计算规则,如此而已

特殊运算符

  • ++ –
int a=1;
a++;   //后置   先其他运算,改变自己
++a;   //前置   先改变自己,在做运算
//a++  a=a+1;
a--;
--a;
//a--  a=a-1
int a = 1;
int b = 1;
int result;
result = a++;  //result=a ,在a=a+1
printf("result=%d\ta=%d\n", result, a);  //1 2
result = ++b;  //先b=b+1  在做 result=b
printf("result=%d\tb=%d\n", result, b);  //2 2
a = 1;
result = 3 * a++;
printf("result=%d\ta=%d\n", result, a);
  • sizeof

    • 统计类型占用内存(字节)
    • 他是再编译期完成的
  • ?:
表达式1?表达式2:表达式3
1成立  2
1不成立  3
  • 逗号运算符

    • 有效值 是最右边的值

内存共享问题

#include <stdio.h>
int main()
{int a = 1;int resultFirst = a++ + a++ + ++a + ++a;  //a:3//只看前置,不看后置,再看几部分// 3+3+3+3//resultFirst:3*4=12  a:5//只看前置:a++ + a++ + ++a + ++a 两个前置:a要加两次,运算时a的值3//再看几部分:a++ + a++ + ++a + ++a 有四部分: 4printf("result=%d\ta=%d\n", resultFirst, a); //12 5int b = 1;int resultSecond = b++ * b++ * ++b * b++ * b++;//自己会算就可以//b运算时候: b=2//resultSecond:2^5;printf("result=%d\tb=%d\n", resultSecond, b); //32 6int c = 1;int resultThree = c++ * c-- * c++ * ++c * --c * c++;printf("result=%d\tc=%d\n", resultThree,c);//切记: 不要自己再自己的代码里面写这样的东西return 0;
}

C语言分支结构

if语句

if(表达式)   //如果
{//满足条件执行
}   //尽量带上这个{},不带只管理一条语句
 //No.1 基本if语句if ("我很帅")           //非零表示成立{printf("那么我很帅!\n");}int a = 1;if (a = 0)   //赋值语句充当条件,最终是赋的值充当条件 if(0){printf("赋值语句!\n");}//求最大值:int b;int c;scanf_s("%d%d%d", &a, &b, &c);//No.2 if处理最大值问题int max = a;if (max < b){max = b;}if (max < c) {max = c;}printf("Max=%d\n", max);//No.3 if处理多个条件使用逻辑运算符&& ||//判断一个数字是否能够被3和5整除int num = 45;if (num % 3 == 0 && num % 5 == 0) {printf("满足条件的数字\n");}//判断一个数字是否能够被3但是不能被5整除//num % 3 == 0 && num % 5 != 0//判断一个数字能够被3或者被5整除//num % 3 == 0 || num % 5 == 0

if _else语句

//复合语句
if(表达式)
{//条件成立执行这里  printf("1");
}
else
{//条件不成立执行这里printf("1");
}

if_else if _else

//条件细化
//条件的逐步筛选
if(表达式1)
{//1.
}
else if(表达式2)
{//2.
}
else if(表达式3)
{//3.
}
.....
else
{//
}
//整个语句一条复合语句,只会做一次成功的比较,执行一个地方

switch语句

switch(表达式1)
{case 常量1:  //表达式1 和常量1比较语句1;case 常量2:语句2;case 常量3:语句3;default:其他情况执行的地方;
}
//1.switch执行过程
//2.case后面必须是常量,一般写的数字,或者字符,不能写字符串
//3.switch语句只做一次成功的比较,后面的语句不需要比较,都会一次执行
//4.default的位置是随便放的,一般是放在最小面的
//5.switch语句不执行case和default以外的其他语句

switch常用形态

菜单跳转

 //1.菜单交互while (1) {//菜单绘制printf("------【xxx管理系统】------\n");printf("\t0.退出系统\n");printf("\t1.录入信息\n");printf("\t2.删除信息\n");printf("\t3.查找信息\n");printf("-------------------------\n");//菜单交互int key = 0;scanf_s("%d", &key);switch (key) {case 0:printf("退出系统成功!\n");system("pause");     //防止闪屏 ,等待按键继续exit(0);       //关闭整个程序;break;case 1:printf("---录入信息!\n");break;case 2:printf("---删除信息!\n");break;case 3:printf("---查找信息!\n");break;default:printf("---输入错误,重新输入\n");break;}system("pause");     //防止闪屏 ,等待按键继续system("cls");           //清屏}

按键交互

#include <stdio.h>
#include <stdlib.h>       //system函数头文件
#include <conio.h>
int main()
{while (1) {printf("一个貌美如花的小姐姐朝你走来!---\n");char userkey = _getch();  //不可见的按键交互,不需要回车确认输入switch (userkey) {//相同的case处理方案可以把所有case写在一起case 'w':case 'W':case 72:printf("你朝-->上方-->逃\n");break;case 'A':case 'a':case 75:   printf("你朝-->左边-->逃\n");break;case 's':case 'S':case 80:printf("你朝-->下方-->逃\n");break;case 'd':case 'D':case 77:printf("你朝-->右边-->逃\n");break;}}return 0;
}

变量的作用域和生命周期

  • 作用域: 使用范围

    • 局部变量
    • 全局变量
    • 外部变量:extern
  • 生命周期: 变量产生到死亡的时间段
    • 静态变量
    • 自动变量(auto)
#include <stdio.h>
int g_num;                  //全局变量
extern int e_num;           //告诉当前文件,该变量是外部文件中的变量
//全局变量没有做初始化,默认0
//全局变量不易过多,因为生命周期,只有当程序关闭 才结束
int main()
{//静态变量养成初始化的习惯static  int sNum=1;     //会存放上一次执行结果,初始化操作只会做一次printf("%d\n", sNum);while (1) {static  int sCount = 1;   //只会执行int count = 1;              //每一次循环都执行sCount++;count++;printf("sCount=%d,count=%d\n", sCount,count);}printf("%d\n", g_num);printf("%d\n", e_num);int iNum = 0;               //局部变量{int number = 0;         //局部变量printf("%d\n", number);}int num;//error C4700: 使用了未初始化的局部变量“num”//printf("%d\n", num);        //printf("%d\n", number);  未定义的标识符for (int i;0;) {}//i = 3;  //未定义return 0;
}

C语言循环结构

基本循环

  • while循环
  • do-while循环
  • for循环
while(表达式)
{//循环体  //.....//循环条件的改变-->朝着不满足条件方向走
}
//无论条件成立与否,至少执行一次
do
{}while(表达式);
//for用的多一点
for(表达式1;表达式2;表达式3)
{循环体4;
}
//1  2 4 3  2 4 3  2 4 3
//表达式3写是循环变量的改变
//表达式2:循环条件
//表达式1:循环条件初始化
//--表达式1定义的变量只能在循环中使用

特殊形态

while(1);   //死循环-->一直在循环
while(getchar()!='\n');//可以用来清空缓冲区的换行
do{}while(0);//不执行
for(;;)

循环跳转语句

break;  跳出整个复合语句
continue: 跳出本次,继续复合语句
goto: 跳转   --->深层循环嵌套的跳出
(千万不要有用goto写这种荒谬的想法)

循环嵌套

#include <stdio.h>
int main()
{//No.1行列关系描述//for循环嵌套,原则上来说或任何循环都可以随便嵌套for (int i = 0; i < 4; i++)           //i=0    -------->行的变化{                                 //for (int j = 0; j < 3; j++)     //j=0  j=1  j=2 --->列的变化{                             //printf("%d%d\t", i, j);     //00   01   02}                             //printf("\n");}//00 01 02 \n//10 11 12 \n//20 21 22 \n//30 31 32 \n/*i   j****    1   4    5-i***     2   3    5-i**      3   2    5-i*      4   1*/for (int i = 1; i <= 4; i++) {for (int j = 1; j <= 5 - i; j++) {printf("*");}printf("\n"); //换行才有行列关系}/*      i   j*       1   1  2*i-1***      2   3  2*i-1*****     3   5  2*i-1*******    4   7  2*i-1*/for (int i = 1; i <= 4; i++){for (int j = 1; j <= 5 - i; j++){printf(" ");}for (int k = 1; k <= 2 * i - 1; k++) {printf("*");}printf("\n");  //换行才有行列关系}//No.2 遍历做二次筛选//-->给你一个范围//-->给你条件//100-999  abc  a^3+b^3+c^3=abc;//100-999里面的求素数: 只能被1和自身整除for (int i = 100; i <= 999; i++)  //每次循环从区间里面拿一个数字出来{int flag = 1;                //每个数字的标记都是从1开始for (int k = 2; k < i; k++)   //i=100{if (i % k == 0)           //100%2==0{flag = 0;    //设置一个标记位  //flag=0break;}}//退出里面循环有两种情况: //第一种,里面if执行了(flag=0)//第二种里面一次都没执行(flag=1)if (flag == 1)                       {printf("%d\t", i);}}return 0;
}

C语言一维数组

认识数组

  • 如何创建一个数组
//No.1 数组定义语法
//类型 数组名[数组长度]
int  num[3];
float fNum[3];
double dNum[3];
char cNum[3];
//类型: 学过的数据类型
//数组名: 随便起的
//数组长度:根据需求决定数组长度
  • 数组的长相(在内存上的长相)

    • 数组是什么? 多个变量名有规律并且内存连续的变量的集合(白话:多个普通变量)

  • 数组初始化

    //No.1 创建数组的时候赋值,数组长度和元素个数相同
    int array[3]={1,2,3};   //{}不能省略,arary[0]=1 array[1]=2 array[2]=3
    //No.2 元素个数和长度不同,默认初始化为0
    int array2[3]={1};      //arary2[0]=1 array2[1]=0 array2[2]=0
    //No.3 当初始化的元素完整,数组长度可以省略
    int array3[]={1,2,3};  //自动推断数组长度为3
    //No.4 不存在数组长度为0的数组
    //int array4[];    错误
    //No.5 不能先创建数组在使用{}方式初始化
    //int array[4];
    //array[4]={1,2,3,4};    错误的写法
    
  • 数组的访问

    • 数组名[下标]
    • 一般操作数组都是用循环操作(输入还是打印)

一维数组常规操作

  • 插入—有序插入
#include <stdio.h>
int main()
{int array[100];                //数组能存储的最大元素个数是100个int curSize = 0;            //当前元素是0int data = 0;while (1) {printf("input data:");scanf_s("%d", &data);array[curSize++] = data;  //array[0]=1 curSize=1  //做调整//在数组里面做下标-1操作或者+1操作,需要考虑下标合理性for (int pos = curSize - 1; pos > 0; pos--) {if (array[pos] < array[pos - 1]) { int temp = array[pos];array[pos] = array[pos - 1];  //0-1: -1array[pos - 1] = temp;}else {break;}}int user = 0;while (getchar() != '\n');user = getchar();if (user == 'n' || user == 'N')  //输入n和N退出 输入状态break;}for (int i = 0; i < curSize; i++) {printf("%d\t", array[i]);}printf("\n");return 0;
}
  • 查找+删除(移位)
#include <stdio.h>
int main()
{//查找和删除int array[10] = { 10,20,30,40,50,60,70,80,90,100 };int curSize = 10;//数组的删除叫做伪删除int data = 60;           //要找的数据//数组的查找int pos = -1;            //查找下标一般用-1 ,数组下标会是0for (int i = 0; i < curSize; i++) {if (array[i] == data) {pos = i;       //记录找到元素下标break;}}if (pos == -1) {printf("没有找到相关元素,无法删除!");}else {//在数组里面做下标-1操作或者+1操作,需要考虑下标合理性for (int k = pos; k < curSize-1; k++)   //k=9{array[k] = array[k + 1];         //array[10]}curSize--;          //数组真正的删除}for (int i = 0; i < curSize; i++) {printf("%d\t", array[i]);}printf("\n");return 0;
}
  • 查找+修改
#include <stdio.h>
int main()
{//查找和修改int array[10] = { 10,20,30,40,50,60,70,80,90,100 };int curSize = 10;                                  //记录当前数组中元素个数int data = 60;int pos = -1;for (int i = 0; i < curSize; i++) {if (array[i] == data) {pos = i;break;}}if (pos == -1) {printf("没有找到无法修改!\n");}else {printf("请输入新的值:");scanf_s("%d", &array[pos]);}for (int i = 0; i < curSize; i++) {printf("%d\t", array[i]);}return 0;
}
  • 排序—>冒泡排序
#include <stdio.h>
int main()
{//冒泡排序int array[10] = { 60,70,80,90,100,10,20,30,40,50 };int curSize = 10;//排序//int count = 0;for (int i = 0; i < curSize; i++)          //每个元素都需要一个机会冒泡{for (int j = 0; j < curSize-1-i; j++)  //描述每个元素冒泡过程{//从小到大排序if (array[j] > array[j + 1])  //不满足规则交换{int temp = array[j];array[j] = array[j + 1];array[j + 1] = temp;//count++;}}}for (int i = 0; i < curSize; i++) {printf("%d\t", array[i]);}  return 0;
}

字符数组和字符串

  • 字符数组和数字类数字处理方案相同的
  • 字符串: 存在一个不可见字符\0
  • 字符串的打印: %s去做打印
    • %s的工作原理: 从首地址打印到\0 所以%s对应的是地址
 //No.1 字符数组char array[] = { 'A','B','C' };  //自动推断长度3for (int i = 0; i < 3; i++) {putchar(array[i]);}putchar('\n');//No.2 字符数组操作字符串//字符串有一个不可见字符\0char str[] = "ILoveyou";         //自动长度是:9char inputstr[] = { 'A','B','C','D','\0','A','B','C' };//%s对应的是地址printf("%s\n", &inputstr[0]);    //ABCD  &inputstr[0]到\0printf("%s\n", &inputstr[2]);    //CD    &inputstr[2]到\0printf("%s\n", &array[0]);         //ABC烫烫烫烫烫烫烫烫烫烫烫烫烫烫?// 0    1   2   3    4   5  6   7//{ 'A','B','C','D','\0','A','B','C' }// 数组名代表第一个元素的地址char output[] = "ILoveyou";printf("%s\n", output);   //&output[0]int key = 0;scanf_s("%d", &key);while (getchar() != '\n');      //字符串做输入前做了输入,可以清除缓冲区char username[10];printf("请输入用户名:");//scanf不需要加长度,增强版要加长度 //scanf输入不接受带空格的输入scanf_s("%s", username,10);  printf("name:%s\n", username);//字符串输入函数: gets:输入+puts:打印char passw[20];//所有字符串处理用的数组printf("输入密码:");while (getchar() != '\n');gets_s(passw, 20);     //20一般用的是passw的长度puts(passw);           //自带换行printf("-----\n");

字符串处理函数

  • strcat: 连接
  • strcpy:拷贝
  • strcmp:比较
  • strlen:统计可见长度

C语言二维数组

认识二维数组

  • 如何创建二维数组
//NO.1 如何创建二维数组,固定语法: 类型 数组名[数组长度1][数组长度2];
int array[2][2];
//数组长度1: 2-->行
//数组长度2: 2-->列
//总元素个数:数组长度1*数组长度2
//最大下标是: 数组名[数组长度1-1][数组长度2-1]   array[1][1]
  • 在内存上的长相

常规操作

  • 数组的初始化
  • 数组的遍历
//No.1 完整初始化
int array1[2][2]={1,2,3,4};
//No.2 默认初始化
int array2[2][2]={1,2};
//No.3 带{}默认初始化 ,一个{}代表一行
int array3[3][4]={{1},{2},{3}};   //第一行第一个元素:1,第二个第一个元素:2,第三行第一个元素:3
//No.4 数据完整初始化,数组长度1可以不写
int array[][3]={1,2,3,4,5,6,7,8,9}; //自动推导数组长度1是:3
  • 二维数组的基本操作

    • 行列数据的操作(excel表数据操作)

      • 行求和
      • 列求和
    • 二维数组充当地图
    • 矩阵变化
  • 二维数组操作字符串
    • 初始化
    • 遍历
    • 多个字符串排序

C语言函数

认识函数

//No.1 学会创建函数
函数返回值类型  函数名(函数参数)
{//函数体return  函数返回值;
}
//没有返回值写: void :没有
//没有参数可以不写
void  函数名()  //void 函数名(void)
{//没有返回值不代表没有returnreturn ; //return可以不写,没有返回值可以写//可以结合if语句提前结束函数printf("永远不会执行");
}
//函数返回值类型: 是由return后面值的类型决定
int ReturnInt()
{return 1;
}
double ReturnDouble()
{double d=1.11;return d;
}
char ReturnChar()
{return 'A';
}
//函数名: 和标识符其名的规则一样
//函数参数:
//形参--->创建函数时用的参数(类型修饰的)
//实参--->调用函数时用的参数(没有类型修饰)
int Max(int a,int b)        //形参 int a,int b //int a=1 ,int b=2
{return a>b?a:b;
}
//调用函数: 函数名(实参);  //形参和实参类型必须一致
Max(1,2);           //1,2实参
int a=1,b=3;
Max(a,b);           //a和b叫做实参
//函数调用每一次使用的内存都是不一样。
//所有传参的方式都是用赋值的方式传参  Max(1,2)
#include <stdio.h>
//No.1 无参,无返回的函数,用来做效果,
//这种效果一般可能需要重复使用
void print()
{//return;  函数遇到这个reutrn就结束printf("----------------------\n");printf("\t0.退出\n");printf("\t1.录入\n");printf("\t2.浏览\n");printf("----------------------\n");
}
//No.2 带返回值 带参
//用来做数据的加工
//函数处理后结果需要在调用者中使用,可以同函数返回值体现
int Max(int a, int b)    //int a=aa,int b=bb
{return a > b ? a : b;//2
}
//拷贝本的问题
void modifyNum(int a)   //int a=num;
{a = 1004;         //局部的printf("%d\n", a);
}
void printA()
{//a = 0;
}
int main()
{print();  //函数名(参数)        print();int aa = 1;int bb = 2;int result=Max(aa, bb);    //函数返回值是函数调用表达式代表值//Max(aa, bb) = 101;    //函数返回值是一个值,不能充当左值printf("Max=%d\n", result);printf("Max=%d\n", Max(3, 4)); //Max(3, 4)是一个int的值,所以直接打印printf("Max=%d\n", Max(Max(5, 6), Max(4, 3)));//上面一行等效下面三行int first = Max(5, 6);int second = Max(4, 3);printf("Max=%d\n", Max(first, second));//所有传参方式都是赋值方式int num = 100;modifyNum(num);printf("num=%d\n", num);//printf("a=%d\n", a);return 0;
}

函数传参

#include <stdio.h>
#include <string.h>
//No.1 对于函数的每一个参数自己都要能解释清除
int Max(int a, int b)   //功能:求最大值, 求谁的最大值(a和b的最大)
{return a > b ? a : b;
}
//No.2 传一维数组
//数字类的数组必须要传入数组长度
void printArray(int num[], int arrayNum)
{for (int i = 0; i < arrayNum; i++) {printf("%d\t", num[i]);}printf("\n");
}
//void printArray2(int* num, int arrayNum); 和上面写法等效
//字符类的不需要长度 ,字符串结束标记
//strlen
int my_strlen(char str[])   //str="ABC"
{int count = 0;while (str[count] != '\0')   //str[0]:'A'  str[1]:'B' str[2]:'C'  str[3]:'\0'count++;              //count=1     count=2    count=3return count;                //count=3
}
//int my_strlne(const char* str);   //const常属性
//No.3 函数调用的时候,形参和实参类型必须一致
//不一致会存在强制类型转换 ,如果转换不了直接崩掉
void print(int num)  //int num='A';  //int num="ILoveyou";
{printf("\nnum=%d\n", num);
}
//No.4 二维数组独有传参方式
//行可以不同,但是列必须相同
void printArray2D(int array[][4], int row, int cols)
{for (int i = 0; i < row; i++) {for (int j = 0; j < cols; j++) {printf("%d\t", array[i][j]);}printf("\n");}
}
int main()
{Max(3, 4);         //赋值的方式传参int array[3] = { 1,2,3 };//写一个在函数打印数组printArray(array, 3);int array2[5] = { 1,2,3,4,5 };printArray(array2, 5);char str[] = { "ILoveyou" };printf("%d\t", my_strlen("ABC"));printf("%d\t", my_strlen(str));printf("%zd\t", strlen(str));       //zd  ---unsigned int print('A');//print("ILoveyou");// 一些编译器只会提醒不报错,但是应该把编译器这种提醒当做错误// warning C4047: “函数”:“int”与“char [9]”的间接级别不同// warning C4024: “print”: 形参和实参 1 的类型不同int array2D[3][4] = { {1},{2},{3} };   //其他的值默认初始化为0printArray2D(array2D, 3, 4);//所有的数组为参数,都是传入数组名return 0;
}

函数调用

  • 普通调用: 函数名(实参) ,保持形参和实参的类型和数目一致即可
  • 嵌套调用
    • 成为一个函数参数
    • 自己调用自身(递归调用)
      • 退出性条件
      • 推导公式

标准库中一些函数

  • printf和scanf返回值
  • 随机函数

C语言指针基础篇

什么是指针

指针就是一个地址,如何获取地址?用的是&:取地址符

  • 指针就是一个整数
  • 获取指针:&

指针变量

存放地址的,也就是存放一个特定的整数(这个的整数可以表示地址)

  • 如何产生一个指针变量

​ 类型* 变量名;

​ 类名 *变量名;

  • 指针变量的两个重要概念

    • 指针的类型: 去掉变量名
    • 指针所指向的类型:去掉变量名和*号

​ 用指针的时候需要保持上述两个类型的一致

int* p;
//类型: int*
//所指向的类型: int  --->本质就是指针所操作数据类型
int(*pp)[3];  //--->指针
//int(*)[3];
//int[3]--->pp操作的就是一个数组
int* pArray[3];//--->数组
//上面两个本节课不需要考虑如何使用,下节课在做研究
  • 不同类型的指针变量

    • 所有类型指针占用的字节数都是相同 32位系统下都是4个字节
    • 特殊的指针类型: void*
    • 专门用来初始化指针变量的东西:NULL
      • 防止悬浮指针
      • 防止野指针
    • 指针变量,获取当前地址中值: *指针变量

指针的运算

  • *指针变量: 指针变量所指向的内存存储的值
  • p+n或者p-n操作:实质是内存字节偏移,和指向内存存储的数据类型有关
    • p+sizeof(指针所指向的类型)*n
    • 对于一个指针变量来说,不会单独的去偏移,一般要指向一段内存做偏移(数组)
    • 对于不同类型的指针之间是没有什么算术运算
    • p++ 和p-- 也算p+n

内存四区

#include <stdio.h>void print()
{static int num = 1;       //这个代码运行的时候只执行一次//不做初始化默认为0num++;printf("%d\n", num);
}char* returnPoint()
{//返回局部变量地址,不允许的//函数调用完,内存会被系统回收(清除所有数据)char array[10] = "ILoveyou";//%s 打印方式,从首地址开始,打印到\0char* p = &array[0];//把数据存到堆区,返回堆区这段内存的首地址return p;
}
int main()
{print();       //num=2;print();       //num=3;//num = 3;        //错误静态变量有作用int* p = NULL;  //0存放在常量区//*p = 1234;      //不能修改0所在的内存   写入访问权限冲突//printf("%d", *p);    //------------------------------------------------------char* str = "ILoveyou";  //解析: 把这段内存的首地址赋值给指针变量char* pchar;puts(str);pchar = "ILoveyou";//*pchar = 'M';        //写入访问权限冲突。puts(pchar);//-------------------------------------------------------char array[10] = "ILoveyou";pchar = &array[0];*pchar = 'M';puts(array);int* result = returnPoint();puts(result);puts(result);puts(result);puts(result);return 0;
}

万能指针

  • 万能指针就是void* 类型的指针变量
  • 万能指针在访问数据的时候必须做强制类型类型
#include <stdio.h>
int main()
{int num = 10;void* pVoid = &num;    //解析方式: pVoid=&num; 不是*pVoid=&num;printf("%d\n", *(int*)pVoid);double dNum = 1.11;pVoid = &dNum;printf("%.3lf\n", *(double*)pVoid);//万能指针使用的时候要强制转换为目标类型(就指向数据类型的指针)int number = 0x00410042; //高低字节:左边:高  右边:低printf("%d\n", number);void* p = &number;char* pp = (char*)p;//两位十六进制位是不是一个字节//一个十六进制位是4个二进制位//8个二进制位是不是一个字节  8个二进制位是不是2个十进制数//42 00 41 00printf("%c\n", *pp);   //42 -->B    //0000005B9FDBF5E4  低地址char* pc = (char*)p;        printf("%c\n", *(pc + 2));//41-->A    //000005B9FDBF5E6   高地址// -->小端模式  高字节存放到内存地址高的地址上// -->大端模式  高字节存放到内存地址低的地址上//十进制:1235//1高位 5低位printf("%p\n", pp);      //0000005B9FDBF5E4printf("%p\n", pc + 2);    //0000005B9FDBF5E6//万能指针应用: 统一接口(统一函数传参)--->目前不需要大家掌握//malloc(void* p,int arrayNum); 会使用这样的函数即可,不需要自己会写//数据类型在讲数据类型讲过return 0;
}

C语言指针与数组

指针操作数组

  • 指针操作一维数组
//int array[3]
//数组名: 代表了这段内存首地址  --->指针
//&array[0]
//正常操作: 直接存储数组的首地址
//直接把指针当作数组名就可以了
//非正常操作:指向其他元素开始操作数组#include <stdio.h>
int main()
{int array[3] = { 1,2,3 };printf("%p\n", array);printf("%p\n", &array[0]);int* p = array;for (int i = 0; i < 3; i++) {//printf("%d\t", p[i]);  //--->这种方式使用不会错误printf("%d\t", *(p + i));//*(p+i) 等效p[i];}printf("\n");//array = array + 3;  不能修改//array-->&array[0]//array+3-->&array[3];for (p = array; p < array + 3; p++) {printf("%d\t", *p);//*p等效p[0]  取当前地址对应内存中值}printf("\n");int test[3] = { 1,2,3 };int* pT = &test[2];printf("%d\n", pT[0]);   //指针操作数组,用数组用法,[0]下表不一定是第0个元素printf("%d\n", pT[-1]);printf("%d\n", pT[1]);    //未知量  -858993460return 0;
}
//一级指针操作字符串char str[] = "ILoveyou";char* pstr = str + 3;puts(pstr);//[]操作等效取*运算putchar(pstr[0]);   //pstr[0] 等效*pstr操作,等效取当前地址下面值//*pstr 等效*(str+3) 等效 str[3];char* lastchar = str + 8;putchar(lastchar[0]);pstr = str;printf("\n");puts(pstr);
  • 指针操作二维数组
//二维数组基础知识
//数组名:指针数组的指针
//行地址:数组名[i];
//一级指针操作二维数组 (二维数组在内存连续的)
//数组指针操作二维数组
#include <stdio.h>
//void printArray2D(int p[][3], int cols, int row)
void printArray2D(int(*p)[3], int cols, int row)
{for (int i = 0; i < 2; i++){for (int j = 0; j < 3; j++){printf("%d\t", p[i][j]);   //自己写代码这样就行,怎么方便怎么来}printf("\n");}
}int main()
{int array[2][3] = { 1,2,3,4,5,6 };int* p = &array[0][0];//warning C4047: “=”:“int *”与“int (*)[3]”的间接级别不同//p = array;printf("%p\n", array);printf("%p\n", &array[0][0]);printf("%p\n", array[0]);printf("%p\n", array + 1);   //12/4=3printf("%p\n", p + 1);        //4printf("%p\n", array[0] + 1);//char* pC = &array[0][0];//int* pI = &array[0][0];for (int i = 0; i < 2; i++){for (int j = 0; j < 3; j++){printf("%d\t", *(p + j + i * 3));//j+i*3 把坐标转为第几个元素//p+n 等下偏移}printf("\n");}int(*pArray)[3] = NULL;    //括号必须要有,列数和表示的数组的列数必须相同//先算() ,他是一个子指针, 数组指针  指向数组的指针//联想整形指针,指向整数pArray = array;//直接当作数组名去名printf("-------------------------------\n");for (int i = 0; i < 2; i++){for (int j = 0; j < 3; j++){printf("%d\t", pArray[i][j]);   //自己写代码这样就行,怎么方便怎么来}printf("\n");}printf("-------------------------------\n");for (int i = 0; i < 2; i++){for (int j = 0; j < 3; j++){printf("%d\t", *(*(pArray + i) + j));//一维数组:p[i] 等效 *(p+i)//printf("%d\t", *(pArray[i] + j));//取* 等效数组下表运算 *(pArray+i)  (pArray+i)[0]//printf("%d\t", *((pArray + i)[0] + j));//printf("%d\t", ((pArray + i)[0] + j)[0]);//printf("%d\t", (pArray[i] + j)[0]);//看到会读就OK}printf("\n");}printf("-------------------------------\n");printArray2D(array, 2, 3);//存储多个字符串--->指针数组 多个指针变量//整形数组: 多个整形变量char* pstr[3] = { "Iloveyou","IMiss","IiIIII" };  //每一行上面字符串可以不相同,并且不浪费//int array[3]  array[0] array[1] array[2]//pstr[0] pstr[1] pstr[2]; 分别存储的是每个字符串首地址for (int i = 0; i < 3; i++) {puts(pstr[i]);}//字符串二维数组也可以存储多个字符串? 列数相同 会导致浪费,多余内存return 0;
}

二级指针

//int** p=NULL;
//int*** pp=NULL;
#include <stdio.h>
int main()
{int a = 1;int* p = &a;       //一级指针变量存储的普通变量的地址int** pp=&p;     //二级指针变量存储一级指针变量地址//*地址 --->得到当前地址对应内存中的值printf("%d\t%d\n", *p, p[0]);printf("%d\t%d\n", **pp, pp[0][0]);int array[2][3] = { 1,2,3,4,5,6 };//一级指针算偏移//数组指针做遍历// warning C4047: “=”:“int **”与“int (*)[3]”的间接级别不同// 所有这种提醒麻烦大家当作错误处理 C++当作是错误的//pp = array;//for (int i = 0; i < 2; i++) //{//  for (int j = 0; j < 3; j++) //    {//     printf("%d\n", pp[i][j]);//   }// printf("\n");//}return 0;
}

动态内存申请

  • 动态内存申请的函数
//No.1 断言
#include <assert.h>
assert(p);      //p等于 触发断点,中断程序 断言#include <malloc.h>
//#include <stdlib.h>
//No.2 malloc  动态内存,不做初始化
void*  malloc(size_t _Size);  //size:申请内存的总字节数
//No.3 calloc  动态内存申请,会初始化为0
void*  calloc( size_t _Count,size_t _Size); //有几个数据:count,每个数据占用的字节数:size
//No.4 realloc  内存重新申请,保留原数据
void*  realloc(void*  _Block,size_t _Size); //原指针,重新申请的内存大小
//No.5 释放内存函数
void  free(void* _Block);
//__cdecl 调用准则,限定参数传参顺序(入栈顺序)--->了解一下
//_In_ 参数类型修饰 传入
//_out_ 传出参数
//size_t  unsigned int
//void* 使用前必须强制类型转换
  • 一维数组
  • 二维数组

C语言函数与指针

指针函数

指针函数就是用到指针函数,它是一个函数

  • 指针当作函数参数(如何修改实参)

  • 指针当作函数返回值

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
void modify(int count)  //int count=1
{count = 111;
}
void modifyCount(int* p) //int *p=&count
{*p = 999;
}
//址传递
//值传递
/*子函数想要修改普通变量实参,需要传入普通变量地址:一级指针,在修改的是普通变量子函数想要修改指针变量实参,需要传入指针变量地址: 二级指针,在修改的是一级指针
*/
int g_num = 1992;
void modifyPoint(int** p)
{//修改了一级指针变量值*p = &g_num;
}
//---->无头链表
//当做函数返回值--->不能返回局部变量地址
//warning C4172: 返回局部变量或临时变量的地址: num
int* returnPoint()
{int num = 100;        //局部变量, 100会被回收return &num;
}int* returnPoint2(int* p)
{return p;
}
//也是被允许,堆区内存不会自动回收
int* returnMalloc()
{int* p = (int*)malloc(sizeof(int));assert(p);*p = 11334;return p;
}
int main()
{int count = 1;modify(count);printf("%d\n", count);modifyCount(&count);printf("%d\n", count);  //999int* p = &count;printf("%d\n", *p);modifyPoint(&p);printf("%d\n", *p);int* result = returnPoint();printf("%d\n", *result);printf("%d\n", *result);printf("%d\n", *result);result = returnPoint2(p);int* pMory = (int*)malloc(sizeof(int) * 3);result = returnMalloc();free(result);result = NULL;free(pMory);pMory = NULL;return 0;
}

函数指针

函数指针是什么

函数指针: 指向函数的指针,(函数名就是函数指针,&函数指针)

作用: 用来指向函数,调用函数

如何创建一个函数指针变量

(*指针名)替换函数名 剩下照抄,形参名可以省略,这就是函数指针

通过和函数指针调用函数

  • 直接用指针名替换函数去调用函数
  • (*指针名)替换函数名调用函数

函数指针一般是用在充当函数的参数

(专业术语叫做回调函数)

typedef与函数指针

  • typedef基本用法
  • typedef与函数指针
#include <stdio.h>void print(int(*p)(int, int), int a, int b)
{printf("%d\n", p(a, b));
}
//和上面函数一样的
typedef int(*FUNC)(int, int);
void print2(FUNC p, int a, int b)   //FUNC p     int(*p)(int, int);
{printf("%d\n", p(a, b));
}
//typedef 给结构体起别名 后面讲
int main()
{//No.1 给基本数据类型起别名typedef int 整数;int a1 = 1;整数 a2 = 1;            //int  a2 = 1//typedef unsigned int  size_t;size_t a3 = 1;//No.2 数组起别名typedef int ARRAY[3];ARRAY arr = { 1,2,3 };                    // int arr[3];   int arr[3];for (int i = 0; i < 3; i++) {printf("%d\n", arr[i]);}ARRAY array[3] = { 1,2,3,4,5,6,7,8,9 };   // int array[3][3];typedef int A2D[3][3];A2D array2d;                           // int array2d[3][3];return 0;
}

万能指针充当函数指针

  • 万能指针: 使用必须做强制类型转换
  • 指针类型: 去掉变量名

C语言自定义类型

结构体

  • 什么是结构体 (不同类型的变量集合,数组: 相同类型变量集合)

  • 创建结构体的语法
struct 结构名
{//结构体成员: 变量
};
//结构体一般描述是一系列事物的抽象
//抽象的是共同的属性(特征-->数据描述)
  • 如何结构体:充当记录复杂数据功能

    • 定义一个结构体变量

      • 普通结构体变量: 类型 变量名;
      • 指针变量: 类型* 变量名;
      • 数组: 类型 数组名[数组长度];
    • 如何访问结构体中数据
      • 结构体中数据只能通过结构体变量(当然指针算是特殊结构体变量,数组特殊结构体变量)
      • 访问方式只有两种
        • 变量.成员
        • 指针->成员 ->:指针指向运算符
  • 结构体变量的初始化
    • 定义的初始化,必须类型上要和成员顺序一致
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <malloc.h>
struct student
{char name[20];int age;int math;int score;
};int main()
{//No.1 定义变量struct student  memory = { "Iloveyou",18,59,59};struct student joker;//先创建后初始化,成员变量以前是怎么用,现在就怎么用//前缀的含义是主体,表示整个成员是谁。//joker.name = "joker";  //字符数组不能直接复制strcpy_s(joker.name, 20, "joker");joker.age = 18;joker.math = 99;joker.score = 99;//No.2 用户输入结构体变量struct student sun;printf("请输入学生信息:");scanf_s("%s%d%d%d", sun.name, 20, &sun.age, &sun.math, &sun.score);printf("%s\t%d\t%d\t%d\n", sun.name, sun.age, sun.math, sun.score);//No.3 结构体数组--->管理表格数据printf("请输入3个学生信息:");struct student array[3];for (int i = 0; i < 3; i++) {scanf_s("%s%d%d%d", array[i].name, 20, &array[i].age, &array[i].math, &array[i].score);}printf("姓名\t年龄\tmath\tscore\n");for (int i = 0; i < 3; i++) {printf("%s\t%d\t%d\t%d\n", array[i].name, array[i].age, array[i].math, array[i].score);}printf("姓名\t年龄\tmath\tscore\n");for (int i = 0; i < 3; i++){printf("%s\t%d\t%d\t%d\n", (array + i)->name, (array + i)->age, (array + i)->math, (array + i)->score);}//No.4 指针struct student* p = NULL;//p->age = 12;   //错误,空里面没有这个age//结构体指针变成结构体变量//4.1 指向普通变量p = &memory;printf("%s\t%d\t%d\t%d\n", p->name, p->age, p->math, p->score);printf("%s\t%d\t%d\t%d\n", (*p).name, (*p).age, (*p).math, (*p).score);//->: 怎么打出来 先打减号 在打大于号//4.2 动态内存申请struct student* pM = (struct student*)malloc(sizeof(struct student));assert(pM);scanf_s("%s%d%d%d", pM->name, 20, &pM->age, &pM->math, &(*pM).score);printf("%s\t%d\t%d\t%d\n", pM->name, pM->age, pM->math, pM->score);free(pM);pM = NULL;return 0;
}

联合体

#include <stdio.h>
#include <stdlib.h>
//联合体:共用体
union  MM
{char name[20];int age;
};
//基本不用,但是网络库中int main()
{//所有数据成员用的是同一段内存,最长的那个变量的内存//所以定义的时候不能直接初始化两个数据//任何时候共用体只有一个数据有效union MM mm = { "A" };//union MM mm2 = { "A" ,68};  //错误的写法printf("%d\n", mm.age);//union MM mm2 = { 68 };   //00 00 00 44    //44 00 00 00//printf("%s", mm2.name);return 0;
}

枚举类型

#include <stdio.h>
#include <stdlib.h>
//enum 枚举类型名{符号1,符号2,...};
enum COLOR{Red,Green,Blue,Yellow};
//枚举类型就是一个常量,就是整数
//默认初始化: 从0开始一次初始化
//增加代码可读性,防止幻数-->大量的常量
//手动初始化
enum  Eement {Wall=1,Road=0,Box=4,End=3,People=5};
//部分初始化,部分默认
//不写的是写了+1
enum NUM {a=1,b,c=4,d};
//b=a+1:2
//d=c+1:5
void print(int num)
{printf("%d\n", num);
}
//C语言把枚举理解int
void printEnum(enum COLOR cnum)
{printf("%d\n", cnum);
}
//enum class
int main()
{printf("red:%d\n", Red);printf("Green:%d\n", Green);printf("Blue:%d\n", Blue);printf("Yellow:%d\n", Yellow);printf("%d\t%d\t%d\t%d\n", a, b, c, d);print(Red);printEnum(a);printEnum(1);return 0;
}

位段

#include <stdio.h>
//特殊的结构体:内存精确到二进制位
//作用:节省内存
struct Data
{unsigned int a : 3;  //_ _ _   000  111:7unsigned int b : 1;  //0 1unsigned int c : 4;  //0000 1111:15
};
//寄存器的描述
//标志位
//12*4=48
//4个字节int main()
{struct Data data = { 8,1,18 };printf("%d,%d,%d", data.a, data.b, data.c);return 0;
}

内存对齐

增加无用字节去补齐内存

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//1.建议: 小的内存写上面,大的写下面
//字符可以拆分,算最小的
struct A
{char name[5];      //整数和字符可以合一起int a;double b;
};
struct B
{char name[20];//8//8//4补4double b;  //8int a;     //4补4
};
//2.内存对齐并不重要,一般只会在考题中,写程序算占用内存一个sizeof搞定
//3.为什么需要内存对齐-->地址总线--->有兴趣可以自己了解
struct C
{int* p;                //x64 :8 -->64位char str[10];     //x86--->32位int a;x86--->32位//4//4//4//2 补齐2//4
};
//骚操作
struct Data
{int a;char str[3];int b;
};
void test()
{struct Data pData = { 1,"A",4 };void* p = &pData;int* pInt = p;printf("%d\n", *pInt);       //pData.a;pInt++;printf("%s\n", (char*)pInt);//pData.str;pInt++;  printf("%d\n", *pInt);        //pData.b;struct Data* pp = &pData;printf("%d\n", pp->a);printf("%s\n", pp->str);printf("%d\n", pp->b);
}int main()
{test();printf("\nA:%zd\n", sizeof(struct A));printf("B:%zd\n", sizeof(struct B));printf("B:%zd\n", sizeof(struct C));struct B* p = (struct B*)malloc(sizeof(struct B));assert(p);free(p);p = NULL;return 0;
}

C语言链表

什么是链表

链表是一种物理存储单元上的非连续,非顺序的存储结构,数据元素的逻辑顺序通过链表中指针连接的。

通俗一点: 链表就是多个结构体变量通过结构体指针连接起来

链表节点组成

  • 数据域
  • 指针域
struct student
{char name[20];int age;int num;
};
struct Node
{//struct student dataint data;          //数据域,应用链表时候,这个数据是一个结构体struct Node* next;   //指针域,
};

简单链表

#include <stdio.h>
#include <assert.h>
struct Node
{int data;struct Node* next;
};
int main()
{int a = 1;int* pa = &a;//正常操作struct Node data = { 1,NULL };     //结构体变量struct Node* p = &data;printf("%d\n", p->data);            //通过结构体指针访问数据printf("%p\n", p->next);//链表就是多个结构体变量struct Node node1 = { 1,NULL };     //节点1struct Node node2 = { 2,NULL };       //节点2struct Node node3 = { 3,NULL };       //节点3node1.next = &node2;node2.next = &node3;//链表的打印struct Node* pmove = &node1;printf("第一个节点数据:%d\n", pmove->data);printf("第二个节点数据:%d\n", pmove->next->data);printf("第三个节点数据:%d\n", pmove->next->next->data);//上面不太方便,用循环来做while (pmove != NULL) {printf("%d\t", pmove->data);pmove = pmove->next;     //pmove=pmove->next}//强调比较重要的概念//指针怎么变成变量//1.指向变量,*指针等效变量int a = 1;int* pa = &a;//2.动态内存是申请一个变量内存int* pM = (int*)malloc(sizeof(int));assert(pM);*pM = 134;return 0;
}

链表的分类

根据第一个节点是否存放了有效数据

  • 有头链表:表头永远都不会变量
  • 无头链表: 表头会改变

(无头链表,双向链表,循环双向链表,有序链表)

链表实现代码封装

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//链表基础数据结构
//1.封装单一个体
struct Node
{int data;struct Node* next;
};
//2.创建表头-->创建一个结构体变量
//有头链表,第一个节点不存放数据
struct Node* createHead()
{struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));assert(headNode);//(*headNode).data = 0;  存储当前链表的节点数headNode->next = NULL;return headNode;
}
//3.创建节点,为插入做准备
struct Node* createNode(int data)
{struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));assert(newNode);newNode->data = data;newNode->next = NULL;return newNode;
}
void insertByHead(struct Node* headNode,int data)
{struct Node* newNode = createNode(data);      //调用创建节点的函数,把用户的数据变成一个节点newNode->next = headNode->next;headNode->next = newNode;
}
void insertByTail(struct Node* headNode, int data)
{if (headNode == NULL){printf("链表不存在的!\n");return;}struct Node* newNode = createNode(data);struct Node* tailNode = headNode;while (tailNode->next != NULL) {tailNode = tailNode->next;}tailNode->next = newNode;
}
//很多种方式
//1.以数据作为参照
void insertByAppoin(struct Node* headNode, int posData, int data)
{struct Node* preNode = headNode;struct Node* curNode = headNode->next;//条件不能交换//curNode等于NULL,NULL->datawhile (curNode != NULL && curNode->data != posData) {//preNode = preNode->next;//curNode = curNode->next;//等效下面写法preNode = curNode;curNode = preNode->next;}//分析结果if (curNode == NULL) {printf("没有找到指定节点,无法插入!\n");}else {struct Node* newNode = createNode(data);preNode->next = newNode;newNode->next = curNode;}
}
//2.以第几个节点作为参照
void insertByIndex(struct Node* headNode, int index, int data)
{if (index <= 0){printf("序号有问题,不能插入!");return;}int count = 1;                         //不存在第0个元素struct Node* preNode = headNode;     //头结点struct Node* curNode = headNode->next; //头结点下一个while (curNode!=NULL&&count < index) {preNode = curNode;curNode = preNode->next;count++;}if (curNode == NULL) {printf("序号有问题,无法插入!\n");}else {struct Node* newNode = createNode(data);preNode->next = newNode;newNode->next = curNode;}
}
//删除
void deleteByHead(struct Node* headNode)
{//.c文件没有省略struct的写法,.cpp才有省略struct写法struct Node* nextNode = headNode->next;if (headNode==NULL||nextNode == NULL) {printf("链表为空,无法删除!\n");return;}else {headNode->next = nextNode->next;free(nextNode);nextNode = NULL;}}
void deleteByTail(struct Node* headNode)
{struct Node* preNode = headNode;struct Node* tailNode = headNode->next;if (headNode == NULL || tailNode == NULL){printf("链表为空,无法删除!\n");return;}while (tailNode->next != NULL) {preNode = tailNode;tailNode = preNode->next;}preNode->next = NULL;free(tailNode);tailNode = NULL;
}
void deleteByAppoin(struct Node* headNode,int posData)
{struct Node* preNode = headNode;struct Node* curNode = headNode->next;if (headNode == NULL || curNode == NULL){printf("链表为空,无法删除!\n");return;}while (curNode != NULL && curNode->data != posData) {preNode = curNode;curNode = preNode->next;}if (curNode == NULL) {printf("未找到指定位置无法删除!\n");}else {preNode->next = curNode->next;free(curNode);curNode = NULL;}
}
//删掉所有的相同的
struct Node* searchNode(struct Node* headNode,int posData)
{struct Node* pmove = headNode->next;while (pmove != NULL && pmove->data != posData) {pmove = pmove->next;}return pmove;
}
void deleteAll(struct Node* headNode, int posData)
{while (searchNode(headNode, posData) != NULL) {deleteByAppoin(headNode, posData);}
}
//销毁链表
void destoryList(struct Node** headNode)
{while ((*headNode)->next != NULL){deleteByHead(*headNode);}free(*headNode);*headNode = NULL;
}
//打印链表
void printList(struct Node* headNode)
{struct Node* pmove = headNode->next;   //因为是有头的,所以要从第二个开始打印while (pmove != NULL){printf("%d\t", pmove->data);pmove = pmove->next;}printf("\n");
}
void print(int data)
{printf("%d\n", data);
}
int main()
{int a = 1;print(a);print(a);print(a);struct Node* headNode = createHead();  //这就是创建的链表for (int i = 1; i < 3; i++) {insertByHead(headNode, i);}printList(headNode);insertByTail(headNode, 1);insertByTail(headNode, 2);insertByTail(headNode, 3);printList(headNode);//321123insertByAppoin(headNode, 1, 666);printList(headNode);insertByIndex(headNode, 1, 888);printList(headNode);deleteByHead(headNode);printList(headNode);deleteByTail(headNode);printList(headNode);deleteByAppoin(headNode, 666);printList(headNode);deleteAll(headNode, 1);printList(headNode);destoryList(&headNode);printf("%p\n", headNode);return 0;
}

链表写管理系统

写好链表后,只需要修改和数据有关的操作,一般使用链表只需要以下操作

  • 创建头和创建节点
  • 一种插入方式
  • 一种指定位置删除
  • 查找
  • 遍历
  • 链表排序:冒泡排序等都可

C语言文件操作

文件基础

文件操作: 针对磁盘是文件做一个读写操作

  • 标准输入: stdin —>键盘输入
  • 标准输出: stdout—>控制台
  • 自定义文件: FILE* fp

文本文件

二进制文件

1.文件打开

FILE* fopen(const char* fileName,const char* mode);
//filename: 文件路径
//mode: 读写方式
/************************************************************
*       w:  写的方式(write) 创建文件方式写,原文件存在就清空
*       r:  读的方式(read)
*       a:  追加模式(append) 不清空原文件写操作,在原文件末位接着写+可创建
*       +: 代表可读可写
*       w+ r+ a+
*       b: 二进制的方式
*       wb ab rb wb+ ab+ rb+
*************************************************************/
//注意点: C语言打开文件不具备创建文件夹的功能

2.文件关闭

int fclose(FILE* file)

3.文件结束标记: EOF

#include <stdio.h>
int main()
{FILE* fp=NULL;fp = fopen("1.txt", "w");if (fp == NULL) {printf("文件打开失败!\n");return 0;}fclose(fp);fp = fopen("append.txt", "a");if (fp == NULL){printf("文件打开失败!\n");return 0;}fclose(fp);fp = fopen("read.txt", "r");printf("%d\n", EOF);if (fp == NULL){printf("文件打开失败!\n");return 0;}return 0;
}

文件读写

注意点:所有文件操作底层都做了文件指针移动,一般不需要大家手动做文件指针移动(fp++错误)

  • 以字符和字符串的方式读写

    • 字符的方式
    //fgetc(): 读操作
    //fputc(): 写操作
    int  fgetc(FILE* _Stream);
    //返回值: 读写的字符
    //参数: 文件指针,读的是那个文件
    int  fputc(int   _Character,FILE* _Stream);
    //打印的字符是什么:_Character
    //打印到哪里: 文件指针指向的文件
    
    • 字符串的方式
    //fgets();
    //fputs();
    int  fputs( char const* _Buffer,FILE* _Stream);
    //_Buffer写内容
    //_Stream哪个文件
    char*  fgets(char* _Buffer,int   _MaxCount,FILE* _Stream);
    //读的内容存到那里: _Buffer
    //容器大小:_MaxCount
    //从那里开始读的
    _Stream
    
#include <stdio.h>
int main()
{FILE* fp=NULL;fp = fopen("1.txt", "w");if (fp == NULL) {printf("文件打开失败!\n");return 0;}fclose(fp);fp = fopen("append.txt", "a");if (fp == NULL){printf("文件打开失败!\n");return 0;}fclose(fp);fp = fopen("read.txt", "r");printf("%d\n", EOF);if (fp == NULL){printf("文件打开失败!\n");return 0;}return 0;
}
  • 格式化读写

    格式化读写(表格数据读写)不适用于文件指针移动

    int fprintf(FILE* fp,const char* mode,...);
    //fp: 打印到文件指针指向文件
    //mode: 格式控制字符
    //...: 参数名表,多个变量
    int fscanf(FILE* fp,const char* mode,...);
    //fp: 读的哪个文件
    //mode: 按什么格式读
    //...: 参数地址表(多个变量的地址)
    //注意点:
    //1.怎么写怎么读就行了
    //2.读操作不支持精度控制
    
    //结构数据的读写
    #include <stdio.h>
    #include <assert.h>
    struct MM
    {char name[20];int age;int num;
    };
    void writeFile(struct MM array[], int arrayNum, const char* fileName)
    {FILE* fp = fopen(fileName, "w");assert(fp);for (int i = 0; i < arrayNum; i++) {fprintf(fp, "%s\t%d\t%d\n", array[i].name, array[i].age, array[i].num);}fclose(fp);
    }
    void readFile(const char* fileName)
    {FILE* fp = fopen(fileName, "r");assert(fp);struct MM temp[3];int i = 0;while (fscanf(fp, "%s\t%d\t%d\n", temp[i].name, &temp[i].age, &temp[i].num) != EOF) {fprintf(stdout, "%s\t%d\t%d\n", temp[i].name, temp[i].age, temp[i].num);//printf("%s\t%d\t%d\n", temp[i].name, temp[i].age, temp[i].num);i++;}fclose(fp);
    }
    int main()
    {struct MM mm[3] ={{"iron",18,1001},{"Close",16,1002},{"8K龙",19,1003}};writeFile(mm, 3, "fprintf.txt");//readFile("fprintf.txt");FILE* fp = fopen("fprintf.txt", "r");assert(fp);struct MM temp[3];int i = 0;while (fscanf(fp, "%s\t%d\t%d\n", temp[i].name, &temp[i].age, &temp[i].num) != EOF){fprintf(stdout, "%s\t%d\t%d\n", temp[i].name, temp[i].age, temp[i].num);//printf("%s\t%d\t%d\n", temp[i].name, temp[i].age, temp[i].num);i++;}fclose(fp);//int a=1;//fscanf(stdin,"%d",&a);   //等效scanf函数return 0;
    }
    
  • 二进制的方式读写

    size_t  fread( void*  buff,size_t size,size_t count,FILE*  _Stream);
    //读的内容存到那里: buff
    //每一次读多少字节: size
    //总共读几次: count
    //读的哪个文件:_Stream
    //注意点: 堆区内存,只能读到char* 内存中
    size_t  fwrite( void*  buff,size_t size,size_t count,FILE*  _Stream);
    //写的内容: buff
    //每一次写多少字节: size
    //总共写多少次: count
    //写的哪个文件:_Stream
    

一般用来读结构数据,注意点: 如果写到文件中是乱码大家不要介意,能成功读出来即可

文件指针

void rewind(FILE* file);     //把文件指针移动当前文件开头
int  fseek(FILE* _Stream,long  _Offset, int   _Origin);  //文件定位
//文件指针:_Stream
//相对位置偏移量:_Offset  整数:右边  负数: 左边
//参照点:_Origin
//SEEK_CUR   当位置
//SEEK_END   结束位置
//SEEK_SET   开始位置
int ftell(FILE* file);          //文件指针移动
int feof(FILE* file);           //文件是否到达文件末位

文件重定向

FILE*  freopen(char const* _FileName,char const* _Mode,FILE*  _Stream);

文件分割和合并

C语言预处理与宏定义

预处理和宏定义不是语法之类的东西,不算做语句,是没有;

宏替换

  • 单纯替换
  • 宏函数
#define MAX 100    //用一个名字替换一个数字
#define 整形 int   //不用的东西

条件编译

//1.条件编译 是在编译期完成
#if 表达式
//....     //成立执行这部分
#endif#if 1
//...
#else
//....
#endif#if 1
//...
#elif 2
//..
#elif 3
.....
#else
//....
#endif
//多字节处理
//条件编译做兼容性处理
#ifdef UNICODE
#undef UNICODE
#endif#include <stdio.h>
#include <stdlib.h>
#include <graphics.h>#define NUM 10
#define MAX 100         //不这样写代码,宏统一写在头文件下面
void test()
{//可以提前结束宏的作用域//可以坚持这个宏是否存在
#ifdef MAXprintf("%d\n", MAX);
#endif
#ifndef LL          //如果没有定义这个宏
#define LL 100      //就自己定义
#endifprintf("%d\n", LL);//取消宏的作用
#undef LL                       //LL被取消//错误代码//printf("%d\n", LL);        //LL被销毁了
}
int main()
{#if 1printf("1....\n");
#endifint a = -3;
#if a+4>3               //1>3printf("a<1\n");
#endif//一般当做条件编译的条件是宏#if NUM>1printf("num\n");
#endifprintf("%d\n", MAX);outtextxy(10, 10, "dsad");return 0;
}

其他预处理命令

//No.1 #   直接转字符串
//No.2 ## 连接符 产生一个新名字
//No.3 #pragma//comment 加载静态库//warning: 忽略提醒//pack:对齐方式//once
//No.4 预留宏

C语言文件写法

  • 设计多文件的时候,通常情况是一个.h 对应.c

    • 一个.h和.c 实现一个大模块
    • .h: 写的声明
    • .c: 写的实现
  • 对于自己写的头文件包含,通常在.c文件中完成

  • 多文件容易出现重定义和未定义问题

    • #pragma once

C语言命令行参数

命令行参数就是主函数参数

#include <stdio.h>
int main(int argv, char* argc[])
{if (argv != 3) {printf("命令语法不正确。\n");}else {printf("argc[0]:%s\n", argc[0]);printf("argc[1]:%s\n", argc[1]);printf("argc[2]:%s\n", argc[2]);//copy        1.txt    2.txt//argc[0]   argc[1]  argc[2]printf("覆盖 %s 吗? (Yes/No/All):", argc[2]);int userKey = getchar();if (userKey == 'y' || userKey == 'Y') {FILE* f1 = fopen(argc[1], "r");if (f1 == NULL) {printf("系统找不到指定的文件。\n");return;}FILE* f2 = fopen(argc[2], "w");int data = fgetc(f1);while (data != EOF) {fputc(data, f2);data = fgetc(f1);}fclose(f1);fclose(f2);printf("已复制         1 个文件。\n");}else {printf("已复制         0 个文件。\n");}}return 0;
}

C语言其他头文件

所有头文件

http://www.cplusplus.com/

  • aasert.h: 做断言处理
  • ctype.h: 数据类型判断的一些头文件

  • errno.h:描述错误代码

  • fenv.h: 浮点数据的一些设置

  • float.h:浮点数的一些特征

  • inttypes.h: 宽整数的描述

  • iso646.h: 特殊英语单词替换运算符

    #define and &&
    #define and_eq &=
    #define bitand &
    #define bitor |
    #define compl ~
    #define not !
    #define not_eq !=
    #define or ||
    #define or_eq |=
    #define xor ^
    #define xor_eq ^=
    
  • limits.h: 描述数据范围使用

#define MB_LEN_MAX    5
#define SHRT_MIN    (-32768)
#define SHRT_MAX      32767
#define USHRT_MAX     0xffff
#define INT_MIN     (-2147483647 - 1)
#define INT_MAX       2147483647
#define UINT_MAX      0xffffffff
#define LONG_MIN    (-2147483647L - 1)
#define LONG_MAX      2147483647L
#define ULONG_MAX     0xffffffffUL
#define LLONG_MAX     9223372036854775807i64
#define LLONG_MIN   (-9223372036854775807i64 - 1)
#define ULLONG_MAX    0xffffffffffffffffui64#define _I8_MIN     (-127i8 - 1)
#define _I8_MAX       127i8
#define _UI8_MAX      0xffui8#define _I16_MIN    (-32767i16 - 1)
#define _I16_MAX      32767i16
#define _UI16_MAX     0xffffui16#define _I32_MIN    (-2147483647i32 - 1)
#define _I32_MAX      2147483647i32
#define _UI32_MAX     0xffffffffui32#define _I64_MIN    (-9223372036854775807i64 - 1)
#define _I64_MAX      9223372036854775807i64
#define _UI64_MAX     0xffffffffffffffffui64
  • locale.h:环境设置
  • math.h: 数学函数
double  sqrt(_In_ double _X)                 //开平方根
double  pow(_In_ double _X, _In_ double _Y)     //2的3次方
int     abs(_In_ int _X)                        //求绝对值
  • setjmp.h: 跨函数跳转的语句 ---->不建议用
  • signal.h: 信号量: 做进程的,目前不需要掌握
  • stdarg.h: 可变参函数: printf函数
  • stdbool.h: bool类型
#define bool  _Bool
#define false 0
#define true  1
  • stddef.h: 定义一个宏,一般表示类型
#ifdef _WIN64typedef unsigned __int64 size_t;typedef __int64          ptrdiff_t;typedef __int64          intptr_t;
#elsetypedef unsigned int     size_t;typedef int              ptrdiff_t;typedef int              intptr_t;
#endif
  • stdint.h: int类型的数据描述
  • stdio.h: 标准的输入输出头文件
int  sprintf_s( char* const B, size_t   const BCount,char const* const _Format,...)
int  sscanf_s(char const* const B,char const* const F, ...)
  • tgmath.h : 数学函数宏
  • time.h: 时间处理函数
  • uchar.h: 无符号char类型的一些描述
  • wchar.h:宽字节版本的字符串处理函数
  • wctype.h: 宽字节版本的数据类型

时间头文件

  • 使用的时候加个:#include <time.h>
  • struct tm类型:
struct tm
{int tm_sec;   // seconds after the minute - [0, 60] including leap secondint tm_min;   // minutes after the hour - [0, 59]int tm_hour;  // hours since midnight - [0, 23]int tm_mday;  // day of the month - [1, 31]int tm_mon;   // months since January - [0, 11]         //月份也是从0开始int tm_year;  // years since 1900                        //从1900开始int tm_wday;  // days since Sunday - [0, 6]                //星期几:从0开始int tm_yday;  // days since January 1 - [0, 365]int tm_isdst; // daylight savings time flag
};
  • time函数可以获取时间戳
static  time_t  time(time_t* const _Time)
  • ctime: 把时间戳转换为字符串直接打印出来
 static  char*  ctime(const* const _Time)

可变参函数

  • 可变参函数实现前加头文件: stdarg.h
  • 基础知识
//宏函数
va_list:  保存所有参数
va_start: 参数的第一个位置
va_end:  参数的最后一个位置
va_arg:  参数
... :   参数包:无参或者多个参数,参数的个数不定
int __CRTDECL printf(char const* const _Format,...);
  • 简单案例: 求不定参数的数字的和
#include <stdio.h>
#include <stdarg.h>
//count: 有几个参数
//data: 第一个参数
//... 参数包
int Sum(int count, int data, ...)
{int i, sum = data;va_list ap;    //保存所有参数va_start(ap, data);  //第一个参数的位置是datafor (int i = 1; i < count; i++) {//va_argsum += va_arg(ap, int);}va_end(ap);return sum;
}
void testSum()
{printf("%d\n", Sum(3, 1, 2, 3));printf("%d\n", Sum(5, 1, 2, 3,4,5));printf("%d\n", Sum(6, 1, 2, 3,4,5,6));
}
//简单实现printf函数
void myprint(const char* str, ...)
{va_list ap;int iNum;double dNum;va_start(ap, str);while (*str){if (*str == '%') {str++;switch (*str) {case 'd':iNum = va_arg(ap, int);printf("%d", iNum);break;case 'f':dNum = va_arg(ap, double);printf("%f", dNum);break;}}else {printf("%c", *str);}str++;}
}
int main()
{printf("dsds\n");testSum();myprint("整数:%d小数:%f\n", 12, 1.222);return 0;
}

_I64_MIN (-9223372036854775807i64 - 1)
#define _I64_MAX 9223372036854775807i64
#define _UI64_MAX 0xffffffffffffffffui64


+ locale.h:环境设置
+ math.h: 数学函数```c
double  sqrt(_In_ double _X)                    //开平方根
double  pow(_In_ double _X, _In_ double _Y)     //2的3次方
int     abs(_In_ int _X)                        //求绝对值
  • setjmp.h: 跨函数跳转的语句 ---->不建议用
  • signal.h: 信号量: 做进程的,目前不需要掌握
  • stdarg.h: 可变参函数: printf函数
  • stdbool.h: bool类型
#define bool  _Bool
#define false 0
#define true  1
  • stddef.h: 定义一个宏,一般表示类型
#ifdef _WIN64typedef unsigned __int64 size_t;typedef __int64          ptrdiff_t;typedef __int64          intptr_t;
#elsetypedef unsigned int     size_t;typedef int              ptrdiff_t;typedef int              intptr_t;
#endif
  • stdint.h: int类型的数据描述
  • stdio.h: 标准的输入输出头文件
int  sprintf_s( char* const B, size_t   const BCount,char const* const _Format,...)
int  sscanf_s(char const* const B,char const* const F, ...)
  • string.h: 字符处理函数

  • tgmath.h : 数学函数宏
  • time.h: 时间处理函数
  • uchar.h: 无符号char类型的一些描述
  • wchar.h:宽字节版本的字符串处理函数
  • wctype.h: 宽字节版本的数据类型

时间头文件

  • 使用的时候加个:#include <time.h>
  • struct tm类型:
struct tm
{int tm_sec;   // seconds after the minute - [0, 60] including leap secondint tm_min;   // minutes after the hour - [0, 59]int tm_hour;  // hours since midnight - [0, 23]int tm_mday;  // day of the month - [1, 31]int tm_mon;   // months since January - [0, 11]         //月份也是从0开始int tm_year;  // years since 1900                        //从1900开始int tm_wday;  // days since Sunday - [0, 6]                //星期几:从0开始int tm_yday;  // days since January 1 - [0, 365]int tm_isdst; // daylight savings time flag
};
  • time函数可以获取时间戳
static  time_t  time(time_t* const _Time)
  • ctime: 把时间戳转换为字符串直接打印出来
 static  char*  ctime(const* const _Time)

可变参函数

  • 可变参函数实现前加头文件: stdarg.h
  • 基础知识
//宏函数
va_list:  保存所有参数
va_start: 参数的第一个位置
va_end:  参数的最后一个位置
va_arg:  参数
... :   参数包:无参或者多个参数,参数的个数不定
int __CRTDECL printf(char const* const _Format,...);
  • 简单案例: 求不定参数的数字的和
#include <stdio.h>
#include <stdarg.h>
//count: 有几个参数
//data: 第一个参数
//... 参数包
int Sum(int count, int data, ...)
{int i, sum = data;va_list ap;    //保存所有参数va_start(ap, data);  //第一个参数的位置是datafor (int i = 1; i < count; i++) {//va_argsum += va_arg(ap, int);}va_end(ap);return sum;
}
void testSum()
{printf("%d\n", Sum(3, 1, 2, 3));printf("%d\n", Sum(5, 1, 2, 3,4,5));printf("%d\n", Sum(6, 1, 2, 3,4,5,6));
}
//简单实现printf函数
void myprint(const char* str, ...)
{va_list ap;int iNum;double dNum;va_start(ap, str);while (*str){if (*str == '%') {str++;switch (*str) {case 'd':iNum = va_arg(ap, int);printf("%d", iNum);break;case 'f':dNum = va_arg(ap, double);printf("%f", dNum);break;}}else {printf("%c", *str);}str++;}
}
int main()
{printf("dsds\n");testSum();myprint("整数:%d小数:%f\n", 12, 1.222);return 0;
}

C语言复习笔记——适合一些需要复习和进阶c语言的朋友,这只能说是个概要。当然,如果朋友你看到某个内容能联想到知识点,那你应该是掌握了的,你可以尝试找找其他的文章去深入理解下,如有错误和疑问可以直接指出相关推荐

  1. 数据结构(c语言版)笔记6,2020考研计算机《数据结构(C语言版)》复习笔记(6)

    2020年计算机考研复习已经开始,新东方在线在此整理了2020考研计算机<数据结构(C语言版)>复习笔记(6),希望能帮助大家! 第六章 树知识点整理 树是n个结点的有限集合,非空时必须满 ...

  2. 最最最详细的C语言教程笔记零起步(10)进阶必备 同笔者一起学习

    C语言教程笔记 二十二. 输入输出缓存 1. 探究printf的现象 1.1 windows系统上的代码 1.2 linux系统上的代码 1.3 windows系统上的表现 1.4 linux系统上的 ...

  3. 前端复习笔记--1.html标签复习速查

    概览 文档章节 <body> <header> <nav> 导航 <aside> 表示和主要内容不相关的区域 <article> 表示一个独 ...

  4. 【山东大学】web数据管理——复习笔记

    写在前面 若有图片加载失败,请科学上网 . 本文为对软件学院连老师的PPT课件总结所得的复习笔记,仅供参考.不保证对考点的全覆盖,以PPT为主. 对往年考过的题相关知识点前面都标注了"考过& ...

  5. 【前端】HTML标签基础复习笔记

    不够完美又何妨?万物皆有裂隙,那是光进来的地方. 文章目录 HTML复习笔记 JavaWeb相关概述 HTML概述 HTML语法 基本标签 图片标签 链接 列表标签 块级标签 表格标签 表单标签 HT ...

  6. 《微型计算机原理与接口技术》复习笔记(四)

    使用教材为 <微型计算机原理与接口技术>(慕课版) 孙丽娟.李爱群.陈燕俐.周宁宁.邓玉龙编著 微机原理复习笔记一 微机原理复习笔记二 微机原理复习笔记三 微机原理复习笔记四 中断系统 1 ...

  7. 《微型计算机原理与接口技术》复习笔记(二)

    使用教材为 <微型计算机原理与接口技术>(慕课版) 孙丽娟.李爱群.陈燕俐.周宁宁.邓玉龙编著 默认存储器单元的地址编排顺序从上往下,地址从低向高编排 微机原理复习笔记一 微机原理复习笔记 ...

  8. 《微型计算机原理与接口技术》复习笔记(三)

    使用教材为 <微型计算机原理与接口技术>(慕课版) 孙丽娟.李爱群.陈燕俐.周宁宁.邓玉龙编著 微机原理复习笔记一 微机原理复习笔记二 微机原理复习笔记四 微机原理复习笔记三 1. 存储器 ...

  9. 《微型计算机原理与接口技术》复习笔记(一)

    使用教材为 <微型计算机原理与接口技术>(慕课版) 孙丽娟.李爱群.陈燕俐.周宁宁.邓玉龙编著 微机原理复习笔记二 微机原理复习笔记三 微机原理复习笔记四 微机复习笔记(一) 1. 二进制 ...

最新文章

  1. page分页php,Page分页函数
  2. 走进元学习:概述不同类型的元学习方法
  3. Mysql基础--常见的表的约束介绍(一)
  4. python练习题:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度
  5. SAP Leonardo机器学习图片相似度打分API的测试报告
  6. 数据科学与大数据技术的案例_主数据科学案例研究,招聘经理的观点
  7. 考研山东省的计算机学校有哪些,山东考研哪个学校好考
  8. request.getParameter() 和request.getAttribute() 区别
  9. CCPC-Wannafly Comet OJ 夏季欢乐赛(2019)E
  10. SpringCloud工作笔记045---SpringCloud分布式服务部署常用端口
  11. shell每日一句(3)
  12. DenseNet解析
  13. 【游戏开发实战】下载原神模型,PMX转FBX,导入到Unity中,卡通渲染,绑定人形动画(附Demo工程)
  14. java sts安装步骤_下载并安装Java开发工具STS
  15. cad补全三视图_cad补画三视图练习题.doc
  16. 如何成为一流的「匠人」?
  17. anaconda新手使用心得
  18. mastercam2017后处理升级_如何升级Mastercam 9.1版后处理?
  19. 计算机仿真在机械行业中的应用,计算机仿真技术在机械行业中的应用
  20. 计算机应用基础问卷答题,中职计算机应用基础课程教学改革与实践

热门文章

  1. 谷歌、苹果、亚马逊等大厂技术面试真题
  2. MS Visio软件的XML文档结构
  3. nginx使用pagespeed
  4. linux nat和网桥多网卡,linux 双网卡 NAT共享上网
  5. 图文详解STM32F0xx基于标准库新建工程
  6. 数据库 水电费缴费系统
  7. 【剪映】基础剪辑 | 实用技巧
  8. 游戏服务器系统的选择界面,cocos2dx网游选服界面制作三:服务器单个item界面包装...
  9. 字节跳动后端实习面经,一面+二面+三面(已收到offer)
  10. 我的Java学习之路(七)-- 模拟考试系统