C语言malloc()的一个问题——堆越界的一个错误。

本文详细探讨了如何正确使用malloc分配内存及free释放内存的方法,通过实例演示了字符串复制过程中的堆溢出问题,并提供了避免此类错误的有效策略。

malloc函数分配得到的空间是存储在堆区的。

系统推出后是不会被释放的,必须由程序员自己用free释放。

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <malloc.h> 
int main()
{
	int address;
char *q;
	char *p=(char *)malloc(sizeof(char)*10);
q=strcpy(p,"chenhuijie is a good boy");

	printf("p:%s(%p),q:%s(%p)\n",p,p,q,q);

	address=(int)p;

	printf("int:%p",address);

	printf("the string is %s\n",(char *)address);

	free(p);

	

	//p++;


}

在调试时,单步调试至free(p)时。发生如下错误。


原来是发生了堆越界了。

	char *p=(char *)malloc(sizeof(char)*10);
q=strcpy(p,"chenhuijie is a good boy");

分配的堆空间为10个byte。多出来的字符被放置在10之后的内存中。一不小心发生了越界。才出现了这个堆被破坏的错误。


为了提高健壮性,应改为如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <malloc.h> 

#define m_len 30
int main()
{
	int address;
	
    char *q="chenhuijie is a good boy";
	char *p=(char *)malloc(sizeof(char)*m_len);


	assert((strlen(q)+1)<m_len);

    q=strcpy(p,q);

	printf("p:%s(%p),q:%s(%p)\n",p,p,q,q);

	address=(int)p;

	printf("int:%p",address);

	printf("the string is %s\n",(char *)address);

	free(p);

	

	//p++;


}

主要是增加了长度检查的功能。为什么
strlen(q)+1)
这样呢?是因为strlen()返回的是字符串不含结尾符的字符个数。
<think> 我们参考用户提供的多个引用,总结C语言删除数组中某一元素的方法。 核心思想:通常需要遍历数组,找到目标元素,然后通过移动后续元素覆盖该元素,最后调整数组长度(或标记新长度)。 根据引用内容,有以下几种情况: 1. 删除一个特定元素(可能重复,但只删除一次或所有重复元素?) 2. 删除指定位置的元素(按索引) 3. 删除重复元素(去重) 4. 删除所有0元素(特定值) 用户的问题是:删除数组中某一个元素(可理解为删除第一个匹配的元素,或者删除指定位置元素?) 引用[1]:删除数组中第一个匹配的元素(如果重复,可能删除多次,但通过i--处理了连续重复的情况),并在末尾补0(因为数组大小固定,所以用0填充尾部空位)。 引用[2]:删除指定索引位置的元素,并返回一个新数组(但该实现有严重问题:返回了局部数组的地址,这是错误的)。 引用[3]:描述删除指定元素的过程(与引用[1]类似)。 引用[4]:删除重复元素(去重),并调整数组长度(但数组大小固定,实际通过变量标记新长度)。 引用[5]:删除所有0元素,并返回新长度(元素前移)。 针对用户需求,我们分两种情况: 情况一:删除数组中第一个匹配的元素(按值删除第一个匹配项) 情况二:删除数组中指定位置的元素(按索引) 注意:在C语言中,数组大小是固定的,无法真正“删除”元素,只能通过移动元素覆盖,并跟踪新的有效长度。 下面分别给出两种情况的代码示例和说明。 情况一:按值删除第一个匹配的元素 思路: 1. 遍历数组,找到第一个等于目标值的元素,记录其位置(索引)。 2. 从该位置开始,将后续元素依次前移一位(覆盖要删除的元素)。 3. 数组有效长度减1,末尾元素可以置0(可选,取决于需求)。 注意:如果要求只删除第一个匹配的元素,那么找到第一个后就可以停止。 情况二:按索引删除 1. 检查索引是否在有效范围内(0到len-1)。 2. 从索引位置开始,将后续元素依次前移一位。 3. 数组有效长度减1。 另外,如果要求删除所有匹配的元素(比如所有的某个值),则需要在遍历过程中每当遇到目标值就进行移动,并调整索引(因为移动后当前位置的元素已经改变,需要重新检查)。 根据引用[1]提供的代码,它尝试删除所有匹配的元素(通过i--来重新检查当前位置的新元素),并且在删除后末尾补0。但是用户的问题没有明确是否删除所有重复值。 我们假设用户只需要删除第一个匹配的元素(按值)或者按索引删除。 下面给出两个示例: 示例1:按值删除第一个匹配的元素(只删第一个) 示例2:按索引删除指定位置的元素 注意:由于数组大小固定,我们无法改变数组的实际大小,因此我们通过一个变量(如newLen)来记录删除后的有效长度。 另外,如果用户要求返回新数组,那么需要动态分配内存(如引用[2]尝试做的,但引用[2]返回了栈上数组的地址,这是错误的)。因此,如果要求返回新数组,应该动态分配。但用户的问题中没有明确要求,我们按照最常见的做法:在原始数组上操作,并返回新的有效长度。 下面给出删除第一个匹配元素的代码(按值): */ </think> ### C语言删除数组中元素的实现方法 在C语言中,删除数组元素的本质是**移动后续元素覆盖目标位置**,因为数组大小在定义时已固定。根据您的需求,我整理出两种常用方法: --- #### 方法1:直接修改原数组(无需返回新数组) **适用场景**:只需修改原数组,末尾空位补0 **核心思想**: 1. 遍历数组查找目标元素 2. 发现目标后,将其后所有元素前移一位 3. 末尾位置补0 4. 通过`i--`解决连续重复值问题 ```c #include <stdio.h> int main() { int arr[10] = {5, 3, 7, 2, 7, 9}; // 示例数组 int target, i, j; printf("输入要删除的元素: "); scanf("%d", &target); int len = 6; // 初始有效长度 for (i = 0; i < len; i++) { if (arr[i] == target) { for (j = i; j < len - 1; j++) { // 后续元素前移 arr[j] = arr[j + 1]; } arr[len - 1] = 0; // 末尾补0 len--; // 有效长度减1 i--; // 关键:防止跳过连续重复元素 } } printf("删除后数组: "); for (i = 0; i < 10; i++) printf("%d ", arr[i]); return 0; } ``` **执行示例** ``` 原始数组: 5 3 7 2 7 9 输入要删除的元素: 7 结果数组: 5 3 2 9 0 0 0 0 0 0 ``` --- #### 方法2:构造新数组(不修改原数组) **适用场景**:需保留原数组,返回新数组指针 **核心思想**: 1. 动态分配新数组内存 2. 跳过目标元素复制其他元素 3. 返回新数组指针 ```c #include <stdio.h> #include <stdlib.h> int* deleteElement(int* arr, int target, int len, int* newLen) { int count = 0; // 计算目标元素出现次数 for (int i = 0; i < len; i++) { if (arr[i] == target) count++; } *newLen = len - count; int* newArr = malloc((*newLen) * sizeof(int)); // 复制非目标元素 for (int i = 0, j = 0; i < len; i++) { if (arr[i] != target) { newArr[j++] = arr[i]; } } return newArr; } int main() { int arr[] = {1, 4, 6, 4, 8}; int len = 5; int target = 4; int newLen; int* result = deleteElement(arr, target, len, &newLen); printf("新数组: "); for (int i = 0; i < newLen; i++) printf("%d ", result[i]); // 输出: 1 6 8 free(result); // 释放动态内存 return 0; } ``` --- ### 关键注意事项 1. **内存管理** - 方法2需手动释放内存,避免泄漏 - 局部数组不可返回(如引用[2]的错误) 2. **时间复杂度** - 方法1:最坏情况$O(n^2)$(需移动所有后续元素) - 方法2:两次遍历$O(n)$,但需额外空间 3. **边界处理** - 检查数组越界 - 处理空数组或无效输入 > “在C语言中操作数组时,始终牢记数组长度不可变,删除操作本质是元素的覆盖和位移。” —— 计算机程序设计基础[^3] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值