【问题标题】:Conditional jump or move depends on uninitialised value(s) in C条件跳转或移动取决于 C 中未初始化的值
【发布时间】:2017-03-28 21:05:11
【问题描述】:

我有这个函数,它返回一个看起来像这样的 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)

谁能解释一下发生了什么以及为什么?谢谢

【问题讨论】:

  • 错误信息很清楚。获取调试器并在代码中找到位置。
  • 这不是分段错误,但如果你有一个,那么它可能会解释原因。该消息似乎是 valgrind 输出的摘录。
  • 如果您在启用调试信息的情况下进行编译,那么 valgrind 还应该为您提供有关代码中错误发生位置的详细信息。我还建议关闭优化以使此类信息尽可能有用。
  • 我怀疑host_port = (char *) malloc(sizeof(char) * 1);
  • @JohnBollinger 在这一行它说问题是: **parsed_url->path = (char *) malloc(sizeof(char) * strlen(path) + 1); ** 但我之前用 null 初始化了路径

标签: c pointers memory-leaks segmentation-fault malloc


【解决方案1】:

就在那里。尽管它应该是strcpy() 的更安全替代品,但strncpy() 有一个讨厌的疣。标准说:

strncpy 函数从 s2 指向的数组复制不超过 n 个字符(不复制空字符后的字符)到 s1 指向的数组。

(C2011 7.24.2.4/2),注释 308 阐明了这一点

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

最终对您的特定 valgrind 投诉负责的代码如下:

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-14
    • 1970-01-01
    • 1970-01-01
    • 2013-05-18
    • 2011-08-18
    相关资源
    最近更新 更多