14:枚举enum、联合union、动态内存分配malloc
2017-01-14 20:20
211 查看
【枚举】
枚举也可以用来创建新的数据类型
枚举类型存储区就是"整数类型存储区",枚举类型存储区在使用的时候存放"有限"的几个整数
声明枚举类型的时候需要提供一组名称,计算机为每个名称分配一个对应整数,只有这些整数可以记录在这种枚举类型的存储区里
不同枚举类型存储区里可以记录的整数范围不同
声明枚举类型的时候应该使用 enum 关键字,也"不会分配内存"
enum {CHUN, XIA, QIU, DONG};
计算机把从 0 开始的连续的非负整数分配给枚举类型中的所有名称,一一对应
可以在声明枚举类型时指定某个名称对应的整数,后面的名称对应的整数都会随之变化
<示例>
#include <stdio.h>
enum /*season*/ {CHUN, XIA, QIU = 5, DONG}; //名称可省略,使用时不需要
int main(){
printf("CHUN是%d\n", CHUN);//0
printf("XIA是%d\n", XIA);//1
printf("QIU是%d\n", QIU);//5
printf("DONG是%d\n", DONG);//6
return 0;
}
【联合】
联合也可以用来创建新的数据类型
声明联合的时候需要使用 union 关键字(使用 typedef 关键字和结构体一样)
联合的成员变量对应的存储区互相重叠
联合存储区可以当作多种不同类型的存储区使用,每个成员变量代表一种对应的类型
联合的所有成员变量存储区的开始地址一样,联合中所有成员分配的是同一块内存
联合存储区的大小是"最大存储区的大小"
<示例>
#include <stdio.h>
typedef union {
int num; //最大存储区,占4个字节
float fnum;
char buf[2];
} Tmp;
int main(){
Tmp p = {0};
printf("&(p.num)是%p\n", &(p.num)); //0xbfc5ac3c
printf("&(p.fnum)是%p\n", &(p.fnum)); //0xbfc5ac3c
printf("sizeof(Tmp)是%d\n", sizeof(Tmp)); //4
return 0;
}
【高级指针】
记录普通变量地址的指针叫一级指针
"记录一级指针的地址"的指针叫做二级指针
声明二级指针的时候需要写"**" //两个星号
二级指针前使用 ** "两个星号"可以表示捆绑的普通变量的存储区
二级指针前使用 * "一个星号"可以表示捆绑的一级指针的存储区
<示例>
#include <stdio.h>
int main(){
int num = 0;
int *p_num = #
int **pp_num = &p_num; //二级指针声明
**pp_num = 10; //两个星号的二级指针赋值,就是修改num的值
printf("num = %d\n", num);
return 0;
}
二级指针"可以用来代表指针数组",但是不可以代表二维数组
<示例>
#include <stdio.h>
int main(int argc, char **argv){ //打印命令行参数二级指针用法
int num = 0;
for(num = 0; num < argc; num++){
printf("%s\n", *(argv + num));
}
return 0;
}
无类型指针有可能实际代表的是二级指针
"二级指针通常作为形式参数使用",它可以从调用函数向被调用函数传递一个一级指针存储区
【练习】
编写函数从两个圆里找出面积比较大的圆,并把它传递给调用函数
函数不能使用返回值
<思想>
结构体,一级结构体指针记录面积较大的圆,二级指针存储比较大的圆的结构体变量地址
/*代码*/
#include <stdio.h>
typedef struct {
int row, col;
} Pt;
typedef struct {
Pt center;
int r;
} Circle;
void larger(const Circle *p_cl1,const Circle *p_cl2,Circle **pp_cl) { //两个const常量结构体指针地址,一个结构体二级指针
*pp_cl = (Circle *)(p_cl1->r > p_cl2->r ? p_cl1 : p_cl2); //因为有const常量,所以做结构体强制类型转换,注意后面三目表达式整体括号
}
int main() {
Circle cl1 = {0}, cl2 = {0}, cl = {0}, *p_cl = &cl;
printf("请输入第1个圆位置(圆心行,列,半径):");
scanf("%d%d%d", &cl1.center.row, &cl1.center.col, &cl1.r);
printf("请输入第2个圆位置(圆心行,列,半径):");
scanf("%d%d%d", &cl2.center.row, &cl2.center.col, &cl2.r);
larger(&cl1, &cl2, &p_cl);
printf("面积大的圆是((%d, %d), %d)\n", p_cl->center.row, p_cl->center.col, p_cl->r); //如果->写成.访问成员会导致"在非结构或联合中请求成员"的错误
return 0;
}
【函数指针】
C语言里函数也有地址
"函数名称可以表示函数的地址"
函数指针可以"用来记录函数的地址"
函数指针也需要先声明然后才能使用,函数指针声明语句可以根据函数声明语句变化得到
函数指针也有"格式区分",不同格式的函数指针适合与不同的函数捆绑
函数指针"可以用来调用函数"(跨文件调用)
<示例>
#include <stdio.h>
int add(int num, int num1) {
return num + num1;
}
int main() {
int (*p_func)(int, int); //函数指针声明,原函数声明写过来修改
p_func = add; //函数的地址赋值给函数指针,实现捆绑
printf("add是%p\n", add);
int num = p_func(4, 7); //函数指针调用函数格式与函数调用一样
printf("结果是:%d\n", num);
return 0;
}
【回调函数】
函数指针的应用:回调函数
可以"作为实际参数使用的函数"叫回调函数
<示例>
#include <stdio.h>
/*
void print(int *p_num, int size) {
int i = 0;
for(i = 0; i < size; i++){
printf("%d ", *(p_num + i)); //循环变量做下标
}
printf("\n");
}
*/
//回调函数:以下两个函数可以在一起合作,也可以不在一起,由调用函数决定
void print_cb(int *p_num) {
printf("%d ", *p_num);
}
void for_each(int *p_num, int size, void (*p_func)(int *)) {
int num = 0;
for(num = 0; num < size; num++) {
p_func(p_num + num);
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
//print(arr, 5);
for_each(arr, 5, print_cb);//函数名是地址,作为参数传递过去
printf("\n");
return 0;
}
【练习】
利用for_each函数把数组里每个存储区内容编程相反数
(实际工作中for_each这个通用的回调函数是已经写好的,需要写的绝大部分都是专用函数,如下例子中的print_cb,neg_cb)
<思想>
回调函数
/*代码*/
#include <stdio.h>
void print_cb(int *p_num) {
printf("%d ", *p_num);
}
void neg_cb(int *p_num) {
*p_num = 0 - *p_num;
}
void for_each(int *p_num, int size, void (*p_func)(int *)) {
int num = 0;
for(num = 0; num < size; num++) {
p_func(p_num + num);
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
for_each(arr, 5, neg_cb); //调用回调函数neg_cb
for_each(arr, 5, print_cb); //调用回调函数print_cb
printf("\n");
return 0;
}
【动态内存分配】
可以在程序运行的时候临时决定需要分配多少存储区,这种分配方法叫"动态分配内存"
为了使用动态分配内存,需要用到一组"标准函数"
为了使用这些标准函数需要包含"<stdlib.h>"头文件(切记,否则编译会包警告隐式声明问题)
"malloc()" 函数可以动态分配一组连续的字节
这个函数可以动态分配一组连续的字节,这个函数需要一个整数类型参数表示希望分配的字节个数
它的【返回值】就是分配好的"第一个字节的地址"(以此可以找到每个字节)
"动态分配内存会有可能失败的",如果分配失败就返回空地址 NULL(nil)
根据此返回结果判断动态分配内存是否成功
这个函数的"返回值记录在无类型指针的存储区"里,需要首先强制类型转换为有类型指针,然后才能使用
<示例>
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p_num = (int *)malloc(5 * sizeof(int)); //动态分配5个整数类型存,必须强制类型转换为对应的数据类型
储区
if(p_num) {
//if区域内的内容使用动态分配内存
printf("分配成功!\n"); //仅作演示
}
return 0;
}
动态分配内存"不使用之后必须释放"
"free()" 标准函数可以用来释放动态分配内存
free() 函数需要一个参数,这个参数代表动态分配的第一个字节的地址
free() 函数会把一次分配的所有内存都释放掉
如果使用指针作为参数调用free()函数,则"函数调用后指针成为野指针",必须将指针"恢复成空指针"
<示例>/*此为动态分配内存的代码框架(固定写法)*/
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p_num = (int *)malloc(5 * sizeof(int)); //5可用整形变量代替
//5表示分配该类型存储区个数,int是4个字节,此次分配共20个字节空间
if(p_num) {
printf("分配成功!\n"); //仅作演示,代表内容
free(p_num); //释放使用过的动态内存
p_num = NULL; //野指针恢复成空指针
}
return 0;
}
/*动态分配内存代码框架(另外一种固定写法)*/
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p_num = (int *)malloc(5 * sizeof(int));
if(!p_num) { //如果记录的地址为空,证明没有动态内存分配失败
return 0; //结束整个当前程序
}
/* 使用动态分配内存的代码区域 */
free(p_num);
p_num = NULL;
return 0;
}
<实际示例>
#include <stdio.h>
#include <stdlib.h>
int main() {
int num = 0;
int *p_num = (int *)malloc(5 * sizeof(int));//动态分配内存及大小
if(p_num) {
printf("分配成功!\n"); //分配成功!
for(num = 0; num < 5; num++) {
*(p_num + num) = num;
}
for(num = 0; num < 5; num++) {
printf("%d ", *(p_num + num)); //0 1 2 3 4
}
printf("\n");
free(p_num); //释放动态分配的内存
p_num = NULL; //野指针置空
}
return 0;
}
动态分配内存一个很重要的使用场景:
"调用函数可以使用被调用函数动态分配的内存"
(之前是把被掉用函数的存储区静态化 static)
<实际示例>/*生成7组(1-36)之间的数的彩票(动态分配内存+回调函数)*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int *create(int size) { //彩票不重复
int *p_num = (int *)malloc(size * sizeof(int));
int num = 0, i = 0;
if(p_num) { //必须加分支判断动态内存是否分支成功
for(num = 0; num < size; num++) {
*(p_num + num) = rand() % 36 + 1;
for(i = 0;i < num; i++) {
if(*(p_num + num) == *(p_num + i)) {
i--;
}
}
}
}
return p_num;
}
void print(int *p_num) { //回调函数
printf("%d ", *p_num);
}
void for_each(int *p_num, int size, void (*p_func)(int *)) {
int num = 0;
for(num = 0; num < size; num++) {
p_func(p_num + num);
}
}
int main() {
srand(time(0));
int *p_num = create(SIZE); //编译时-D指定SIZE宏的值
if(p_num) {
for_each(p_num, SIZE, print);
printf("\n");
free(p_num); //分支内释放内存
p_num = NULL;
}
return 0;
}
【练习】
编写函数从键盘得到一个点的位置,把这个点的位置传递给调用函数
要求用动态分配内存记录点的位置
<思想>
动态内存分配代码框架
/*代码*/
#include <stdio.h>
#include <stdlib.h> //警告:隐式声明问题,切记不要忘记头文件
typedef struct {
int row, col;
} Pt;
Pt *dian(void) {
Pt *p_pt = (Pt *)malloc(sizeof(Pt));
if(p_pt) { //分支判断是框架一部分
printf("请输入点的坐标行,列:");
scanf("%d%d", &(p_pt->row), &(p_pt->col));
}
return p_pt;
}
int main() {
Pt *p_d = dian();
if(p_d) {
printf("坐标为:(%d, %d)\n", p_d->row, p_d->col);
free(p_d);
p_d = NULL;
}
return 0;
}
【练习】
编写函数计算两个点的中间位置,并把这个位置传递给调用函数
用动态分配内存记录中间点的位置
<思想>
结构体/函数/动态分配内存框架
/*代码*/
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int row, col;
} Pt;
Pt *mid(const Pt *p_d1, const Pt *p_d2) { //不会修改结构体指针的地址,所有要都加 const
Pt *p_mid = (Pt *)malloc(sizeof(Pt));
if(p_mid) {
p_mid->row = (p_d1->row + p_d2->row)/2;
p_mid->col = (p_d1->col + p_d2->col)/2;
}
return p_mid;
}
int main() {
Pt d1, d2;
printf("请输入第1个点的坐标行,列:");
scanf("%d%d", &(d1.row), &(d1.col));
printf("请输入第2个点的坐标行,列:");
scanf("%d%d", &(d2.row), &(d2.col));
Pt *p_mid = mid(&d1, &d2);
if(p_mid) {
printf("中间点坐标为:(%d,%d)\n", p_mid->row, p_mid->col);
free(p_mid);
p_mid = NULL;
}
return 0;
}
【动态分配内存】
"calloc" 函数也可以动态分配内存
这个函数可以把分配的所有内存内容都设置成 0 (malloc内容是随机的未初始化)
这个函数也需要包含"<stdlib.h>"的头文件
这个函数需要两个参数:第 1 个参数表示希望分配的"存储区个数",第 2 个参数表示"单个存储区的大小"
这个函数的【返回值】是分配好的第一个存储区的地址(同malloc函数)
这个函数也可能分配失败,如果失败则返回值是NULL(空地址nil)
"realloc" 函数可以调整一段动态分配内存的大小(尽量少使用这个函数)
枚举也可以用来创建新的数据类型
枚举类型存储区就是"整数类型存储区",枚举类型存储区在使用的时候存放"有限"的几个整数
声明枚举类型的时候需要提供一组名称,计算机为每个名称分配一个对应整数,只有这些整数可以记录在这种枚举类型的存储区里
不同枚举类型存储区里可以记录的整数范围不同
声明枚举类型的时候应该使用 enum 关键字,也"不会分配内存"
enum {CHUN, XIA, QIU, DONG};
计算机把从 0 开始的连续的非负整数分配给枚举类型中的所有名称,一一对应
可以在声明枚举类型时指定某个名称对应的整数,后面的名称对应的整数都会随之变化
<示例>
#include <stdio.h>
enum /*season*/ {CHUN, XIA, QIU = 5, DONG}; //名称可省略,使用时不需要
int main(){
printf("CHUN是%d\n", CHUN);//0
printf("XIA是%d\n", XIA);//1
printf("QIU是%d\n", QIU);//5
printf("DONG是%d\n", DONG);//6
return 0;
}
【联合】
联合也可以用来创建新的数据类型
声明联合的时候需要使用 union 关键字(使用 typedef 关键字和结构体一样)
联合的成员变量对应的存储区互相重叠
联合存储区可以当作多种不同类型的存储区使用,每个成员变量代表一种对应的类型
联合的所有成员变量存储区的开始地址一样,联合中所有成员分配的是同一块内存
联合存储区的大小是"最大存储区的大小"
<示例>
#include <stdio.h>
typedef union {
int num; //最大存储区,占4个字节
float fnum;
char buf[2];
} Tmp;
int main(){
Tmp p = {0};
printf("&(p.num)是%p\n", &(p.num)); //0xbfc5ac3c
printf("&(p.fnum)是%p\n", &(p.fnum)); //0xbfc5ac3c
printf("sizeof(Tmp)是%d\n", sizeof(Tmp)); //4
return 0;
}
【高级指针】
记录普通变量地址的指针叫一级指针
"记录一级指针的地址"的指针叫做二级指针
声明二级指针的时候需要写"**" //两个星号
二级指针前使用 ** "两个星号"可以表示捆绑的普通变量的存储区
二级指针前使用 * "一个星号"可以表示捆绑的一级指针的存储区
<示例>
#include <stdio.h>
int main(){
int num = 0;
int *p_num = #
int **pp_num = &p_num; //二级指针声明
**pp_num = 10; //两个星号的二级指针赋值,就是修改num的值
printf("num = %d\n", num);
return 0;
}
二级指针"可以用来代表指针数组",但是不可以代表二维数组
<示例>
#include <stdio.h>
int main(int argc, char **argv){ //打印命令行参数二级指针用法
int num = 0;
for(num = 0; num < argc; num++){
printf("%s\n", *(argv + num));
}
return 0;
}
无类型指针有可能实际代表的是二级指针
"二级指针通常作为形式参数使用",它可以从调用函数向被调用函数传递一个一级指针存储区
【练习】
编写函数从两个圆里找出面积比较大的圆,并把它传递给调用函数
函数不能使用返回值
<思想>
结构体,一级结构体指针记录面积较大的圆,二级指针存储比较大的圆的结构体变量地址
/*代码*/
#include <stdio.h>
typedef struct {
int row, col;
} Pt;
typedef struct {
Pt center;
int r;
} Circle;
void larger(const Circle *p_cl1,const Circle *p_cl2,Circle **pp_cl) { //两个const常量结构体指针地址,一个结构体二级指针
*pp_cl = (Circle *)(p_cl1->r > p_cl2->r ? p_cl1 : p_cl2); //因为有const常量,所以做结构体强制类型转换,注意后面三目表达式整体括号
}
int main() {
Circle cl1 = {0}, cl2 = {0}, cl = {0}, *p_cl = &cl;
printf("请输入第1个圆位置(圆心行,列,半径):");
scanf("%d%d%d", &cl1.center.row, &cl1.center.col, &cl1.r);
printf("请输入第2个圆位置(圆心行,列,半径):");
scanf("%d%d%d", &cl2.center.row, &cl2.center.col, &cl2.r);
larger(&cl1, &cl2, &p_cl);
printf("面积大的圆是((%d, %d), %d)\n", p_cl->center.row, p_cl->center.col, p_cl->r); //如果->写成.访问成员会导致"在非结构或联合中请求成员"的错误
return 0;
}
【函数指针】
C语言里函数也有地址
"函数名称可以表示函数的地址"
函数指针可以"用来记录函数的地址"
函数指针也需要先声明然后才能使用,函数指针声明语句可以根据函数声明语句变化得到
函数指针也有"格式区分",不同格式的函数指针适合与不同的函数捆绑
函数指针"可以用来调用函数"(跨文件调用)
<示例>
#include <stdio.h>
int add(int num, int num1) {
return num + num1;
}
int main() {
int (*p_func)(int, int); //函数指针声明,原函数声明写过来修改
p_func = add; //函数的地址赋值给函数指针,实现捆绑
printf("add是%p\n", add);
int num = p_func(4, 7); //函数指针调用函数格式与函数调用一样
printf("结果是:%d\n", num);
return 0;
}
【回调函数】
函数指针的应用:回调函数
可以"作为实际参数使用的函数"叫回调函数
<示例>
#include <stdio.h>
/*
void print(int *p_num, int size) {
int i = 0;
for(i = 0; i < size; i++){
printf("%d ", *(p_num + i)); //循环变量做下标
}
printf("\n");
}
*/
//回调函数:以下两个函数可以在一起合作,也可以不在一起,由调用函数决定
void print_cb(int *p_num) {
printf("%d ", *p_num);
}
void for_each(int *p_num, int size, void (*p_func)(int *)) {
int num = 0;
for(num = 0; num < size; num++) {
p_func(p_num + num);
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
//print(arr, 5);
for_each(arr, 5, print_cb);//函数名是地址,作为参数传递过去
printf("\n");
return 0;
}
【练习】
利用for_each函数把数组里每个存储区内容编程相反数
(实际工作中for_each这个通用的回调函数是已经写好的,需要写的绝大部分都是专用函数,如下例子中的print_cb,neg_cb)
<思想>
回调函数
/*代码*/
#include <stdio.h>
void print_cb(int *p_num) {
printf("%d ", *p_num);
}
void neg_cb(int *p_num) {
*p_num = 0 - *p_num;
}
void for_each(int *p_num, int size, void (*p_func)(int *)) {
int num = 0;
for(num = 0; num < size; num++) {
p_func(p_num + num);
}
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
for_each(arr, 5, neg_cb); //调用回调函数neg_cb
for_each(arr, 5, print_cb); //调用回调函数print_cb
printf("\n");
return 0;
}
【动态内存分配】
可以在程序运行的时候临时决定需要分配多少存储区,这种分配方法叫"动态分配内存"
为了使用动态分配内存,需要用到一组"标准函数"
为了使用这些标准函数需要包含"<stdlib.h>"头文件(切记,否则编译会包警告隐式声明问题)
"malloc()" 函数可以动态分配一组连续的字节
这个函数可以动态分配一组连续的字节,这个函数需要一个整数类型参数表示希望分配的字节个数
它的【返回值】就是分配好的"第一个字节的地址"(以此可以找到每个字节)
"动态分配内存会有可能失败的",如果分配失败就返回空地址 NULL(nil)
根据此返回结果判断动态分配内存是否成功
这个函数的"返回值记录在无类型指针的存储区"里,需要首先强制类型转换为有类型指针,然后才能使用
<示例>
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p_num = (int *)malloc(5 * sizeof(int)); //动态分配5个整数类型存,必须强制类型转换为对应的数据类型
储区
if(p_num) {
//if区域内的内容使用动态分配内存
printf("分配成功!\n"); //仅作演示
}
return 0;
}
动态分配内存"不使用之后必须释放"
"free()" 标准函数可以用来释放动态分配内存
free() 函数需要一个参数,这个参数代表动态分配的第一个字节的地址
free() 函数会把一次分配的所有内存都释放掉
如果使用指针作为参数调用free()函数,则"函数调用后指针成为野指针",必须将指针"恢复成空指针"
<示例>/*此为动态分配内存的代码框架(固定写法)*/
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p_num = (int *)malloc(5 * sizeof(int)); //5可用整形变量代替
//5表示分配该类型存储区个数,int是4个字节,此次分配共20个字节空间
if(p_num) {
printf("分配成功!\n"); //仅作演示,代表内容
free(p_num); //释放使用过的动态内存
p_num = NULL; //野指针恢复成空指针
}
return 0;
}
/*动态分配内存代码框架(另外一种固定写法)*/
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p_num = (int *)malloc(5 * sizeof(int));
if(!p_num) { //如果记录的地址为空,证明没有动态内存分配失败
return 0; //结束整个当前程序
}
/* 使用动态分配内存的代码区域 */
free(p_num);
p_num = NULL;
return 0;
}
<实际示例>
#include <stdio.h>
#include <stdlib.h>
int main() {
int num = 0;
int *p_num = (int *)malloc(5 * sizeof(int));//动态分配内存及大小
if(p_num) {
printf("分配成功!\n"); //分配成功!
for(num = 0; num < 5; num++) {
*(p_num + num) = num;
}
for(num = 0; num < 5; num++) {
printf("%d ", *(p_num + num)); //0 1 2 3 4
}
printf("\n");
free(p_num); //释放动态分配的内存
p_num = NULL; //野指针置空
}
return 0;
}
动态分配内存一个很重要的使用场景:
"调用函数可以使用被调用函数动态分配的内存"
(之前是把被掉用函数的存储区静态化 static)
<实际示例>/*生成7组(1-36)之间的数的彩票(动态分配内存+回调函数)*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int *create(int size) { //彩票不重复
int *p_num = (int *)malloc(size * sizeof(int));
int num = 0, i = 0;
if(p_num) { //必须加分支判断动态内存是否分支成功
for(num = 0; num < size; num++) {
*(p_num + num) = rand() % 36 + 1;
for(i = 0;i < num; i++) {
if(*(p_num + num) == *(p_num + i)) {
i--;
}
}
}
}
return p_num;
}
void print(int *p_num) { //回调函数
printf("%d ", *p_num);
}
void for_each(int *p_num, int size, void (*p_func)(int *)) {
int num = 0;
for(num = 0; num < size; num++) {
p_func(p_num + num);
}
}
int main() {
srand(time(0));
int *p_num = create(SIZE); //编译时-D指定SIZE宏的值
if(p_num) {
for_each(p_num, SIZE, print);
printf("\n");
free(p_num); //分支内释放内存
p_num = NULL;
}
return 0;
}
【练习】
编写函数从键盘得到一个点的位置,把这个点的位置传递给调用函数
要求用动态分配内存记录点的位置
<思想>
动态内存分配代码框架
/*代码*/
#include <stdio.h>
#include <stdlib.h> //警告:隐式声明问题,切记不要忘记头文件
typedef struct {
int row, col;
} Pt;
Pt *dian(void) {
Pt *p_pt = (Pt *)malloc(sizeof(Pt));
if(p_pt) { //分支判断是框架一部分
printf("请输入点的坐标行,列:");
scanf("%d%d", &(p_pt->row), &(p_pt->col));
}
return p_pt;
}
int main() {
Pt *p_d = dian();
if(p_d) {
printf("坐标为:(%d, %d)\n", p_d->row, p_d->col);
free(p_d);
p_d = NULL;
}
return 0;
}
【练习】
编写函数计算两个点的中间位置,并把这个位置传递给调用函数
用动态分配内存记录中间点的位置
<思想>
结构体/函数/动态分配内存框架
/*代码*/
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int row, col;
} Pt;
Pt *mid(const Pt *p_d1, const Pt *p_d2) { //不会修改结构体指针的地址,所有要都加 const
Pt *p_mid = (Pt *)malloc(sizeof(Pt));
if(p_mid) {
p_mid->row = (p_d1->row + p_d2->row)/2;
p_mid->col = (p_d1->col + p_d2->col)/2;
}
return p_mid;
}
int main() {
Pt d1, d2;
printf("请输入第1个点的坐标行,列:");
scanf("%d%d", &(d1.row), &(d1.col));
printf("请输入第2个点的坐标行,列:");
scanf("%d%d", &(d2.row), &(d2.col));
Pt *p_mid = mid(&d1, &d2);
if(p_mid) {
printf("中间点坐标为:(%d,%d)\n", p_mid->row, p_mid->col);
free(p_mid);
p_mid = NULL;
}
return 0;
}
【动态分配内存】
"calloc" 函数也可以动态分配内存
这个函数可以把分配的所有内存内容都设置成 0 (malloc内容是随机的未初始化)
这个函数也需要包含"<stdlib.h>"的头文件
这个函数需要两个参数:第 1 个参数表示希望分配的"存储区个数",第 2 个参数表示"单个存储区的大小"
这个函数的【返回值】是分配好的第一个存储区的地址(同malloc函数)
这个函数也可能分配失败,如果失败则返回值是NULL(空地址nil)
"realloc" 函数可以调整一段动态分配内存的大小(尽量少使用这个函数)
相关文章推荐
- 结构struct 联合Union和枚举Enum的细节讨论
- 结构struct 联合Union和枚举Enum的细节讨论
- 结构struct 联合Union和枚举Enum的细节讨论
- 联合体union 枚举enum
- C++11中枚举enum和union,顺带说一下内存对齐和大小端问题
- [Java 14 枚举] Enum, enum
- 14. JAVA 枚举(Enum、类集EnumMap&EnumSet 、实现接口、定义抽象方法) ----- 学习笔记
- [Java 14 枚举] Enum, enum
- android ndk 05 C语言 _union_enum 联合体 枚举 IO文件操作(文件加密解密)
- C语言 枚举(enum)、宏定义(#define)、结构体(struct)的一种联合应用
- 联合体union和枚举enum的用法总结--基于C语法
- C和C++中结构体(struct)、联合体(union)、枚举(enum)的区别
- Neo4j CQL -(14) -UNION联合
- 结构体(struct)、联合体(union)、枚举(enum) 三种结构的区别及各个结构细节的总结
- 结构体struct、枚举enum、联合体union、位字段、自定义类型typedef、字节对齐
- 结构体(Struct)联合体(Union)枚举(enum)总结
- union 或者 union all 与 order by 的联合使用
- 结构体struct && 联合union
- iOS - OC Enum 枚举