一、指针高级
1.野指针和悬空指针
野指针:指针指向的空间未分配
悬空指针:指针指向的空间已分配,但是被释放了
#include<stdio.h>
int* method();
int main()
{
int a = 10;
int* p1 = &a;
printf("%p\n", p1);
printf("%d\n", *p1);
//野指针
int* p2 = p1 + 10;
printf("%p\n", p2);
printf("%d\n", *p2);
//悬空指针
int* p3 = method();
printf("拖点时间\n");
printf("%p\n", p3);
printf("%d\n", *p3);
return 0;
}
int* method()
{
int num = 10;
int* p = #
return p;
}
不要使用野指针和悬空指针
2.void类型的指针
特殊类型:
Void*p不表示任何类型
特点:无法获取数据,无法计算,但是可以接收任意地址
不同类型的指针之间,是不能互相赋值的
void类型的指针打破上面的观念
void没有任何类型,好处可以接受任意类型指针记录的内存地址
缺点:void类型的指针,无法获取变量里面的数据,也不能进行加、减的计算
#include<stdio.h>
void swap(void* p1, void* p2, int len);
int main()
{
int a = 10;
short b = 20;
int* p1 = &a;
short* p2 = &b;
printf("%d\n", *p1);
printf("%d\n", *p2);
void* p3 = p1;
void* p4 = p2;
int c = 100;
int d = 200;
swap(&c, &d, 4);
printf("c=%d,d=%d\n",c,d);
return 0;
}
void swap(void* p1, void* p2, int len)
{
char* pc1 = p1;
char* pc2 = p2;
char temp = 0;
for (int i = 0; i < len; i++)
{
temp = *pc1;
*pc1 = *pc2;
*pc2 = temp;
pc1++;
pc2++;
}
}
3.二级指针和多级指针
指针数据类型:跟指向空间中,数据的类型是保持一致的
作用:二级指针可以操作一级指针记录的地址
二级指针:
格式;数据类型**指针名
- 作用一:利用二级指针修改一级指针里面记录的内存地址
- 作用二:利用二级指针获取到变量中记录的数据
4.数组指针
概念:指向数组的指针,叫做数组指针
作用:方便的操作数组中的各种数据
练习: 利用指针遍历数组
5.数值指针的细节
练习:数组指针的细节
- arr参与计算的时候,会退化为第一个元素的指针特殊情况:
- sizeof 运算的时候,不会退化,arr还是整体&arr获取地址的时候,不会退化
- &arr获取地址的时候,不会退化,记录的内存地址第—个元素的首地址,也是数组的首地址,步长:数据类型*数组的长度49
- arr参与计算的时候,会退化为第一个元素的指针,记录的内存地址第一个元素的首地址,也是数组的首地址,步长:数据类型 int 4
6.利用索引遍历第一种格式的二维数组
二维数组
概念:把多个小数组,放到一个大的数组当中
定义格式一和定义格式二:
利用索引的方式进行遍历
arr[0]:表示二维数组当中的第一个一维数组,{1,2,3,4,5}
arr[1]:表示二维数组当中的第二个一维数组,{11,22,33,44,55}
arr[2]:表示二维数组当中的第三个一维数组,{111,222,333,444,555}
弊端:要求二维数组里面每个一维数组的长度都要一样
7.利用索引遍历第二种格式的二维数组
核心:事先先把所有的一维数组定义完毕,再放入到二维数组当中。
注意:数组的数据类型,跟内部存储的元素类型保持一致int==*==arr[3]
arr1:使用数组名进行计算的时候,退化为指向第一个元素的指针,此时不再表示数组的那个整体了
指针----内存地址 64位win 8个字节
不能使用int len=sizeof(arr[i])/sizeof(int);
#include<stdio.h>
int main()
{
int arr1[3] = { 1,2,3 };
int arr2[5] = { 1,2,3,4,5 };
int arr3[9] = { 1,2,3,4,5,6,7,8,9 };
//预先计算每一个数组真实的长度
int len1 = sizeof(arr1) / sizeof(int);
int len2 = sizeof(arr2) / sizeof(int);
int len3 = sizeof(arr3) / sizeof(int);
//再定义一个数组,装所有数组的长度
int lenArr[3] = { len1,len2,len3 };
//把三个一位数组放入到二维数组当中
int* arr[3] = { arr1,arr2,arr3 };
//利用索引的方式进行遍历
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < lenArr[i]; j++)
{
printf("%d", arr[i][j]);
}
printf("\n");
}
return 0;
}
8.利用指针遍历第一种格式的二维数组
二维数组里面存的是一维数组,一维数组里面存的才是数据本身。
#include<stdio.h>
int main()
{
int arr[3][5] =
{
{1,2,3,4,5},
{11,22,33,44,55},
{111,222,333,444,555}
};
//数组指针里面的数据类型:要跟数组
//内部的元素类型保持一致
//二维数组里面存储的是一维数组int[5]
int(*p)[5] = arr;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j <5; j++)
{
printf("%d ", *(*p+j));
}
printf("\n");
//移动二维数组的指针,继续遍历下一个一维数组
p++;
}
return 0;
}
9.利用指针遍历第二种格式的二维数组
10.数组指针和指针数组
数组指针:指向数组的指针
作用:方便的操作数组中的各种数据
举例:int* p = arr; 步长为:int(4字节)
举例:int(*p)[5] = &arr; 步长为:int乘5(20字节)
指针数组:存放指针的数组作用:用来存放指针
举例:int * p[5],这个数组里面存着int类型的指针
11.函数指针
格式:返回值类型(*指针名)(形参列表)
作用:利用函数指针,可以动态的调用函数
#include<stdio.h>
void method1();
int method2(int num1, int num2);
int main()
{
void (*p1)()=method1;
int (*p2)(int, int)=method2;
p1();
int num=p2(10,20);
printf("%d\n", num);
return 0;
}
void method1()
{
printf("method1\n");
}
int method2(int num1, int num2)
{
printf("method2\n");
return num1 + num2;
}
12.函数指针和函数指针数组的练习
定义加、减、乘、除、四个函数
用户键盘录入三个数字
前两个表示参与计算的数字
第三个数字表示调用的函数
1:加法
2:减法
3:乘法
4:除法
#include<stdio.h>
int add(int num1, int num2);
int subtract(int num1, int num2);
int mutiply(int num1, int num2);
int divide(int num1, int num2);
int main()
{
//函数指针数组
int (*arr[4])(int,int) = { add,subtract,mutiply,divide };
printf("请录入两个数字参与计算\n");
int num1;
int num2;
scanf("%d%d",&num1,&num2);
printf("%d\n", num1);
printf("%d\n", num2);
int choose;
printf("请录入一个数字表示要进行的计算\n");
scanf("%d", &choose);
int res=(arr[choose - 1])(num1,num2);
printf("%d\n", res);
}
int add(int num1, int num2)
{
return num1 + num2;
}
int subtract(int num1, int num2)
{
return num1 - num2;
}
int mutiply(int num1, int num2)
{
return num1 * num2;
}
int divide(int num1, int num2)
{
return num1 / num2;
}
细节:只有形参完全相同而且返回值也要一样的函数,才能放到同一个函数指针数组当中
二、字符串
1.字符串的两种定义方式和底层细节
1.1利用字符数组+双引号的方式定义字符串 char str1[4] = “abc”;
- 细节1:
在底层,实际存储的时候,C语言还是会帮我们把字符串"abc"转换成字符数组进行保存,并且在末尾还要再加上’\0’
{‘a’ ,‘b’ ,‘c’ ,‘\0’}; - 细节2:
数组的长度,要么不写,如果要写的话,记得要把结束标记的空间给预留出来 - 细节3:
字符数组+双引号的方式定义字符串,内容是可以发生改变的
1.2利用指针+双引号的方式定义字符串 char* str2 = “abcd”;
- 细节1:
在底层,实际存储的时候,C语言还是会帮我们把字符串"abcd"转换成字符数组进行保存,并且在末尾还要再加上’\0’
{‘a’ ,‘b’ ,‘c’ ,‘d’ ,‘\0’}; - 细节2:
利用指针+双引号的方式定义字符串,会把底层字符数组放在只读常量区
只读常量区的特点:
内容不可以修改的
里面定义的字符串是可以复用的(在创建abcd的时候,会检查只读常量区里面有没有abcd,如果没有才会创建新的,如果已经有了,不会创建新的,而是进行复用)
2.练习:键盘录入字符并遍历
需求:键盘录入一个字符串,使用程序实现在控制台遍历该字符串
底层逻辑:
程序在运行的时候,首先会创建一个长度为100的字符数组str
在进行健盘录入的时候,会把每一个字符存入到上面的str数姐当中,并加上结束标记\0
在这个过程中,需要修改字将数姐的内容,所以第一种方式可以。第二种方式不可以
3.字符串数组
需求:定义一个数组存储5个学生的名字并进行遍历
字符的底层其实就是字符数组
把多个字符数组,再放入到一个大的数组当中
#include<stdio.h>
int main()
{
//二维数组的方式定义
char strArr[5][100] =
{
"zhangsan",
"lisi",
"wangwu",
"zhaoliu",
"qiangqi"
};
for (int i = 0; i < 5; i++)
{
char* str = strArr[i];
printf("%s\n", str);
}
//指针数组的方式定义
char* strArr2[5] =
{
"zhangsan",
"lisi",
"wangwu",
"zhaoliu",
"qiangqi"
};
for (int i = 0; i < 5; i++)
{
char* str = strArr2[i];
printf("%s\n", str);
}
return 0;
}
4.字符串的常见函数
4.1常见函数
函数 | 作用 |
---|---|
strlen | 获取字符串的长度 |
strcat | 拼接两个字符串 |
strcpy | 复制字符串 |
strcmp | 比较两个字符串 |
strlwr | 将字符串变成小写 |
strupr | 将字符串变成大写 |
#include<stdio.h>
#include<string.h>
int main()
{
char* str1 = "abc";//底层会把字符数组放在只读常量区(只能读,不能修改 复用)
char str2[100] = "abc";
char str3[5] = { 'q','w','e','r','\0' };
printf("-----------------------strlen(长度)------------------------\n");
//细节1:strlen这个函数在统计长度的时候,是不计算结束标记的
//细节2:在windows中,默认情况下,一个中文占两个字节
int len1 = strlen(str1);//3
int len2 = strlen(str2);//3
int len3 = strlen(str3);//4
printf("%d\n", len1);
printf("%d\n", len2);
printf("%d\n", len3);
printf("----------------------strcat(拼接)------------------------\n");
//细节1:把第二个字符串中全部的内容拷贝到第一个字符串的末尾
//前提1:第一个字符串是可以被修改的
//前提2:第一个字符串中剩余的空间可以容纳拼接的字符串
strcat(str2, str3);
printf("%s\n", str2);//abcqwer
printf("%s\n", str3);//qwer
printf("----------------------strcpy(拷贝)------------------------\n");
//细节:把第二个字符串中全部的内容,拷贝到第一个字符串中,把第一个字符串里面的内容里面原有的内容给覆盖了
//前提1:第一个字符串是可以被修改的
//前提2:第一个字符串中的空间可以容纳第二个字符串的完整内容
strcpy(str2, str3);
printf("%s\n", str2);//qwer
printf("%s\n", str3);//qwer
printf("----------------------strcmp(比较)------------------------\n");
//细节:在比较的时候,有要求顺序和内容完全一致,才叫做字符串一样
//完全一样:0
//只要有一个不一样:非0
int res = strcmp(str1, str2);
printf("%d\n", res);
printf("----------------------strlwr(变小写)------------------------\n");
//细节:只能转换英文的大小写,不能修改中文的大小写
strlwr(str2);
printf("%s\n", str2);
printf("----------------------strupr(变大写)------------------------\n");
strupr(str2);
printf("%s\n", str2);
return 0;
}
5.练习1:用户登录
需求:已知正确的用户名和密码,请用程序实现模拟用户登录。
总共给三次机会,登录之后,给出相应的提示
#include<stdio.h>
#include<string.h>
int main()
{
char* rightUsername = "zhangsan";
char* rightPassword = "qwerqwer";
for(int i=0;i<3;i++)
{
printf("请输入用户名\n");
char username[100];
scanf("%s", username);
printf("请输入密码\n");
char password[100];
scanf("%s", password);
if (!strcmp(username, rightUsername) && (!strcmp(password, rightPassword)))
{
printf("登陆成功\n");
break;
}
else
{
if (i == 3)
{
printf("用户%s被锁定,请联系管理员",username);
}
else
{
printf("登陆失败,还剩下%d次机会\n", 3 - i);
}
}
}
return 0;
}
6.练习2:统计次数
键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数(不考虑其他字符)
#include<stdio.h>
#include<string.h>
int main()
{
printf("请输入一个字符串\n");
char str[100];
scanf("%s", str);
int bigCount = 0;
int smallCount = 0;
int numberCount = 0;
for (int i = 0; i < strlen(str); i++)
{
char c = str[i];
if (c >= 'a' && c <= 'z')
{
smallCount++;
}
else if(c >= 'A' && c <= 'Z')
{
bigCount++;
}
else if (c >= '0' && c <= '9')
{
numberCount++;
}
}
printf("大写字符出现了%d次\n", bigCount);
printf("小写字符出现了%d次\n", smallCount);
printf("数字字符出现了%d次\n", numberCount);
return 0;
}
三、结构体
1.结构体
1.1定义:
结构体可以理解为自定义的数据类型
他是由一批数据组合而成的结构性数据
里面的每一个数据都是结构体的“成员”
结构体作为函数参数,在函数中可以传递结构体
1.2传递的两种情况:
传递结构体中的数据值
传递结构体的地址值
1.3书写的位置:
函数的里面:局部位置,只能在本函数中使用
函数的外面:全局位置,在所有的函数中都可以使用
#include<stdio.h>
#include<string.h>
struct GirlFriend
{
char name[100];
int age;
char gender;
double height;
};
int main()
{
struct GirlFriend gf1;
strcpy(gf1.name, "小诗诗");
gf1.age = 23;
gf1.gender = 'F';
gf1.height= 1.63;
printf("我女朋友的名字为:%s\n", gf1.name);
printf("我女朋友的年龄为:%d\n", gf1.age);
printf("我女朋友的性别为:%c\n", gf1.gender);
printf("我女朋友的身高为:%lf\n", gf1.height);
struct GirlFriend gf2;
strcpy(gf2.name, "小丹丹");
gf2.age = 24;
gf2.gender = 'F';
gf2.height = 1.62;
printf("我女朋友的名字为:%s\n", gf2.name);
printf("我女朋友的年龄为:%d\n", gf2.age);
printf("我女朋友的性别为:%c\n", gf2.gender);
printf("我女朋友的身高为:%lf\n", gf2.height);
return 0;
}
2.结构体数组
定义一个结构体表示学生
学生的属性有:姓名、年龄
要求:把三个学生信息放入到数组当中,并遍历数组
crtl+A再ctrl+K和ctrl+F----->格式化
3.起别民
定义一个结构体表示游戏人物
属性有:姓名、攻击力、防御力、血量
要求:把三个游戏人物放入到数组当中,并遍历数组
#include<stdio.h>
#include<string.h>
typedef struct Ultraman
{
char name[100];
int attack;
int defense;
int blood;
}M;
int main()
{
M taro = { "泰罗",100,90,500 };
M rem = { "雷欧",90,80,450 };
M eddie = { "艾迪",120,70,600 };
M arr[3] = { taro,rem,eddie };
for (int i = 0; i < 3; i++)
{
M temp = arr[i];
printf("奥特曼的名字为%s,攻击力是%d,防御力是%d,血量是%d\n", temp.name, temp.attack, temp.defense,temp.blood);
}
return 0;
}
4.结构体作为函数的参数进行传递
定义一个结构体表示学生
学生的属性:姓名,年龄
要求:定义一个函数,修改学生中的数据
#include<stdio.h>
#include<string.h>
typedef struct Student
{
char name[100];
int age;
}S;
//细节: 因为这个函数用到了结构体,所以函数的申明必须写在结构体的下面,否则代码会报错
void method(S st);
void method2(S* p);
int main()
{
S stu;
strcpy(stu.name, "aaa");
stu.age = 0;
printf("学生的初始数据为: %s, %d\n", stu.name, stu.age);
//调用函数修改学生中的数据
method(stu);
printf("学生的信息修改为: %s, %d\n", stu.name, stu.age);
}
void method(S st)
{
printf("接收到 main函数中学生的初始数据为: %s, %d\n", st.name, st.age);
//修改
printf("请输入要修改的学生名字\n");
scanf("%s", st.name);
printf("请输入要修改的学生年龄\n");
scanf("%d", &(st.age));
printf("在method函数中修改之后,学生的信息为: %s, %d\n", st.name, st.age);
}
细节:
如果函数中写的是结构体类型的变量, 相当于是定义了一个新的变量
此时是把 main函数中 stu中的数据,传递给了 method函数,并把 stu中的数据赋值给了新的变量st
我们在函数中,仅仅是修改了变量 st中的值,对 main函数中stu的值,是没有进行修改的
#include<stdio.h>
#include<string.h>
typedef struct Student
{
char name[100];
int age;
}S;
//细节: 因为这个函数用到了结构体,所以函数的申明必须写在结构体的下面,否则代码会报错
void method(S st);
void method2(S* p);
int main()
{
S stu;
strcpy(stu.name, "aaa");
stu.age = 0;
printf("学生的初始数据为: %s, %d\n", stu.name, stu.age);
//调用函数修改学生中的数据
method2(&stu);
printf("学生的信息修改为: %s, %d\n", stu.name, stu.age);
}
void method2(S* p)
{
printf("接收到 main函数中学生的初始数据为: %s, %d\n", (*p).name, (*p).age);// aaa 0
//修改
printf("请输入要修改的学生名字\n");
scanf("%s", (*p).name);
printf("请输入要修改的学生年龄\n");
scanf("%d", &((*p).age));
printf("在method函数中修改之后, 学生的信息为: %s, %d\n", (*p).name, (*p).age);// zhangsan 23
}
如果要在函数中修改 stu的值,此时就不要再定义新的变量了
直接接收 stu的内存地址, 通过内存地址就可以修改 stu中的数据了
指针p里面记录的是 main函数中 stu的内存地址 ( stu 学生)
5.结构体嵌套
定义一个结构体表示学生 Student
Student成员如下 :
名字、年龄、性别、身高、联系方式
联系方式 Message也是一个结构体, 成员如下:
手机号、电子邮箱
#include<stdio.h>
#include<string.h>
struct Message
{
char phone[12];
char mail[100];
};
struct Student
{
char name[100];
int age;
char gender;
double height;
struct Message msg;
};
int main()
{
struct Student stu;
//给里面的每一个成员进行赋值
strcpy(stu.name, "zhangsan");
stu.age = 23;
stu.gender = 'M';
stu.height = 1.78;
strcpy(stu.msg.phone, "13112345678");
strcpy(stu.msg.mail, "12345678@qq");
//输出打印
printf("学生的信息为: \n");
printf("姓名为: %s\n", stu.name);
printf("年龄为: %d\n", stu.age);
printf("性别为: %c\n", stu.gender);
printf("身高为: % lf\n", stu.height);
printf("手机号为: %s\n", stu.msg.phone);
printf("邮箱为: %s\n", stu.msg.mail);
printf("-------------------------------\n");
//批量进行赋值
struct Student stu2 = { "lisi",24,'F',1.65,{"13112347890","5678@qq"} };
//输出打印
printf("学生的信息为: \n");
printf("姓名为: %s\n", stu2.name);
printf("年龄为: %d\n", stu2.age);
printf("性别为: %c\n", stu2.gender);
printf("身高为: % lf\n", stu2.height);
printf("手机号为: %s\n", stu2.msg.phone);
printf("邮箱为: %s\n", stu2.msg.mail);
return 0;
}
6.综合练习:投票选举
某班级组织野外郊游,想要在ABCD四个景点选择其中一个。
现在班上有80名同学进行投票,找出投票数最多的景点
Ps:
1.学生投票,用随机数模拟
2.如果多个景点投票一样的话,A优先B,B优先于C,C优先于D
#include<stdio.h>
#include<time.h>
#include<string.h>
struct spot
{
char name[100];
int count;
};
int main()
{
// 1.定义数组存储4个spot类型的变量
struct spot arr[4] = { {"A",0}, {"B",0}, {"C",0}, {"D",0} };
//2.模拟80名同学的投票
srand(time(NULL));
for (int i = 0; i < 80; i++)
{
// choose变量有两个含义
//含义一:表示用户的投票 0 A 1 B 2 C 3 D
//含义二:表示arr中的索引,通过这个索引就可以获取到景点的名字和投票数量
int choose = rand() % 4; // 0 1 2 3
//choose:表示同学的投票, 同时也表示数组中的索引
// arr[ choose]: 表示获取景点的信息 (名字, 数量)
// arr[ choose]. count: 表示这个景点已经投了多少票
// arr[ choose]. count++: 给这个景点再投一票
arr[choose].count++;
}
//找最大值
int max = arr[0].count;
for (int i = 1; i < 4; i++)
{
struct spot temp = arr[i];
if (temp.count > max)
{
max = temp.count;
}
}
//遍历数组,看谁的票数刚好是最大值
for (int i = 0; i < 4; i++)
{
struct spot temp = arr[i];
if (temp.count == max)
{
printf("投票最多的景点为: %s, 共计: %d张票\n", temp.name, temp.count);
break;
}
}
//遍历
for (int i = 0; i < 4; i++)
{
struct spot temp = arr[i];
printf("%s %d\n", temp.name, temp.count);
}
return 0;
}
7.内存对齐
确定变量位置:只能放在自己类型整数倍的内存地址上
最后一个补位:结构体的总大小。是组大类型的整数倍
内存对齐:
不管是结构体, 还是普通的变量都存在内存对齐
规则:
只能放在自己类型整数倍的内存地址上
简单理解:
内存地址/占用字节 = 结果可以整除
举例:
int存放的位置:内存地址一定能被4整除
long long存放的位置:内存地址一定能被8整除
double存放的位置:内存地址一定能被8整除
结构体的内存对齐:
结构体在上面的基础上又多了一条,结构体的总大小,是最大类型的整数倍(用来确定最后一个数据补位的情况)
Tips:
对齐的时候会补空白字节,但是不会改变原本字节的大小,char补位之后,本身还是1个字节
心得:把小的数据类型放在最上面,大的数据类型放在最下面(节约空间)
四、共用(同)体/联合体
1.共用体
核心:一种数据可能有多个数据
需求:
金融项目中,钱有可能是整数,小数,字符串,请定义对应的共同体
#include<stdio.h>
#include<string.h>
union MoneyType
{
int moneyi;
double moneyd;
char moneystr[100];
};
int main()
{
//利用共同体定义钱的变量
union MoneyType money;
// 整数 moneyi、小数 moneyd、字符串 moneystr
//而且每次只能赋一个值
// money. moneyi = 99999;
// money. moneyd = 123.32;
strcpy(money.moneystr, "100万");
//上面赋值给哪个类型,下面就从哪个类型取出来
// printf("%d\n", money. moneyi);
// printf("%1f\n", money. moneyd);
printf("%s\n", money.moneystr);
return 0;
}
2.共用体的特点
- 特点:
1.共用体,也叫联合体,共同体
2.所有的变量都使用同一个内存空间
3.所占的内存大小=最大成员的长度 (也受内存对齐影响)
细节:怎么存就怎么取出来
4.每次只能给一个变量进行赋值, 因为第二次赋值时会覆盖原有的数据
细节:以最大的单个成员的长度为准
总大小一定是最大单个成员的整数倍
#include<stdio.h>
#include<string.h>
union MoneyType
{
int moneyi;
double moneyd;
char moneystr[100];
};
int main()
{
//利用共同体定义钱的变量
union MoneyType money;
//获取内存地址
printf("%p\n", &(money.moneyi));
printf("%p\n", &(money.moneyd));
printf("%p\n", &(money.moneystr));
printf("%zu\n", sizeof(money.moneyi));// 4
printf("%zu\n", sizeof(money.moneyd));// 8
printf("%zu\n", sizeof(money.moneystr));// 100
printf("%zu\n", sizeof(money));// 104 (后面会补4个空白字节)
money.moneyi = 99;
money.moneyd = 1.23;
printf("%lf\n", money.moneyd);
return 0;
}
3.结构体和共用体的区别
-
区别:
结构体:一种事物中包含多个属性
共用体:一个属性有多个类型 -
存储方式:
结构体:各存各的
共用体:存一起,多次存会覆盖 -
内存占用:
结构体:各个变量的总和 (受内存对齐影响)
共用体:最大类型 (受内存对齐影响)
五、动态内存分配
1.常用函数
函数名 | 全名 | 作用 |
---|---|---|
malloc | memory allocation | 申请空间(连续) |
calloc | contiguous allocation | 申请空间+数据初始化 |
realloc | re-allocation | 修改空间大小 |
free | free | 释放空间 |
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 1.利用 malloc 函数申请一片连续的空间
// 需求:申请一片空间,要存储 10 个 int 类型的整数
// 返回这片空间的首地址
int* p = (int*)malloc(10 * sizeof(int));
// int* p = calloc(10, sizeof(int));
// printf("%p\n", p);
// 2.赋值
for (int i = 0; i < 10; i++)
{
// 第一种赋值
// *(p + i) = (i + 1) * 10; //10 20 30 40 50 60 70 80 90 100
// 第二种赋值
p[i] = (i + 1) * 10;
// p[i] - - - > p + i
}
// 4.扩容,20 个 int 类型的整数
int* pp = (int*)realloc(p, 20 * sizeof(int));
// 3.遍历
for (int i = 0; i < 20; i++)
{
// printf("%d", *(p + i));
printf("%d ", p[i]);
}
//5.释放空间
//如果申请的空间不需要再继续使用,那么记得一定要释放
free(pp);
return 0;
}
2.malloc函数的细节点
- malloc
1.malloc创建空间的单位是字节
2.malloc返回的是void类型的指针,没有步长的概念,也无法获取空间中的数据,需要强转
3.malloc返回的仅仅是首地址,没有总大小,最好定义一个变量记录总大小
4.malloc申请的空间不会自动消失,如果不能正确释放, 会导致内存泄露
5.malloc申请的空间过多,会产生虚拟内存
6.malloc申请的空间没有初始化值,需要先赋值才能使用 - free
7.free释放完空间之后,空间中数据叫做脏数据,可能被清空,可能被修改为其他值 - calloc
8.calloc就是在malloc的基础上多一个初始化的动作 - realloc
9.realloc修改之后的空间,地址值有可能发生变化, 也有可能不会改变,但是原本的数据不会丢失
10.realloc修改之后,无需释放原来的空间,函数底层会进行处理
#include <stdio.h>
#include <stdlib.h>
void method(int* p, int size);
int main()
{
// int*: 指针的步长
//p: 首地址
int* p = (int*)malloc(25 * sizeof(int));
int size = 25;
method(p, 25);
free(p);
return 0;
}
void method(int* p, int size)
{
for (int i = 0; i < size; i++)
{
printf("%d", p[i]);
}
printf("\n");
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
//malloc申请的空间过多, 会产生虚拟内存
//表示单次申请空间的字节大小(1G)
int number = 1024 * 1024 * 1024;
//利用循环不断地申请空间
// malloc 申请空间 (连续)
//如果申请空间成功,返回这个空间的首地址
//如果申请空间失败,返回NULL
int count = 0;
while (1)
{
int* p = (int*)malloc(number);
count++;
if (p == NULL)
{
printf("申请失败");
break;
}
printf("内存%d申请成功%p\n", count, p);
}
return 0;
}
发布者:admin,转转请注明出处:http://www.yc00.com/web/1754766300a5199592.html
评论列表(0条)