一、静态开辟内存
#include
#include // 小写的
// 函数进栈 定义一个int arr[5]; 定义一个 int i; (静态的范畴)
// 进栈
void staticAction() {
int arr[5]; // 静态开辟 栈区 (栈成员)
for (int i = 0; i <5; ++i) {
arr[i] = i;
printf("%d, %p\n", *(arr + i), arr + i);
}
} // 函数的末尾会弹栈(隐士):执行完毕会弹栈 会释放所有的栈成员
// 静态开辟。栈区
int main() {
// int arr[10 * 1024 * 1024]; // 10M * 4 = 40M 会栈溢出
// int arr[1 * 1024 * 1024]; 会栈溢出
int arr[(int)(0.2 * 1024 * 1024)]; // 不会栈溢出
// 栈区:占用内存大小 最大值: 大概 2M 大于2M会栈溢出 平台有关系的
// 堆区:占用内存大小 最大值: 大概80% 40M没有任何问题,基本上不用担心 堆区很大的
// 大概80%: Windows系统 给我们的编译器给予的空间 的 百分之百八十
while (9) {
sleep(100);
staticAction(); // 调用开辟20
}
return (0);
}
二、动态开辟
1、malloc()
#include
#include
#include
void dynamicAction() {
int *arr = malloc(1 * 1024 * 1024); //堆区开辟
//没释放前arr的内存地址不会变,堆区开辟的每次都会新开辟一个,释放后,堆区的内存会复用
printf("arr自己的内存地址:%p,堆区开辟的内存地址:%p\n", &arr, arr);
free(arr); //堆区开辟的空间,必须释放
//悬空指针是指向的地址被释放掉了 没有置为 NULL,置为NULL重新指向
arr = NULL; //重新指向一块内存地址
printf("dynamicAction函数2 堆区开辟的内存地址:%p\n", arr);
//开发过程中,不能出现,野指针,悬空指针
// int * p; //野指针 没有地址的
int * p = NULL;
}
// malloc 在堆区开辟的内存空间 , (动态的范畴)
int main() {
while (9) {
sleep(100);
dynamicAction();
}
return 0;
}
2、使用场景
//动态开辟的场景
int main() {
// 静态开辟的内存空间大小,是不能修改的,如果不需要动态修改空间大小,
// 当然使用 栈区 【尽量使用 静态开辟的,如果实在是需要动态改变,才使用下面】
int num;
printf("请输入数的个数:");
scanf("%d", &num);
int *arr = malloc(sizeof(int) * num);
int print_num;
for (int i = 0; i < num; ++i) {
printf("请输入第%d个的值:", i);
scanf("%d", &print_num);
arr[i] = print_num;
printf("当前元素的值:%d,地址:%p", *(arr + i), arr + i);
}
return 0;
}
3、realloc()
#include
#include
// 动态开辟之realloc
int main() {
int num;
printf("请输入个数");
// 获取用户输入的值
scanf("%d", &num);
// 5个值
int * arr = (int *) malloc(sizeof(int) * num);
for (int i = 0; i < num; ++i) {
arr[i] = (i + 10001); // arr[i]的内部隐式 == *(arr+i)
}
printf("开辟的内存指针: %p\n", arr);
// 打印 内容
for (int i = 0; i < num; ++i) {
printf("元素的值:%d, 元素的地址:%p\n",*(arr + i),(arr + i));
}
// ====== 在堆区开辟新的空间 加长空间大小
// 新增
int new_num;
printf("请输入新增加的个数");
scanf("%d", &new_num);
// 原来的大小4 + 新增加的大小4 = 总大小 8
// void *realloc (void *前面开辟的指针, size_t总大小);
int * new_arr = (int *) realloc(arr, sizeof(int) * (num + new_num));
if (new_arr) { // new_arr != NULL 我才进if 【非0即true】
int j = num; // 4开始
for (; j < (num + new_num); j++) { // 5 6 7 8
arr[j] = (j + 10001);
}
printf("新 开辟的内存指针: %p\n", new_arr);
// 后 打印 内容
for (int i = 0; i < (num + new_num); ++i) {
printf("新 元素的值:%d, 元素的地址:%p\n",*(arr + i),(arr + i) );
}
}
// 我已经释放
free(new_arr);
new_arr = NULL;
// 重复释放/重复free VS会奔溃, CLion会优化(发现不奔溃) [错误的写法]
/*free(new_arr);
new_arr = NULL;*/
// 必须释放【规则】
/*if (arr) {
free(arr); // 如果不赋值给NULL,就是悬空指针了
arr = NULL;
}*/
if (new_arr) {
free(new_arr); // 如果不赋值给NULL,就是悬空指针了
new_arr = NULL;
}
return 0;
}
// 面试题: realloc 为什么一定要传入 arr指针,为什么要传总大小
//第一个参数arr指针是为了寻找前面开辟的空间的首地址,总大小是要开辟的总空间,是为了防止开辟的时候空间被占用;如图,一般情况下,新开辟都是正常的;如果新开辟的被占用了,则会重新开辟一块原来大小的空间,将原来的复制过来,然后在这后面接着开辟空间,相当于是正常开辟,首地址不变,重新开辟的话首地址会发生改变。
三、字符串
1、使用
int main() {
//第一种
// char str[] = {'Z', 'h', 'a', 'n', 'g'}; //Zhtng0{/
char str[] = {'Z', 'h', 'a', 'n', 'g','\0'}; //Zhtng printf打印的时候,必须以\0结尾
str[2] = 't';
printf("str:%s",str);
//第二种
char *str2 = "Zhang"; //隐式的加了\0
str2[2] = 't'; //不能这修改,会奔溃
printf("str2:%s",str2);
return 0;
}
如图所示,第一种方式是将字符串复制到栈区进行修改;第二种是字符串地址指向全局或者静态区,不能访问。
2、获取长度
int getLength(char *string) {
int count = 0;
while (*string) { // *string != '\0'
string++;
count++;
}
return count;
}
int main() {
char str[] = {'a', 'b', 'c', 'd'};
int length = getLength(str);
printf("str长度:%d", length);
return 0;
}
3、字符串转换、比较
#include
#include
#include
int main() {
// 字符串转换
char * num = "1"; // 字符串
num = "12.68";
// 【int】
int result = atoi(num);
if (result) { // 非0即ture 不是0进入if, 0就是转换失败了
printf("恭喜你转换成功:%d\n", result);
} else {
printf("转换失败!\n");
}
// 【double】
double resultD = atof(num);
printf("恭喜你转换成功:%lf\n", resultD);
// 字符串的比较
char * str1 = "Yefan";
char * str2 = "yefan";
// int resultC = strcmp(str1, str2); // strcmp = 区分大小写
int resultC = strcmpi(str1, str2); // strcmpi = 不区分大小写
if (!resultC) { // 0代表是相等的, 非0代表是不相等的
printf("相等");
} else {
printf("不相等");
}
return 0;
}
4、字符串查找、包含、拼接
#include
#include
#include
nt mainT5() {
char * text = "name is Derry";
char * subtext = "D";
char * pop = strstr(text, subtext);
// 怎么去 字符串查找
if (pop) { // 非NULL,就进入if,就查找到了
printf("查找到了,pop的值是:%s\n", pop);
} else {
printf("没有查找到,subtext的值是:%s\n", subtext);
}
// 包含了D吗
if (pop) {
printf("包含了\n");
} else {
printf("没有包含\n");
}
// printf("pop地址%p, text地址:%p,\n", pop, text);
// 求取位置? 数组是一块连续的内存空间,没有断层,所以可以-
int index = pop - text; // pop="Derry" - text"name is Derry"
printf("%s第一次出现的位置是:%d\n", subtext, index); // 我的D在第8个位置
// 指针是可以:++ -- += -=
// 拼接 ========================
char destination[25]; // 容器 25的大小 已经写死了
char * blank = "--到--", *CPP="C++", *Java= "Java";
strcpy(destination, CPP); // 先Copy到数组里面去
strcat(destination, blank); // 然后再拼接
strcat(destination, Java); // 然后再拼接
printf("拼接后的结果:%s\n", destination); // C++--到--Java
return 0;
}
5、大小写转换
#include
#include
// 指针的理解
void lower(char * dest, char * name) {
char * temp = name; // 临时指针,你只能操作,临时指针,不能破坏name指针
while (*temp) {
*dest = tolower(*temp);
temp ++; // 挪动指针位置 ++
dest ++; // 挪动指针位置 ++ 目的是为了 挪动一个存储一个 挪动一个存储一个 ...
}
// printf '\0'
*dest = '\0'; // 避免printf打印系统值
printf("不能破坏 name:%s\n", name); // temp的好处就是,不会破坏name
}
// 全部变成小写 derry
int mainT6() {
char * name = "DerrY";
// 先定义结果
char dest[20];
lower(dest, name);
printf("小写转换后的结构是:%s\n", dest);
return 0;
}