C[内存管理/构造类型]

2023-5-10首次编辑


Unit 8:内存管理

一.概念

作用域:

  • 变量起作用的范围

生命周期:

  • 开辟空间到释放空间的整个过程

局部变量(auto)

  • 作用域 : 在定义变量的{}中有效
  • 生命周期 : 程序运行至变量定义处开辟空间,所在的函数结束之后释放空间
  • 未初始化的值 : 随机

静态局部变量(static)

  • 作用域 : 在定义变量的{}中有效
  • 生命周期 : 指向面函数之前就已经开辟空间,程序结束之后才释放空间
  • 未初始化的值 : 0

全局变量

  • 作用域 : 整个工程,所有文件
  • 生命周期 : 执行main函数之前九已经开辟空间,程序结束之后才释放空间
  • 未初始化的值 : 0

静态全局变量

  • 作用域 : 当前文件
  • 生命周期 : 执行main函数之前九已经开辟空间,程序结束之后才释放空间
  • 未初始化的值 : 0

注意:

  • 在头文件中,全局变量只声明不定义,定义只放在源文件中
  • 变量重名时,考虑作用域的前提下,采用就近原则(不同作用域可以重名)

二.静态函数

  • 只可以被当前文件函数调用
  • 普通函数 == 全局函数,整个工程可以调用

三.mem系列

#include <string.h>

1.memset
void *memset(void *s, int c, size_t n);
功能:将s的内存区域的前n个字节以参数c填入
参数:
       s:需要操作内存s的首地址
       c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255
       n:指定需要设置的大小
返回值:s的首地址

2.memcpy
void *memcpy(void *dest, const void *src, size_t n);
功能:拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数:
       dest:目的内存首地址
       src:源内存首地址
       //dest和src所指的内存空间不可重叠,可能会导致程序报错
       n:需要拷贝的字节数
返回值:dest的首地址

3.memcmp
int memcmp(const void *s1, const void *s2, size_t n);
功能:比较s1和s2所指向内存区域的前n个字节(遇到0,'\0'不会结束操作)
参数:
       s1:内存首地址1
       s2:内存首地址2
       n:需比较的前n个字节
返回值:
       相等:=0
       大于:>0
       小于:<0
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

//1.memset
//填充
void test1()
{
	int a = 10;
	memset(&a, 0, sizeof(a));
	printf("a=%d\n", a); //0

	char buf[10] = "";
	strcpy(buf, "hello");
	printf("%s\n", buf); //hello

	memset(buf, 0, sizeof(buf));
	printf("%s\n", buf); //空

	memset(buf, 'a', sizeof(buf)-1);
	printf("%s\n", buf); //aaaaaaaaa
}

//2.memcpy
//拷贝
void test2()
{
	char str1[128] = "";
	char str2[128] = "abcdef\0ABCDEF";
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int b[10] = { 0 };

	memcpy(str1, str2, sizeof(char) * 10); //遇到'\0'会结束拷贝
	printf("%s\n", str1); //abcdef

	memcpy(b, a, sizeof(10) * 5);
	for (int i = 0; i < sizeof(b)/sizeof(b[0]); i++)
	{
		printf("%d", b[i]);
	}
	printf("\n");
}

//3.memcmp
//比较
void test3()
{
	char num1[] = { 1,0,3,4,5,6,7 };
	char num2[] = { 1,0,3,7,6,5,4 };
	char str1[] = "abc\0ABC";
	char str2[] = "abc\0CBA";

	printf("%d\n", memcmp(num1, num2, sizeof(char) * 7)); //-1
	printf("%d\n", strncmp(num1, num2, sizeof(char) * 7)); //0,遇到0会结束

	printf("%d\n", memcmp(str1, str2, sizeof(str1))); //-1
	printf("%d\n", strncmp(str1, str2, sizeof(str1))); //0,遇到'\0'会结束
}

int main()
{
	//test1();
	//test2();
	test3();
	system("pause");
	return 0;
}

四.申请/释放

#include <stdlib.h>

1.malloc申请
void *malloc(size_t size);
功能:在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。分配的内存空间内容不确定,一般使用memset初始化。
参数:
       size:需要分配内存大小(单位:字节)
返回值:
       成功:分配空间的起始地址
       失败:NULL

2.free释放
void free(void *ptr);
功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,指向被释放区域的首地址。
     //对同一内存空间多次释放会出错
参数:
     ptr:需要释放空间的首地址,被释放区应是由malloc函数所分配的区域。
返回值:无
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>

void test1()
{
	//1.申请
	char *p = (char *)malloc(1024);

	memset(p, 0, 1024);
	strcpy(p, "helloworld");
	printf("%s\n", p);
	
	//2.释放
	//free只能释放一次
	//地址必须与malloc的相同,不能改变这个地址
	free(p);
	//free(p+1); //err
}

void test2()
{
	int *p = (int *)malloc(sizeof(int) * 10);
	memset(p, 0, sizeof(int) * 10);
	*p = 1000;
	*(p + 5) = 2000;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
	free(p);
}

int main()
{
	test1();
	test2();
	system("pause");
	return 0;
}

五.内存泄漏/污染

内存泄漏:

  • 只申请,不释放

内存污染:

  • 向没有申请过的内存空间写入数据

程序退出后,所使用的所有内存都将释放

六.子函数返回地址

返回变量的地址:

  • 只有普通局部变量的地址不可以返回
  • 普通局部变量在所在的函数结束之后会被释放

返回堆区的地址

  • 只要没被free释放
  • 都可以被返回
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>

void *func()
{
	char *q = (char *)malloc(100);
	return q; //返回堆区地址
}

void test()
{
	char *p = func();

	//p = "hello";
	//进行free会报错
	//p此时指向的是文字常量区"hello"

	strcpy(p, "hello");
	//p此时指向的还是堆区空间

	free(p);
}

int main()
{
	test();
	system("pause");
	return 0;
}

七.大小端

小端:

  • 高位高地址,低位低地址(逆序)
  • 应用于小型计算机

大端:

  • 高位低地址,低位高地址(顺序)
  • 应用于大型服务器,网络上的数据

Unit 8:构造类型

一.结构体(复合类型)

将多个相同或不同类型的数据存放在一块连续的内存空间中

#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

//定义一个结构体数据类型
//1.只是一个类型丶一个模板,没有空间,不可以给结构体成员赋值
//2.{}后面需要加分号
struct stu1
{
	int id;
	int age;
	char name[128];
}a; //定义类型时,同时定义了两个结构体变量struct stu a;

void test1()
{
	//1.初始化所有成员
	//struct stu d = { 1,20,"悟空" };
	//2.部分成员初始化,其他成员内容为0
	//struct stu d = { .age = 20 };
	//3.所有成员不初始化
	struct stu1 d;

	//1.通过结构体变量操作结构体成员,使用点域.
	/*d.id = 2;
	d.age = 22;
	strcpy(d.name, "贝吉塔");
	printf("%d %d %s\n", d.id, d.age, d.name);*/
	//2.通过结构体地址操作结构体成员,使用->
	(&d)->id = 3;
	(&d)->age = 3000;
	strcpy((&d)->name, "比克大魔王");
	printf("%d %d %s\n", (&d)->id, (&d)->age, (&d)->name);
}

//结构体数组:
//每一个数组元素都是结构体
void test2()
{
	struct stu1 num[5] = { {1,20,"悟空"},{2,22,"贝吉塔"},{3,3000,"比克大魔王"} };

	//打印
	for (int i = 0; i < sizeof(num)/sizeof(num[0]); i++)
	{
		printf("%d %d %s\n", num[i].id, num[i].age, num[i].name);
	}
}

//结构体套结构体
struct stu2
{
	struct stu1 s;
	char subject[128];
};

void test3()
{
	struct stu2 c;
	c.s.id = 1;
	c.s.age = 20;
	strcpy(c.s.name, "悟空");
	strcpy(c.subject, "C");

	printf("%d %d %s %s\n", c.s.id, c.s.age, c.s.name, c.subject);
}

//结构体赋值
void test4()
{
	struct stu1 a = { 1,20,"悟空" };
	struct stu1 b;

	b = a; //相同类型的变量之间可以相互赋值
	printf("%d %d %s\n", b.id, b.age, b.name);

}

int main()
{
	//test1();
	//test2();
	//test3();
	test4();
	system("pause");
	return 0;
}

二.结构体与指针

#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

struct stu
{
	int id;
	int age;
	char name[128];
};

//结构体指针
void test1()
{
	//p不能是野指针
	//1.
	//struct stu a;
	//struct stu *p = &a;
	//2.
	struct stu *p = (struct stu *) malloc(sizeof(struct stu));

	p->id = 1;
	p->age = 20;
	strcpy(p->name, "悟空");
	printf("%d %d %s\n", p->id, p->age, p->name);
	free(p);
}

//结构体套指针
struct t
{
	int a;
};

struct tea
{
	int id;
	char *p; //字符指针
	struct t *b; //结构体指针
};

void test2()
{
	struct tea *tmp = (struct tea *)malloc(sizeof(struct tea));
	tmp->id = 100;

	//1.p指向常量区
	//tmp->p = "hello";
	//strcpy(tmp->p, "world"); //err:不可以修改常量区的内容
	//2.p指向堆区空间
	tmp->p = (char *)malloc(100);
	strcpy(tmp->p, "hello");
	strcpy(tmp->p, "world");
	printf("%s\n", tmp->p);
	
	//不能直接赋值b所指向的空间,否则会造成野指针
	tmp->b = (struct t *)malloc(sizeof(struct t));
	tmp->b->a = 1000;

	//从内往外释放申请的堆区空间
	free(tmp->p);
	free(tmp->b);
	free(tmp);
}

//结构体数组作为函数的形参
void set_num(struct stu *p, int n)
{
	for (int i = 0; i < n; i++)
	{
		//(*(p + i)).id = i + 10;
		//(p + i)->id = i + 10;
		p[i].id = i + 10; //10 11...
		p[i].age = i + 100; //100 101...
		char buf[128] = "";
		sprintf(buf, "%d%d%d", i, i, i);
		strcpy(p[i].name, buf); //000 111...
	}
}

void test3()
{
	struct stu num[5];
	memset(num, 0, sizeof(num));
	set_num(num, sizeof(num) / sizeof(num[0])); //此处num = &num[0]

	for (int i = 0; i < sizeof(num)/sizeof(num[0]); i++)
	{
		printf("%d %d %s\n", num[i].id, num[i].age, num[i].name);
	}
	printf("\n");
}

//const修饰的结构体指针变量
void test4()
{
	struct stu a;
	struct stu b;
	a.id = 1;
	b.id = 2;

	//1.结构体常量指针
	struct stu const *p = &a;
	//p->id = 100; //err:不能修改指针所指向空间的内容
	p = &b; //可以修改指针指向
	printf("%d\n", p->id);

	//2.结构体指针常量
	struct stu * const q = &a;
	q->id = 100; //可以修改指针所指向空间的内容
	//q = &b; //err:不能修改指针指向
	printf("%d\n", q->id);
}

int main()
{
	//test1();
	//test2();
	//test3();
	test4();
	system("pause");
	return 0;
}

三.共用体

  • 多个变量共用同一块内容空间
  • 同一时刻,只能有一个变量起作用
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>

union abc
{
	short a;
	char buf[2];
};

//共用体验证大小端
void test()
{
	union abc tmp;
	tmp.a = 0x0102;
	if (tmp.buf[0] = 0x01)
	{
		printf("大端:高位低地址,低位高地址(顺序)\n");
	}
	else
	{
		printf("小端:高位高地址,低位低地址(逆序)\n");
	}
}

int main()
{
	test();
	system("pause");
	return 0;
}

四.枚举

  • 将枚举类型的变量值一一列出来,变量的值只限于列举出来的值的范围内
#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>

//值都为常量,默认从0开始(顺序递增)
enum ab { SUN, RAIN = 5, SNOW };

void test()
{
	enum ab tmp = SNOW; //枚举变量的值只可以赋值为{}里面的值

	printf("%d %d %d\n", SUN, RAIN, SNOW); //0,5,6
	int a = 0;
	scanf("%d", &a);
	if (SUN == a)
	{
		printf("sleep\n");
	}
	else if (RAIN == a)
	{
		printf("music\n");
	}
	else
	{
		printf("game\n");
	}
}

int main()
{
	test();
	system("pause");
	return 0;
}

五.typedef关键字

取别名

#define _CRT_SECIRE_NO_WARNINGS
#include <stdio.h>

//常用于结构体
typedef struct stu 
{
	int id;
	int age;
}SS; //取的别名

void test()
{
	SS tmp; //SS == struct stu
	tmp.id = 1;
	tmp.age = 100;
	printf("%d %d\n", tmp.id, tmp.age);
}

int main()
{
	test();
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cforikl_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值