C語(yǔ)言學(xué)習(xí)1-數(shù)組、字符串
C語(yǔ)言學(xué)習(xí)2-指針的使用、const 指針
C語(yǔ)言學(xué)習(xí)3-指針和數(shù)組
C語(yǔ)言學(xué)習(xí)4-結(jié)構(gòu)體
C語(yǔ)言學(xué)習(xí)5-函數(shù)
一、為什么需要?jiǎng)討B(tài)內(nèi)存分配?
1.1 靜態(tài)數(shù)組的局限性
#include <stdio.h>
// 問(wèn)題:數(shù)組大小必須在編譯時(shí)確定
void static_array_limitations() {
// ? 可以:編譯時(shí)常量
int fixed_array[100]; // 固定100個(gè)元素
// ? 錯(cuò)誤:運(yùn)行時(shí)變量作為數(shù)組大小
int n;
printf("請(qǐng)輸入需要的數(shù)組大小: ");
scanf("%d", &n);
// int dynamic_array[n]; // 編譯錯(cuò)誤!C89標(biāo)準(zhǔn)不支持
}
1.2 動(dòng)態(tài)內(nèi)存分配的優(yōu)勢(shì)
- 按需分配:程序運(yùn)行時(shí)決定需要多少內(nèi)存
- 靈活調(diào)整:可以隨時(shí)申請(qǐng)和釋放
- 避免浪費(fèi):不預(yù)先分配過(guò)多內(nèi)存
二、malloc 和 free 基礎(chǔ)
2.1 內(nèi)存管理器(MM)概念
系統(tǒng)中有一個(gè)內(nèi)存管理器負(fù)責(zé)管理閑置內(nèi)存:
- 應(yīng)用程序通過(guò)
malloc借出內(nèi)存 - 使用完后通過(guò)
free歸還內(nèi)存 - 所有程序共享同一個(gè)內(nèi)存池(堆)
2.2 malloc 函數(shù)詳解
#include <stdio.h>
#include <stdlib.h>
void malloc_basic_usage() {
// 申請(qǐng) 1024 字節(jié)內(nèi)存
void* raw_ptr = malloc(1024);
// 轉(zhuǎn)換為具體類型
char* char_ptr = (char*)malloc(100); // 100字節(jié)字符數(shù)組
int* int_ptr = (int*)malloc(10 * sizeof(int)); // 10個(gè)整數(shù)的數(shù)組
double* double_ptr = (double*)malloc(5 * sizeof(double)); // 5個(gè)雙精度數(shù)組
printf("字符指針: %p\n", (void*)char_ptr);
printf("整型指針: %p\n", (void*)int_ptr);
printf("雙精度指針: %p\n", (void*)double_ptr);
// 記得釋放!
free(raw_ptr);
free(char_ptr);
free(int_ptr);
free(double_ptr);
}
2.3 free 函數(shù)詳解
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Contact {
int id;
char name[32];
char phone[16];
};
void free_basic_usage() {
// 申請(qǐng)內(nèi)存
int size = 5 * sizeof(struct Contact);
struct Contact* contacts = (struct Contact*)malloc(size);
if (contacts != NULL) {
// 使用內(nèi)存
contacts[0].id = 1;
strcpy(contacts[0].name, "張三");
strcpy(contacts[0].phone, "13800138000");
printf("聯(lián)系人: %s, 電話: %s\n", contacts[0].name, contacts[0].phone);
// 釋放內(nèi)存
free(contacts);
contacts = NULL; // 良好習(xí)慣:釋放后置為NULL
}
}
三、動(dòng)態(tài)內(nèi)存的實(shí)際應(yīng)用
3.1 用戶自定義大小的數(shù)組
#include <stdio.h>
#include <stdlib.h>
void dynamic_array_example() {
int count;
printf("請(qǐng)輸入需要存儲(chǔ)的聯(lián)系人數(shù)量: ");
scanf("%d", &count);
// 動(dòng)態(tài)分配足夠的內(nèi)存
struct Contact* contacts = (struct Contact*)malloc(count * sizeof(struct Contact));
if (contacts == NULL) {
printf("錯(cuò)誤:內(nèi)存分配失?。n");
return;
}
// 輸入數(shù)據(jù)
for (int i = 0; i < count; i++) {
printf("\n請(qǐng)輸入第 %d 個(gè)聯(lián)系人的信息:\n", i + 1);
contacts[i].id = i + 1;
printf("姓名: ");
scanf("%s", contacts[i].name);
printf("電話: ");
scanf("%s", contacts[i].phone);
}
// 顯示數(shù)據(jù)
printf("\n=== 所有聯(lián)系人 ===\n");
for (int i = 0; i < count; i++) {
printf("%d. %s - %s\n", contacts[i].id, contacts[i].name, contacts[i].phone);
}
// 釋放內(nèi)存
free(contacts);
contacts = NULL;
}
3.2 指針數(shù)組的動(dòng)態(tài)分配
#include <stdio.h>
#include <stdlib.h>
void pointer_array_example() {
int array_size = 5;
// 分配指針數(shù)組:包含5個(gè)double指針
double** pointer_array = (double**)malloc(array_size * sizeof(double*));
if (pointer_array == NULL) {
printf("指針數(shù)組分配失?。n");
return;
}
// 為每個(gè)指針?lè)峙鋬?nèi)存
for (int i = 0; i < array_size; i++) {
pointer_array[i] = (double*)malloc(sizeof(double));
if (pointer_array[i] == NULL) {
printf("元素 %d 分配失??!\n", i);
// 釋放已分配的內(nèi)存
for (int j = 0; j < i; j++) {
free(pointer_array[j]);
}
free(pointer_array);
return;
}
*pointer_array[i] = i * 1.5; // 賦值
}
// 使用數(shù)據(jù)
printf("指針數(shù)組內(nèi)容:\n");
for (int i = 0; i < array_size; i++) {
printf("pointer_array[%d] = %.2f\n", i, *pointer_array[i]);
}
// 正確釋放:先釋放元素,再釋放數(shù)組
for (int i = 0; i < array_size; i++) {
free(pointer_array[i]);
pointer_array[i] = NULL;
}
free(pointer_array);
pointer_array = NULL;
}
3.3 跨函數(shù)使用動(dòng)態(tài)內(nèi)存
#include <stdio.h>
#include <stdlib.h>
// 修改動(dòng)態(tài)內(nèi)存內(nèi)容的函數(shù)
void modify_dynamic_memory(int* ptr, int new_value) {
if (ptr != NULL) {
*ptr = new_value;
}
}
// 創(chuàng)建動(dòng)態(tài)內(nèi)存的函數(shù)
int* create_dynamic_integer(int initial_value) {
int* ptr = (int*)malloc(sizeof(int));
if (ptr != NULL) {
*ptr = initial_value;
}
return ptr;
}
// 釋放動(dòng)態(tài)內(nèi)存的函數(shù)
void destroy_dynamic_integer(int* ptr) {
if (ptr != NULL) {
free(ptr);
// 注意:這里不能將外部指針置為NULL,調(diào)用者需要自己處理
}
}
void cross_function_example() {
// 在一個(gè)函數(shù)中創(chuàng)建
int* number = create_dynamic_integer(42);
if (number != NULL) {
printf("初始值: %d\n", *number); // 輸出: 42
// 在另一個(gè)函數(shù)中修改
modify_dynamic_memory(number, 100);
printf("修改后: %d\n", *number); // 輸出: 100
// 在另一個(gè)函數(shù)中釋放
destroy_dynamic_integer(number);
number = NULL; // 調(diào)用者負(fù)責(zé)置為NULL
}
}
四、對(duì)象與動(dòng)態(tài)內(nèi)存
4.1 對(duì)象的概念
在C語(yǔ)言中,對(duì)象指的是一塊內(nèi)存區(qū)域,用于存儲(chǔ)數(shù)據(jù)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 汽車結(jié)構(gòu)體
struct Car {
char maker[32];
int price;
};
// 市民結(jié)構(gòu)體
struct Citizen {
char name[32];
int deposit;
struct Car* car; // 指向Car對(duì)象的指針
};
// 購(gòu)買汽車
void buy_car(struct Citizen* owner) {
if (owner == NULL) return;
// 動(dòng)態(tài)創(chuàng)建Car對(duì)象
struct Car* new_car = (struct Car*)malloc(sizeof(struct Car));
if (new_car == NULL) {
printf("購(gòu)車失?。簝?nèi)存不足!\n");
return;
}
strcpy(new_car->maker, "雪佛蘭");
new_car->price = 100000;
if (owner->deposit >= new_car->price) {
owner->car = new_car;
owner->deposit -= new_car->price;
printf("%s 購(gòu)買了 %s,花費(fèi) %d 元\n", owner->name, new_car->maker, new_car->price);
} else {
printf("%s 存款不足,無(wú)法購(gòu)買!\n", owner->name);
free(new_car); // 立即釋放
}
}
// 報(bào)廢汽車
void discard_car(struct Citizen* owner) {
if (owner != NULL && owner->car != NULL) {
printf("%s 報(bào)廢了 %s\n", owner->name, owner->car->maker);
free(owner->car);
owner->car = NULL;
}
}
// 轉(zhuǎn)讓汽車
void transfer_car(struct Citizen* from, struct Citizen* to) {
if (from == NULL || to == NULL || from->car == NULL) return;
printf("%s 將 %s 轉(zhuǎn)讓給 %s\n", from->name, from->car->maker, to->name);
to->car = from->car;
from->car = NULL; // 原主人不再擁有汽車
}
void object_management_example() {
// 創(chuàng)建市民對(duì)象(棧上)
struct Citizen alice = {"Alice", 200000, NULL};
struct Citizen bob = {"Bob", 50000, NULL};
// Alice 購(gòu)買汽車
buy_car(&alice);
printf("Alice 剩余存款: %d\n", alice.deposit);
// 轉(zhuǎn)讓給 Bob
transfer_car(&alice, &bob);
// Bob 報(bào)廢汽車
discard_car(&bob);
}
五、malloc 和 free 的注意事項(xiàng)
5.1 必須遵守的規(guī)則
#include <stdio.h>
#include <stdlib.h>
void malloc_free_rules() {
// ? 規(guī)則1:只能free通過(guò)malloc分配的內(nèi)存
int* valid_ptr = (int*)malloc(sizeof(int));
free(valid_ptr); // 正確
// ? 錯(cuò)誤:free非malloc的指針
int stack_variable = 10;
int* stack_ptr = &stack_variable;
// free(stack_ptr); // 運(yùn)行時(shí)錯(cuò)誤!
// ? 規(guī)則2:必須free首地址
char* full_block = (char*)malloc(100);
free(full_block); // 正確:釋放整個(gè)塊
// free(full_block + 50); // 錯(cuò)誤:不是首地址
// ? 規(guī)則3:free后立即置為NULL
int* ptr = (int*)malloc(sizeof(int));
free(ptr);
ptr = NULL; // 防止懸空指針
// ? 錯(cuò)誤:使用已free的內(nèi)存
// *ptr = 100; // 未定義行為!
}
5.2 內(nèi)存泄漏檢測(cè)
#include <stdio.h>
#include <stdlib.h>
void memory_leak_example() {
// ? 內(nèi)存泄漏:分配后沒(méi)有釋放
for (int i = 0; i < 10; i++) {
int* leak = (int*)malloc(sizeof(int));
*leak = i;
// 忘記 free(leak)!
}
// ? 正確:及時(shí)釋放
for (int i = 0; i < 10; i++) {
int* proper = (int*)malloc(sizeof(int));
if (proper != NULL) {
*proper = i;
// 使用...
free(proper); // 及時(shí)釋放
proper = NULL;
}
}
}
void infinite_memory_leak() {
// ? 危險(xiǎn):無(wú)限循環(huán)分配內(nèi)存
/*
while (1) {
void* memory = malloc(1024 * 1024); // 每次1MB
if (memory == NULL) {
printf("內(nèi)存耗盡!\n");
break;
}
// 沒(méi)有free!
}
*/
}
5.3 返回值檢查
#include <stdio.h>
#include <stdlib.h>
void safe_malloc_usage() {
// 申請(qǐng)大量?jī)?nèi)存
void* large_block = malloc(1024 * 1024 * 1024); // 1GB
if (large_block == NULL) {
printf("警告:內(nèi)存分配失?。∠到y(tǒng)可能內(nèi)存不足。\n");
// 嘗試分配較小的內(nèi)存
large_block = malloc(1024 * 1024); // 1MB
}
if (large_block != NULL) {
printf("內(nèi)存分配成功!\n");
// 使用內(nèi)存...
free(large_block);
} else {
printf("所有內(nèi)存分配嘗試都失敗了!\n");
}
}
六、內(nèi)存操作函數(shù)
6.1 memset - 內(nèi)存設(shè)置
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void memset_example() {
// 分配并初始化內(nèi)存
int* numbers = (int*)malloc(10 * sizeof(int));
if (numbers != NULL) {
// 將內(nèi)存塊全部設(shè)置為0
memset(numbers, 0, 10 * sizeof(int));
// 驗(yàn)證初始化結(jié)果
for (int i = 0; i < 10; i++) {
printf("numbers[%d] = %d\n", i, numbers[i]); // 全部為0
}
// 設(shè)置為特定值(注意:對(duì)于int類型要小心)
memset(numbers, 0xFF, 10 * sizeof(int)); // 全部設(shè)置為-1
free(numbers);
}
// 字符數(shù)組的memset使用
char* text = (char*)malloc(100);
if (text != NULL) {
memset(text, 'A', 99); // 前99字節(jié)設(shè)置為'A'
text[99] = '\0'; // 最后1字節(jié)設(shè)置為字符串結(jié)束符
printf("文本: %s\n", text);
free(text);
}
}
6.2 memcpy - 內(nèi)存拷貝
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void memcpy_example() {
int source[5] = {1, 2, 3, 4, 5};
int* destination = (int*)malloc(5 * sizeof(int));
if (destination != NULL) {
// 拷貝整個(gè)數(shù)組
memcpy(destination, source, 5 * sizeof(int));
printf("拷貝結(jié)果:\n");
for (int i = 0; i < 5; i++) {
printf("dest[%d] = %d\n", i, destination[i]);
}
free(destination);
}
// 結(jié)構(gòu)體拷貝
struct Point {
int x, y;
};
struct Point p1 = {10, 20};
struct Point* p2 = (struct Point*)malloc(sizeof(struct Point));
if (p2 != NULL) {
memcpy(p2, &p1, sizeof(struct Point));
printf("點(diǎn)坐標(biāo): (%d, %d)\n", p2->x, p2->y);
free(p2);
}
}
6.3 memmove - 安全內(nèi)存移動(dòng)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void memmove_example() {
char buffer[] = "Hello, World!";
printf("移動(dòng)前: %s\n", buffer);
// 重疊內(nèi)存區(qū)域的移動(dòng)
// 將 "Hello" 移動(dòng)到 "World" 的位置
memmove(buffer + 7, buffer, 5);
printf("移動(dòng)后: %s\n", buffer); // 輸出: Hello, Hello!
// 與memcpy的對(duì)比
char data1[] = "ABCDEFGHIJ";
char data2[] = "ABCDEFGHIJ";
// memcpy在重疊時(shí)可能出錯(cuò)
// memcpy(data1 + 2, data1, 5); // 未定義行為
// memmove總是安全的
memmove(data2 + 2, data2, 5);
printf("memmove結(jié)果: %s\n", data2);
}
七、最佳實(shí)踐總結(jié)
7.1 動(dòng)態(tài)內(nèi)存使用原則
#include <stdio.h>
#include <stdlib.h>
// 模板:安全的動(dòng)態(tài)內(nèi)存使用模式
void safe_dynamic_memory_pattern() {
// 1. 聲明指針并初始化為NULL
int* data = NULL;
size_t element_count = 100;
// 2. 分配內(nèi)存并檢查成功
data = (int*)malloc(element_count * sizeof(int));
if (data == NULL) {
printf("錯(cuò)誤:內(nèi)存分配失敗\n");
return; // 早期返回
}
// 3. 使用內(nèi)存前可以初始化
memset(data, 0, element_count * sizeof(int));
// 4. 安全使用內(nèi)存
for (size_t i = 0; i < element_count; i++) {
data[i] = (int)(i * i);
}
// 5. 使用完畢后立即釋放
free(data);
data = NULL; // 防止懸空指針
printf("動(dòng)態(tài)內(nèi)存使用完成\n");
}
7.2 錯(cuò)誤處理模式
#include <stdio.h>
#include <stdlib.h>
// 帶完整錯(cuò)誤處理的動(dòng)態(tài)數(shù)組
int* create_dynamic_array(size_t size, int initial_value) {
if (size == 0) {
printf("錯(cuò)誤:數(shù)組大小不能為0\n");
return NULL;
}
int* array = (int*)malloc(size * sizeof(int));
if (array == NULL) {
printf("錯(cuò)誤:無(wú)法分配 %zu 個(gè)整數(shù)的內(nèi)存\n", size);
return NULL;
}
// 初始化數(shù)組
for (size_t i = 0; i < size; i++) {
array[i] = initial_value;
}
return array;
}
void cleanup_dynamic_array(int* array) {
if (array != NULL) {
free(array);
// 注意:這里不能修改外部指針,調(diào)用者需要自己置NULL
}
}
void demonstrate_best_practices() {
int* numbers = NULL;
numbers = create_dynamic_array(100, 0);
if (numbers == NULL) {
return; // 分配失敗,直接返回
}
// 使用數(shù)組...
for (int i = 0; i < 100; i++) {
numbers[i] = i * 2;
}
// 清理
cleanup_dynamic_array(numbers);
numbers = NULL; // 調(diào)用者負(fù)責(zé)置NULL
}
7.3 核心要點(diǎn)回顧
- 分配前:確定需要的大小,檢查參數(shù)有效性
- 分配時(shí):檢查malloc返回值,處理分配失敗
- 使用時(shí):確保不越界訪問(wèn),及時(shí)處理錯(cuò)誤
- 釋放時(shí):只free malloc返回的地址,釋放后置NULL
- 設(shè)計(jì)時(shí):誰(shuí)分配誰(shuí)釋放,或明確所有權(quán)轉(zhuǎn)移
掌握這些動(dòng)態(tài)內(nèi)存管理技術(shù),你就能編寫出高效、安全的C語(yǔ)言程序!