一、靜態(tài)開辟內(nèi)存
#include <stdio.h>
#include <unistd.h> // 小寫的
// 函數(shù)進(jìn)棧 定義一個int arr[5]; 定義一個 int i; (靜態(tài)的范疇)
// 進(jìn)棧
void staticAction() {
int arr[5]; // 靜態(tài)開辟 棧區(qū) (棧成員)
for (int i = 0; i <5; ++i) {
arr[i] = i;
printf("%d, %p\n", *(arr + i), arr + i);
}
} // 函數(shù)的末尾會彈棧(隱士):執(zhí)行完畢會彈棧 會釋放所有的棧成員
// 靜態(tài)開辟。棧區(qū)
int main() {
// int arr[10 * 1024 * 1024]; // 10M * 4 = 40M 會棧溢出
// int arr[1 * 1024 * 1024]; 會棧溢出
int arr[(int)(0.2 * 1024 * 1024)]; // 不會棧溢出
// 棧區(qū):占用內(nèi)存大小 最大值: 大概 2M 大于2M會棧溢出 平臺有關(guān)系的
// 堆區(qū):占用內(nèi)存大小 最大值: 大概80% 40M沒有任何問題,基本上不用擔(dān)心 堆區(qū)很大的
// 大概80%: Windows系統(tǒng) 給我們的編譯器給予的空間 的 百分之百八十
while (9) {
sleep(100);
staticAction(); // 調(diào)用開辟20
}
return (0);
}
二、動態(tài)開辟
1、malloc()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void dynamicAction() {
int *arr = malloc(1 * 1024 * 1024); //堆區(qū)開辟
//沒釋放前arr的內(nèi)存地址不會變,堆區(qū)開辟的每次都會新開辟一個,釋放后,堆區(qū)的內(nèi)存會復(fù)用
printf("arr自己的內(nèi)存地址:%p,堆區(qū)開辟的內(nèi)存地址:%p\n", &arr, arr);
free(arr); //堆區(qū)開辟的空間,必須釋放
//懸空指針是指向的地址被釋放掉了 沒有置為 NULL,置為NULL重新指向
arr = NULL; //重新指向一塊內(nèi)存地址
printf("dynamicAction函數(shù)2 堆區(qū)開辟的內(nèi)存地址:%p\n", arr);
//開發(fā)過程中,不能出現(xiàn),野指針,懸空指針
// int * p; //野指針 沒有地址的
int * p = NULL;
}
// malloc 在堆區(qū)開辟的內(nèi)存空間 , (動態(tài)的范疇)
int main() {
while (9) {
sleep(100);
dynamicAction();
}
return 0;
}

動態(tài)開辟內(nèi)存.png
2、使用場景
//動態(tài)開辟的場景
int main() {
// 靜態(tài)開辟的內(nèi)存空間大小,是不能修改的,如果不需要動態(tài)修改空間大小,
// 當(dāng)然使用 棧區(qū) 【盡量使用 靜態(tài)開辟的,如果實在是需要動態(tài)改變,才使用下面】
int num;
printf("請輸入數(shù)的個數(shù):");
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āng)前元素的值:%d,地址:%p", *(arr + i), arr + i);
}
return 0;
}
3、realloc()
#include <stdio.h>
#include <stdlib.h>
// 動態(tài)開辟之realloc
int main() {
int num;
printf("請輸入個數(shù)");
// 獲取用戶輸入的值
scanf("%d", &num);
// 5個值
int * arr = (int *) malloc(sizeof(int) * num);
for (int i = 0; i < num; ++i) {
arr[i] = (i + 10001); // arr[i]的內(nèi)部隱式 == *(arr+i)
}
printf("開辟的內(nèi)存指針: %p\n", arr);
// 打印 內(nèi)容
for (int i = 0; i < num; ++i) {
printf("元素的值:%d, 元素的地址:%p\n",*(arr + i),(arr + i));
}
// ====== 在堆區(qū)開辟新的空間 加長空間大小
// 新增
int new_num;
printf("請輸入新增加的個數(shù)");
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 我才進(jìn)if 【非0即true】
int j = num; // 4開始
for (; j < (num + new_num); j++) { // 5 6 7 8
arr[j] = (j + 10001);
}
printf("新 開辟的內(nèi)存指針: %p\n", new_arr);
// 后 打印 內(nèi)容
for (int i = 0; i < (num + new_num); ++i) {
printf("新 元素的值:%d, 元素的地址:%p\n",*(arr + i),(arr + i) );
}
}
// 我已經(jīng)釋放
free(new_arr);
new_arr = NULL;
// 重復(fù)釋放/重復(fù)free VS會奔潰, CLion會優(yōu)化(發(fā)現(xiàn)不奔潰) [錯誤的寫法]
/*free(new_arr);
new_arr = NULL;*/
// 必須釋放【規(guī)則】
/*if (arr) {
free(arr); // 如果不賦值給NULL,就是懸空指針了
arr = NULL;
}*/
if (new_arr) {
free(new_arr); // 如果不賦值給NULL,就是懸空指針了
new_arr = NULL;
}
return 0;
}
// 面試題: realloc 為什么一定要傳入 arr指針,為什么要傳總大小
//第一個參數(shù)arr指針是為了尋找前面開辟的空間的首地址,總大小是要開辟的總空間,是為了防止開辟的時候空間被占用;如圖,一般情況下,新開辟都是正常的;如果新開辟的被占用了,則會重新開辟一塊原來大小的空間,將原來的復(fù)制過來,然后在這后面接著開辟空間,相當(dāng)于是正常開辟,首地址不變,重新開辟的話首地址會發(fā)生改變。

動態(tài)realloc.png
三、字符串
1、使用
int main() {
//第一種
// char str[] = {'Z', 'h', 'a', 'n', 'g'}; //Zhtng0{/
char str[] = {'Z', 'h', 'a', 'n', 'g','\0'}; //Zhtng printf打印的時候,必須以\0結(jié)尾
str[2] = 't';
printf("str:%s",str);
//第二種
char *str2 = "Zhang"; //隱式的加了\0
str2[2] = 't'; //不能這修改,會奔潰
printf("str2:%s",str2);
return 0;
}
如圖所示,第一種方式是將字符串復(fù)制到棧區(qū)進(jìn)行修改;第二種是字符串地址指向全局或者靜態(tài)區(qū),不能訪問。

字符串01.png
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、字符串轉(zhuǎn)換、比較
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// 字符串轉(zhuǎn)換
char * num = "1"; // 字符串
num = "12.68";
// 【int】
int result = atoi(num);
if (result) { // 非0即ture 不是0進(jìn)入if, 0就是轉(zhuǎn)換失敗了
printf("恭喜你轉(zhuǎn)換成功:%d\n", result);
} else {
printf("轉(zhuǎn)換失敗!\n");
}
// 【double】
double resultD = atof(num);
printf("恭喜你轉(zhuǎn)換成功:%lf\n", resultD);
// 字符串的比較
char * str1 = "Yefan";
char * str2 = "yefan";
// int resultC = strcmp(str1, str2); // strcmp = 區(qū)分大小寫
int resultC = strcmpi(str1, str2); // strcmpi = 不區(qū)分大小寫
if (!resultC) { // 0代表是相等的, 非0代表是不相等的
printf("相等");
} else {
printf("不相等");
}
return 0;
}
4、字符串查找、包含、拼接
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
nt mainT5() {
char * text = "name is Derry";
char * subtext = "D";
char * pop = strstr(text, subtext);
// 怎么去 字符串查找
if (pop) { // 非NULL,就進(jìn)入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);
// 求取位置? 數(shù)組是一塊連續(xù)的內(nèi)存空間,沒有斷層,所以可以-
int index = pop - text; // pop="Derry" - text"name is Derry"
printf("%s第一次出現(xiàn)的位置是:%d\n", subtext, index); // 我的D在第8個位置
// 指針是可以:++ -- += -=
// 拼接 ========================
char destination[25]; // 容器 25的大小 已經(jīng)寫死了
char * blank = "--到--", *CPP="C++", *Java= "Java";
strcpy(destination, CPP); // 先Copy到數(shù)組里面去
strcat(destination, blank); // 然后再拼接
strcat(destination, Java); // 然后再拼接
printf("拼接后的結(jié)果:%s\n", destination); // C++--到--Java
return 0;
}
5、大小寫轉(zhuǎn)換
#include <stdio.h>
#include <ctype.h>
// 指針的理解
void lower(char * dest, char * name) {
char * temp = name; // 臨時指針,你只能操作,臨時指針,不能破壞name指針
while (*temp) {
*dest = tolower(*temp);
temp ++; // 挪動指針位置 ++
dest ++; // 挪動指針位置 ++ 目的是為了 挪動一個存儲一個 挪動一個存儲一個 ...
}
// printf '\0'
*dest = '\0'; // 避免printf打印系統(tǒng)值
printf("不能破壞 name:%s\n", name); // temp的好處就是,不會破壞name
}
// 全部變成小寫 derry
int mainT6() {
char * name = "DerrY";
// 先定義結(jié)果
char dest[20];
lower(dest, name);
printf("小寫轉(zhuǎn)換后的結(jié)構(gòu)是:%s\n", dest);
return 0;
}