当前位置: 首页 > news >正文

数据分析——动态分配内存、结构体


一、动态分配内存

申请堆区的空间,需要手动申请,手动释放

如果直接定义在代码中的变量,是系统默认分配栈区的空间,栈区空间自动申请,生命周期结束自动释放。

需要用到头文件#include

malloc和free一定成对出现

1.1 malloc

函数原型:
void *malloc(size_t size);   //有参数有返回值函数
功能:
从堆区申请size个字节,并将这一片空间的首地址返回给主调函数处
所以返回值为void*方便强转
void*表示万能指针(可以被强转成任何类型的指针)
使用:
int *p = (int*)malloc(sizeof(int));  //赋值运算符右侧的(int*)表示将malloc申请的首地址,强转成int类型
//让int*类型的指针p指向malloc申请的(sizeof(int))大小的堆区空间
//int *p = (int*)malloc(4);

1.2 free

功能:释放堆区的内存空间

函数原型:
    void free(void *ptr);
返回值:无
参数:要释放的堆区空间的首地址
堆区申请空间一定要使用free释放防止内存泄漏
一般释放完堆区内存后,会将原本指向堆空间的指针指向NULL

1.3 指针的强转

因为不同数据类型的指针的大小相同,所以指针之间的强转是安全的

但是会改变指针的偏移量

int *p; 
char *p1=(char*)p; 
上面的强转过程不会造成数据的丢失,但是如果p本身已经指向int类型数据,使用p1访问该数据时,可能只访问到低字节的数据

1.4 堆空间的地址作为指针函数的返回值

#include <stdio.h>
#include <stdlib.h>
int *func()
{
    int *p=(int*)malloc(4);
    *p=49;
    return p;
}
int main(int argc, const char *argv[])
{

    int *ret=func();  //因为func的返回值为堆空间申请的内存,不手动释放不会被回收    
    printf("%d\n",*ret);
    printf("%p\n",ret);
 //释放堆内存
 free(ret);
    return 0;
}

1.5 悬空指针

指针指向一片地址,但是对这片地址没有使用权,间接访问可能会发生段错误

1.5.1 指向已经堆放的空间

#include <stdio.h>
#include <stdlib.h>
int *func()
{
    int *p=(int*)malloc(4);
    *p=49;
    return p;
}
int main(int argc, const char *argv[])
{

    int *ret=func();  //因为func的返回值为堆空间申请的内存,不手动释放不会被回收    
    printf("%d\n",*ret);
    printf("%p\n",ret);
 //释放堆内存
 free(ret);
 printf("%p\n",ret);  //释放前后,ret的值不变
 //一般释放完堆区内存后,会将原本指向堆空间的指针指向NULL
 ret=NULL;
    return 0;
}

1.5.2 指向已经被回收的空间

#include <stdio.h>
int *func()
{
    int a=90;
    int *p=&a;
    return p;   //这种方式返回编译不会报警告,因为智能识别到p是局部变量,返回p的值
}
int main(int argc, const char *argv[])
{
    int *ret=func();  //ret是一个悬空指针,指向已经被回收的栈空间的地址
    printf("%d\n",*ret);
    printf("%p\n",ret);
    return 0;
}

1.6 特殊指针

  1. 空指针:指向NULL的指针,间接访问一定会段错误
  2. 野指针:没有明确指向的指针,间接访问错误不可预知
  3. 悬空指针:指向已经被回收没有使用权的地址的指针,间接访问错误不可预知(知道指向,但是对空间没有使用权)
  4. 万能指针:可以强转为任意类型的指针

1.7 大小端存储

小端存储:低数据位存在低地址上

大端存储:低数据位存在高地址上

#include <stdio.h>
int main()
{
    int a = 0x12345678;
    char *p = &a;  //因为要取一个字节的数据进行判断
    //如果使用int *,*p取得是四个字节的内容
    if(*p==0x78)
    {
        printf("小端存储\n");    
    }
    else if(*p=0x12)
    {
        printf("大端存储\n");    
    }
}

1.8 练习

1.8.1 在堆区申请一个数组的空间,完成对数组元素的输入和输出

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	int *p=(int *)malloc(sizeof(int)*5);  // 申请一个客村下五个数的数组空间
	printf("请输入存入数组内的数(5)\n");
	for(int i=0;i<5;i++)  // 输入
	{
		scanf("%d",p+i);
	}
	printf("arr= ");
	for(int j=0;j<5;j++)  // 输出
	{
		printf("%d ",*(p+j));
	}
	free(p);  // 释放空间
	printf("\n");
	return 0;
}

二、结构体

属于构造数据类型

存储某些特征的结构,由多个或一个数据类型构成

2.1 定义

以学生为例,一个学生的信息包含:姓名、年龄、成绩、学校,这四个信息需要多个数据类型,数组无法完成,可以将这样的结构封装成结构体。

struct 结构体名
{
 //结构体成员
 数据类型 变量名;
 数据类型 变量名;
 ····   
};   //定义了一个结构体类型

学生结构体的实现

#include <stdio.h>
//一般结构体的定义写在全局处
struct Stu
{
    int age;
 //年龄
    char name[100];
 //姓名
    float score;  //成绩
};
int main(int argc, const char *argv[])
{
    
    return 0;
}

2.2 定义结构体变量

struct 结构体名 结构体变量名;

2.3 初始化和赋值

2.3.1 完全初始化

定义的同时,按照结构体成员在结构体中的顺序完全初始化
struct Stu s2={20,"zhangsan",75.2}; 

2.3.2 不完全初始化

定义的同时,按照结构体成员在结构体中的顺序给部分成员赋值,未初始化的部分,默认为0
struct Stu s3={21};

2.3.3 按成员初始化

定义的同时,在初始化列标中以访问指定结构体成员的方式赋值
struct Stu s4={.name="lisi",.age=22};
#include <stdio.h>
#include <string.h>
//一般结构体的定义写在全局处
struct Stu
{
    int age;
    char name[100];
    float score;
}s;  //s是在定义结构体类型的同时,直接定义结构体变量

int main(int argc, const char *argv[])
{
    struct Stu s1;   //定义了一个Stu类型的结构体变量s1
    /**************结构体的初始化和赋值*****************/
    struct Stu s2={20,"zhangsan",75.2};   //定义结构体变量,并完全初始化
    printf("s2.age=%d\n",s2.age);
    printf("s2.name=%s\n",s2.name);
    printf("s2.score=%f\n",s2.score);
    struct Stu s3={21}; //定义结构体变量不完全初始化,未初始化结构体成员默认为0
    printf("s3.age=%d\n",s3.age);
    printf("s3.name=%s\n",s3.name);
    printf("s3.score=%f\n",s3.score);
    //定义结构体变量,按照指定的顺序给某些/全部成员初始化
    struct Stu s4={.name="lisi",.age=22};
    printf("s4.age=%d\n",s4.age);
    printf("s4.name=%s\n",s4.name);
    printf("s4.score=%f\n",s4.score);
    /****************结构体变量的赋值******************/
 s1.age=18;    //单个成员的赋值,不能再进行整体赋值
    //s1.name="zhaoliu";
    //gets(s1.name);   用gets的方式给字符数组成员赋值
    strcpy(s1.name,"zhaoliu");
 s1.score=79.1;
    printf("s1.age=%d\n",s1.age);
    printf("s1.name=%s\n",s1.name);
    printf("s1.score=%f\n",s1.score);
    
    return 0;
}

2.4 访问结构体中的成员

通过.运算符访问结构体中的成员

  1. 结构体变量名.结构体成员名;
  2. 访问Stu中的name成员,已有结构体变量s1
  3. s1.name ---->获取到结构体变量s1中name成员的值

2.5 结构体指针访问结构体成员

struct 结构体名 *结构体指针名;
通过结构体指针访问结构体成员,用->访问
格式:
    结构体指针名->结构体成员;
    
 s1.age=18;    //单个成员的赋值,不能再进行整体赋值
    //s1.name="zhaoliu";
    //gets(s1.name);   用gets的方式给字符数组成员赋值
    strcpy(s1.name,"zhaoliu");
 s1.score=79.1;
    //定义结构体指针,指向s1
    struct Stu *p = &s1; 
    printf("p->age=%d\n",p->age);
    printf("p->name=%s\n",p->name);
    printf("p->score=%f\n",p->score);
    

2.6 嵌套的结构体

#include <stdio.h>
struct Person
{
    int age;
    char name[100];
};
struct Stu
{
    struct Person p1;  //在Stu中嵌套Person类型的成员
    float score;
};
/*struct A
{
    //A里面包含了B类型结构体的定义
    struct B
    {
        int a;
        char c;
    };
    float b;
};*/
struct A
{
    //A里面包含了B类型的结构体成员b1
    struct B
    {
        int a;
        char c;
    }b1;
    float b;
};
//上面两种不同的结构体类型的定义都可以使用里面声明的struct B结构体类型
int main(int argc, const char *argv[])
{
    struct Stu s1;
 s1.p1.age = 90;
    printf("%d\n",s1.p1.age);
    struct B b2;
    return 0;
}

2.7 结构体的大小

结构体的大小需要进行字节对齐

字节对齐的规则:(通用的嵌套结构体也可以用)

  1. 每一个结构体中的成员对首地址的偏移量,必须是该成员本身对齐量的整数倍
  2. 成员本身的对齐量=成员本身对齐量>操作系统对齐量?操作系统的对齐量:成员本身大小(操作系统的对齐量,64位8Byte,32位4Byte)
  3. 结构体整体的大小,必须是最大对齐成员对齐量的整数倍
#include <stdio.h>
struct Person
{
    int age;
    char name[5];
};
struct Stu
{
    struct Person p1;  //在Stu中嵌套Person类型的成员
    float score;
};
/*struct A
{
    //A里面包含了B类型结构体的定义
    struct B
    {
        int a;
        char c;
    };
    float b;
};*/
struct Test
{
    char a;
    double k;
    int b;
};
struct A
{
    //A里面包含了B类型的结构体成员b1
    struct B
    {
        int a;
        char c;
    }b1;
    float b;
};
//上面两种不同的结构体类型的定义都可以使用里面声明的struct B结构体类型
int main(int argc, const char *argv[])
{
    struct Stu s1;
    s1.p1.age = 90;
    printf("%d\n",s1.p1.age);
    struct B b2;
    printf("%ld\n",sizeof(struct Person));
    printf("%ld\n",sizeof(struct A));
    return 0;
}

2.8 练习

2.8.1 从堆区申请一个结构体数组的空间,并完成对结构体成员的输入和输出

#include <stdio.h>
#include <stdlib.h>
struct Stu  // 定义一个结构体
{
	int age;
	char name[100];
	float score;
};
int main(int argc, const char *argv[])
{
	struct Stu *arr=(struct Stu*)malloc(sizeof(3));  // 从堆区申请一个储存三个结构体变量的空间
	for(int i=0;i<3;i++)  // 输入结构体内数据
	{
		printf("请输入学生姓名\n");
		scanf("%s",(arr+i)->name);
		printf("请输入学生年龄和分数\n");
		scanf("%d%f",&(arr+i)->age,&(arr+i)->score);
	}
	printf("姓名\t年龄\t分数\n");
	for(int j=0;j<3;j++)  // 输出结构体内数据
	{
		printf("%s\t%d\t%f\n",(arr+j)->name,(arr+j)->age,(arr+j)->score);
	}
    free(arr);  // 释放空间
	printf("\n");
	return 0;
}

2.8.2 计算下列结构体大小

相关文章:

  • c语言基础08
  • 【Elasticsearch】监控与管理:集群健康检查
  • 【ENSP】华为设备console 认证配置
  • (学习总结22)Linux 基本指令1
  • 离散数学之谓词等值式与蕴涵式
  • 2.13寒假作业
  • 初阶c语言(练习题,猜随机数)
  • 手写一个Java Android Binder服务及源码分析
  • Day 38 卡玛笔记
  • web自动化笔记(二)
  • MySQL判空函数--IFNULL函数的使用
  • TikTok成功打破传统媒体壁垒,用户涌入平台创作
  • 在带有Intel NPU的Windows上安装IPEX-LLM
  • Python 闭包与装饰器
  • Softhsm储存安全数据性能整理
  • Swift 的 KeyPath 是什么?
  • NS6116-同步降压稳压器 3.1A输出电流 耐压30V
  • 测试方案整理
  • 【原理图PCB专题】自制汉字转码工具,适配Allgero 17版本 Skill
  • C++模板进阶
  • 企业公示信息查询系统广西/seo网络培训机构
  • 什么网站排名做的最好/百度金融
  • 网站建设系统公司/郑州网站定制
  • wordpress自定义密码/搜狗seo查询
  • 仁怀企业网站建设/seo人员是什么意思
  • 邢台网站建设行情/整站优化seo