c - 使用动态分配的内存替换字符串
问题描述
我正在使用以下函数替换给定字符串中的子字符串
void ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *result = NULL;
int i, cnt = 0;
int tolen = strlen(to);
int fromlen = strlen(from);
if (*inputString == NULL)
return;
// Counting the number of times old word
// occur in the string
for (i = 0; (*inputString)[i] != '\0'; i++)
{
if (strstr((&(*inputString)[i]), from) == &(*inputString)[i])
{
cnt++;
// Jumping to index after the old word.
i += fromlen - 1;
}
}
// Making new string of enough length
result = (char *)malloc(i + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return;
memset(result, 0, i + cnt * (tolen - fromlen) + 1);
i = 0;
while (&(*inputString))
{
// compare the substring with the result
if (strstr(*inputString, from) == *inputString)
{
strncpy(&result[i], to, strlen(to));
i += tolen;
*inputString += fromlen;
}
else
{
result[i++] = (*inputString)[0];
if ((*inputString)[1] == '\0')
break;
*inputString += 1;
}
}
result[i] = '\0';
*inputString = result;
return;
}
上述函数的问题是内存泄漏。为 inputString 分配的任何内存都将在此行之后丢失。
*inputString = result;
因为我正在使用 strstr 并且 inputString *inputString += fromlen;
inputString 的移动指针在上述行之前指向 NULL。那么这里如何处理内存泄漏。
注意:我不想返回函数内部分配的新内存。我需要根据新长度更改 inputString 内存。
解决方案
您应该使用局部变量来迭代输入字符串,并避免*inputString
在释放前一个字符串并将其替换为新分配的指针的最后一步之前进行修改。
对于当前的 API,ReplaceSubStr
必须使用指向分配malloc()
或类似块的指针的地址来调用。传递指向本地存储的指针或字符串文字将具有未定义的行为。
以下是一些改进的想法:
您可以返回新字符串并将其留给调用者以释放前一个字符串。在这种情况下,您将按值而不是按地址获取输入字符串:
char *ReplaceSubStr(const char *inputString, const char *from, const char *to);
如果
from
字符串为空,您应该to
在输入字符串的每个字符之间插入字符串,或者什么也不做。如发布的那样,您的代码对于这种边界情况具有未定义的行为。- 要检查
from
字符串是否存在于 offseti
,请使用memcmp
而不是strstr
。 - 如果
cnt
为 0,则无事可做。 - 您应该为调用者返回一个错误状态,以确定是否可以分配内存。
- 无需初始化
result
数组。 - 避免使用
strncpy()
. 此函数具有违反直觉的语义,并且经常被误用。阅读:https ://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
这是一个改进的版本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ReplaceSubStr(char **inputString, const char *from, const char *to) {
char *input = *inputString;
char *p, *q, *result;
size_t cnt;
size_t tolen = strlen(to);
size_t fromlen = strlen(from);
if (input == NULL || fromlen == 0)
return 0;
// Counting the number of times old word occurs in the string
for (cnt = 0, p = input; (p = strstr(p, from)) != NULL; cnt++) {
p += fromlen;
}
if (cnt == 0) // no occurrence, nothing to do.
return 0;
// Making new string of enough length
result = (char *)malloc(strlen(input) + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return -1;
for (p = input, q = result;;) {
char *p0 = p;
p = strstr(p, from);
if (p == NULL) {
strcpy(q, p0);
break;
}
memcpy(q, p0, p - p0);
q += p - p0;
memcpy(q, to, tolen);
q += tolen;
p += fromlen;
}
free(*inputString);
*inputString = result;
return 0;
}
int main() {
char *p = strdup("Hello world!");
ReplaceSubStr(&p, "l", "");
printf("%s\n", p); // prints Heo word!
free(p);
return 0;
}
推荐阅读
- vb.net - 'String' 类型的值不能转换为 'String()'
- r - 在 print.tbl 中使用个人打印方法
- javascript - 如何观看 google dataLayer 对象?
- codenameone - 代号一种具有百分比宽度和自适应高度的自定义布局
- python - Pandas Dataframe groupby:一次应用多个 lambda 函数
- java - NumberFormat.getCurrencyInstance() 不返回语言环境中国和法国的货币符号 (jdk-1.8)
- c# - 用户数据未显示在模态中,但在单击按钮时会打开新选项卡,但第一条记录除外
- java - TokenEnhancer 不仅向 jwt 添加了附加信息,还向响应对象添加了附加信息
- google-cloud-platform - 通过 jenkins 推送 docker 镜像
- django - “WSGIRequest”对象没有属性“thema”