我有此函数,返回一个如下所示的parsed_url结构

typedef struct url_parser_url {
    char *protocol;
    char *host;
    int port;
    char *path;
    char *query_string;
    int host_exists;
} url_parser_url_t;

url_parser_url_t *parsed_url;
parsed_url = (url_parser_url_t *) malloc(sizeof(url_parser_url_t));
parse_url(address, true, parsed_url);
printf("parsed_url->path = %s\n", parsed_url->path);


parse_url函数看起来像

int parse_url(char *url, bool verify_host, url_parser_url_t *parsed_url) {
    char *local_url = (char *) malloc(sizeof(char) * (strlen(url) + 1));
    char *token;
    char *token_host;
    char *host_port;
    char *token_ptr;
    char *host_token_ptr;

    char *path = NULL;
    strcpy(local_url, url);

    token = strtok_r(local_url, ":", &token_ptr);
    parsed_url->protocol = (char *) malloc(sizeof(char) * strlen(token) + 1);
    strcpy(parsed_url->protocol, token);

    token = strtok_r(NULL, "/", &token_ptr);
    if (token) {
        host_port = (char *) malloc(sizeof(char) * (strlen(token) + 1));
        strcpy(host_port, token);
    } else {
        host_port = (char *) malloc(sizeof(char) * 1);
        strcpy(host_port, "");
    }

    token_host = strtok_r(host_port, ":", &host_token_ptr);
    if (token_host) {
        parsed_url->host = (char *) malloc(
                sizeof(char) * strlen(token_host) + 1);
        strcpy(parsed_url->host, token_host);

        if (verify_host) {
            struct hostent *host;
            host = gethostbyname(parsed_url->host);
            if (host != NULL) {
                parsed_url->host_exists = 1;
            } else {
                parsed_url->host_exists = 0;
            }
        } else {
            parsed_url->host_exists = -1;
        }
    } else {
        parsed_url->host_exists = -1;
        parsed_url->host = NULL;
    }

    token_host = strtok_r(NULL, ":", &host_token_ptr);
    if (token_host)
        parsed_url->port = atoi(token_host);
    else
        parsed_url->port = 0;

    token_host = strtok_r(NULL, ":", &host_token_ptr);
    assert(token_host == NULL);

    token = strtok_r(NULL, "?", &token_ptr);
    parsed_url->path = NULL;
    if (token) {
        path = (char *) realloc(path, sizeof(char) * (strlen(token) + 2));
        strcpy(path, "/");
        strcat(path, token);
        parsed_url->path = (char *) malloc(sizeof(char) * strlen(path) + 1);
        strncpy(parsed_url->path, path, strlen(path));
        free(path);
    } else {
        parsed_url->path = (char *) malloc(sizeof(char) * 2);
        strcpy(parsed_url->path, "/");
    }

    token = strtok_r(NULL, "?", &token_ptr);
    if (token) {
        parsed_url->query_string = (char *) malloc(
                sizeof(char) * (strlen(token) + 1));
        strncpy(parsed_url->query_string, token, strlen(token));
    } else {
        parsed_url->query_string = NULL;
    }

    token = strtok_r(NULL, "?", &token_ptr);
    assert(token == NULL);

    free(local_url);
    free(host_port);
    return 0;
}


问题是当我调用函数parse_url,然后我使用parsed_url-> path成员时,它引发了这种分段错误

==16647== Conditional jump or move depends on uninitialised value(s)


谁能解释我发生了什么事,为什么?谢谢

最佳答案

在那里。尽管它被认为是strcpy()的更安全替代品,但strncpy()有疣。该标准说:


  strncpy函数从s2指向的数组复制到s1指向的数组最多复制n个字符(不复制跟随空字符的字符)。


C2011 7.24.2.4/2),并且注释308阐明了


  因此,如果s2指向的数组的前n个字符中没有空字符,则结果将不会以空字符结尾。


最终引起您特定的valgrind投诉的代码是:

    strncpy(parsed_url->path, path, strlen(path));


由于根据定义,在strlen(path)的前path个字符中不能有空字符,因此该strncpy()可靠地不能确保副本以空字符结尾。您的代码中至少还有一个相同问题的其他实例。

由于您似乎已经足够小心以确保有足够的可用空间,因此一种解决方案是将strncpy()切换为strcpy()。这样也将更加有效,因为您可以避免重复调用strlen()

但是,正如我在评论中指出的那样,如果您愿意依赖POSIX的strdup(),则它比strlen() + malloc() + str[n]cpy()干净,并且具有相同的语义(您负责释放内存)分配给副本)。如果您以这种方式制作副本,您甚至没有机会犯这些错误。

关于c - 条件跳转或移动取决于C中的未初始化值,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43079929/

10-08 22:09
查看更多