C语言课程笔记-顿开莫帅
C语言基本数据类型
C语言常量
- 什么是常量: 固定的值
常量的分类:
- 整形常量
//整数 //1,2,3,4 十进制数 //特殊格式的整数 //0123 八进制:0开头 //08---> 不算 //0x123 十六进制整数: 0x开头 //0b10111 2019以下的编译器不支持 //特殊后缀的整数 //1u 无符号整数: 没有负数的意思 //1L 长整形
- 浮点常量
//实数(包含整数包含小数) //1.333 默认是双精度(double)浮点型常量 //1.33f 单精度浮点常量 //特殊的表示方式 //科学计数法 //1234E-5 // .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--> -2^31-2^31-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; 正确应该是把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表数据操作)
- 行求和
- 列求和
- 二维数组充当地图
- 矩阵变化
二维数组操作字符串
- 初始化
- 遍历
- 多个字符串排序
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;
}