这篇文章最初发表在Code Review上,有人认为堆栈溢出更适合。。。
在寻找搜索和捕获子字符串的方法时,我编写了以下代码:
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的方法可能会调用未定义的行为。如果是,怎么做?
最佳答案
问题中显示的代码是“好的”(咬紧牙关),但如果它是一个通用的“从子字符串的第一次出现开始到字符串的结尾复制到另一个字符串”函数的大纲,则有多个问题。
全局变量是不可取的。
缺少单独的功能是不可取的。
更详细地说:
代码不会检查子字符串是否在源字符串中找到;它应该是,因为代码将取消对空指针的引用。strncpy()
测量子字符串的长度,假设有一个子字符串,然后故意不将空终止符复制到buf
中,并故意忽略buf
的大小,尽管它在strncpy()
之后显式地空终止复制的数据。在显示代码的情况下,这无关紧要;字符串足够短并且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);
memmove
函数将n
指向的对象中的s2
字符复制到s1
指向的对象中。复制时,好像首先将
n
指向的对象中的s2
字符复制到一个不与n
和s1
指向的对象重叠的s2
字符的临时数组中,然后将临时数组中的n
字符复制到s1
指向的对象中。除非没有可靠的方法来发现两个数组之间的重叠,否则,
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]
关于c - 这种地址分配和使用方法可以调用未定义的行为吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46103705/