这篇文章最初发表在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是一个初始化为零的全局变量——尽管可以问为什么targetbuf不是局部变量)。
一般来说,这样的编码是草率的,容易造成缓冲区溢出。
您在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字符复制到一个不与ns1指向的对象重叠的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/

10-09 18:39
查看更多