这最初发布于 Code Review ,有人建议 Stack Overflow 更适合......
在寻找一种方法来搜索和捕获子字符串时,我编写了以下代码:
char string[] = {"this is the source string to search"};
char substring[] = {"string to"};
char *target;
char buf[80];
int main(void)
{
target = strstr(string, substring);
int len = strlen(target);
strncpy(buf, target, len);
buf[len]=0;
return 0;
}
我的问题: 可以按照此处使用的方式使用指针 char *target 可能会调用未定义的行为。如果是,如何?
最佳答案
问题中显示的代码写的是“OK”(略微咬紧牙关),但如果它是一个通用的“从子字符串第一次出现的开始复制到字符串的末尾转换为另一个字符串的函数。
- 全局变量是不可取的。
- 缺少单独的功能是不可取的。
更详细:
- 代码不检查是否在源字符串中找到子字符串;它应该,因为否则代码将取消引用空指针。
strncpy()
测量子串的长度,假设有一个,然后故意不将空终止符复制到buf
中,并故意忽略大小buf
,尽管它在strncpy()
之后明确地以 null 终止复制的数据。使用显示的代码,这无关紧要;字符串足够短并且buf
足够大(并且buf
是一个零初始化的全局变量——尽管询问为什么target
是有效的和buf
不是局部变量)。- 一般来说,这样的编码很草率并且容易出现缓冲区溢出。
- 您在 CR 上收到的关于使用
memcpy()
的评论是有效的。您也可以使用len + 1
来复制空字节,从而保存显式赋值,但是您仍然应该限制自己复制的数据不超过buf
的容量。 - 您还应该考虑如果字符串太长而无法放入
buf
中是否适合截断,或者您是否真的应该完全拒绝该操作。 - 尽管在所示代码中没有风险,但我通常建议使用
memmove()
而不是memcpy()
,因为无论源区和目标区重叠。如果您不确定没有重叠,请使用memmove()
。
请注意,memmove()
的 C 标准指定(强调):
void *memmove(void *s1, const void *s2, size_t n);
The
memmove
function copiesn
characters from the object pointed to bys2
into the object pointed to bys1
. Copying takes place as if then
characters from the object pointed to bys2
are first copied into a temporary array ofn
characters that does not overlap the objects pointed to bys1
ands2
, and then then
characters from the temporary array are copied into the object pointed to bys1
.
memmove()
的正常实现不会像标准建议的那样将数据复制到中间数组中,除非没有可靠的方法来发现两个数组之间的重叠,这种情况很少见。实现者了解机器的能力,几乎总能避免重复复制。
下面是一个可能的代码实现,如果复制的字符串太大而无法放入目标缓冲区,则会将其截断。可以实现其他行为以满足应用程序设计者的突发奇想。
#include <assert.h>
#include <stdio.h>
#include <string.h>
static void copy_substr(const char *source, const char *substr, char *buffer, size_t buflen)
{
assert(source != 0 && substr != 0 && substr[0] != '\0' && buffer != 0 && buflen != 0);
size_t length = 0;
const char *target = strstr(source, substr);
if (target != 0)
{
length = strlen(target);
if (length >= buflen)
length = buflen - 1; // length = 0 would be an option too
memmove(buffer, target, length);
}
buffer[length] = '\0'; // target might not be null terminated within length
}
int main(void)
{
char string[] = {"this is the source string to search"};
char substr[] = {"string to"};
char buffer[80];
copy_substr(string, substr, buffer, sizeof(buffer));
printf("Main string: [%s]\n", string);
printf("Substring: [%s]\n", substr);
printf("Tail string: [%s]\n", buffer);
return 0;
}
输出:
Main string: [this is the source string to search]
Substring: [string to]
Tail string: [string to search]
关于这种地址分配和使用方法会调用未定义的行为吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46103705/