这种地址分配和使用方法是否可以调用未定义的行为?

时间:2017-09-07 19:23:04

标签: c undefined-behavior

这最初发布在 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这里使用的方式可能会调用未定义的行为。如果是,怎么样?

2 个答案:

答案 0 :(得分:2)

此特定代码(使用您正在使用的特定数据)不会调用未定义的行为。但是,对数据的一些轻微更改将调用未定义的行为,因此您的代码非常非常危险。

如果“substring”不是“string”的子字符串,或者它是一个子字符串,并且它的第一次出现在“string”结尾之前是80字节或更多,则会发生未定义的行为。

顺便说一句。 strncpy(buf,target,len); buf [len] = 0;将与strcpy(buf,target)完全相同,因为len == strlen(target)。一般来说,使用strncpy是一个等待发生的事故。

答案 1 :(得分:2)

问题中显示的代码是'OK'(带有轻微咬紧的牙齿),如果要写的是从第一次出现子字符串开始到通用'副本的大纲'有多个问题字符串的结尾是另一个字符串'function。

  • 全局变量是不受欢迎的。
  • 缺乏单独的功能是不可取的。

更详细:

  • 代码不检查源字符串中是否找到子字符串;它应该,因为代码将取消引用空指针。
  • 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字符首先被复制到不与对象重叠的n个字符的临时数组中由s1s2指向,然后临时数组中的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]