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=%c 1, '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; //定义变量赋值为0
int 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 a:左值 1:右值
-
优先级:算法: 算表达式的顺序(先算*/ 再算+ -)
-
结合性: 读法: 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 有四部分: 4
printf("result=%d\ta=%d\n", resultFirst, a); //12 5
int b = 1;
int resultSecond = b++ * b++ * ++b * b++ * b++;
//自己会算就可以
//b运算时候: b=2
//resultSecond:2^5;
printf("result=%d\tb=%d\n", resultSecond, b); //32 6
int 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=0
break;
}
}
//退出里面循环有两种情况:
//第一种,里面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; //当前元素是0
int 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: -1
array[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 ,数组下标会是0
for (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' }; //自动推断长度3
for (int i = 0; i < 3; i++)
{
putchar(array[i]);
}
putchar('\n');
//No.2 字符数组操作字符串
//字符串有一个不可见字符\0
char str[] = "ILoveyou"; //自动长度是:9
char inputstr[] = { 'A','B','C','D','\0','A','B','C' };
//%s对应的是地址
printf("%s\n", &inputstr[0]); //ABCD &inputstr[0]到\0
printf("%s\n", &inputstr[2]); //CD &inputstr[2]到\0
printf("%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表数据操作)
- 行求和
- 列求和
- 二维数组充当地图
- 矩阵变化
- 行列数据的操作(excel表数据操作)
- 二维数组操作字符串
- 初始化
- 遍历
- 多个字符串排序
C语言函数
认识函数
//No.1 学会创建函数
函数返回值类型 函数名(函数参数)
{
//函数体
return 函数返回值;
}
//没有返回值写: void :没有
//没有参数可以不写
void 函数名() //void 函数名(void)
{
//没有返回值不代表没有return
return ; //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=3
return 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} }; //其他的值默认初始化为0
printArray2D(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; //这个代码运行的时候只执行一次
//不做初始化默认为0
num++;
printf("%d\n", num);
}
char* returnPoint()
{
//返回局部变量地址,不允许的
//函数调用完,内存会被系统回收(清除所有数据)
char array[10] = "ILoveyou";
//%s 打印方式,从首地址开始,打印到\0
char* 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 = # //解析方式: pVoid=# 不是*pVoid=#
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 00
printf("%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); //0000005B9FDBF5E4
printf("%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]); //未知量 -858993460
return 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=3
printf("%p\n", p + 1); //4
printf("%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 #
}
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); //999
int* 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:7
unsigned int b : 1; //0 1
unsigned 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补4
double b; //8
int 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 data
int 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 }; //节点1
struct Node node2 = { 2,NULL }; //节点2
struct Node node3 = { 3,NULL }; //节点3
node1.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->data
while (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);
//321123
insertByAppoin(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 MAX
printf("%d\n", MAX);
#endif
#ifndef LL //如果没有定义这个宏
#define LL 100 //就自己定义
#endif
printf("%d\n", LL);
//取消宏的作用
#undef LL //LL被取消
//错误代码
//printf("%d\n", LL); //LL被销毁了
}
int main()
{
#if 1
printf("1....\n");
#endif
int a = -3;
#if a+4>3 //1>3
printf("a<1\n");
#endif
//一般当做条件编译的条件是宏
#if NUM>1
printf("num\n");
#endif
printf("%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文件中完成
-
多文件容易出现重定义和未定义问题
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 _WIN64
typedef unsigned __int64 size_t;
typedef __int64 ptrdiff_t;
typedef __int64 intptr_t;
#else
typedef 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 second
int 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); //第一个参数的位置是data
for (int i = 1; i < count; i++)
{
//va_arg
sum += 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 _WIN64
typedef unsigned __int64 size_t;
typedef __int64 ptrdiff_t;
typedef __int64 intptr_t;
#else
typedef 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 second
int 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); //第一个参数的位置是data
for (int i = 1; i < count; i++)
{
//va_arg
sum += 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;
}