Ошибка C Strcat valgrind

Я пытаюсь объединить две строки, чтобы получить путь к файлу. Однако я получаю сообщение об ошибке в valgrind

Условный переход или перемещение зависит от неинициализированных значений.

Мой код:

/**
 * @brief Concatenate two strings to get file path
 * @param firstP - First string
 * @param secondP - Second string
 * @return Returns the concatenated string
 */
char *getPathDir(char *firstP, char *secondP) {
    char *new_str;
    int stringSize = strlen(firstP)+strlen(secondP)+2;

    if((new_str = malloc(stringSize)) != NULL){
        new_str[0] = '\0';
        strcat(new_str,firstP);
        new_str[strlen(firstP)] = '/';
        strcat(new_str,secondP);
    } else {
        perror("malloc");
        cleanUp();
        exit(EXIT_FAILURE);
    }
    return new_str;
}

person Cows42    schedule 12.10.2017    source источник
comment
sprintf(new_str, "%s/%s", firstP, secondP); вместо. new_str[strlen(firstP)] = '/'; перезаписывает последний нуль-терминатор строки. Таким образом, 2-й strcat не может найти конец правильной строки.   -  person BLUEPIXY    schedule 13.10.2017
comment
Есть гораздо лучший способ сделать это... функция strcat. См.: tutorialspoint.com/c_standard_library/c_function_strcat.htm или предложение @BLUEPIXY :)   -  person Burstfulovic    schedule 13.10.2017
comment
@BLUEPIXY Я удалил все внутри оператора if и заменил его на sprintf(new_str, %s/%s, firstP, secondP); теперь он работает отлично. Спасибо :)   -  person Cows42    schedule 13.10.2017


Ответы (1)


Давайте посмотрим на эти строки:

    new_str[0] = '\0';
    strcat(new_str,firstP);
    new_str[strlen(firstP)] = '/';
    strcat(new_str,secondP);

Перед записью строка выглядит так:

     +---+---+---+---+---+---+---+---+
     | ? | ? | ? | ? | ? | ? | ? | ? |
     +---+---+---+---+---+---+---+---+

После первой строки (new_str[0] = '\0';) у вас есть это:

     +---+---+---+---+---+---+---+---+
     | 0 | ? | ? | ? | ? | ? | ? | ? |
     +---+---+---+---+---+---+---+---+

После второй строки (strcat(new_str,firstP);) это выглядит так:

     +---+---+---+---+---+---+---+---+
     | A | B | C | D | 0 | ? | ? | ? |
     +---+---+---+---+---+---+---+---+

Теперь, когда вы выполняете строку

    new_str[strlen(firstP)] = '/';

вы перезаписываете нулевой терминатор и получаете следующее:

     +---+---+---+---+---+---+---+---+
     | A | B | C | D | / | ? | ? | ? |
     +---+---+---+---+---+---+---+---+

Это проблема, потому что ваша строка больше не завершается нулем, поэтому, когда вы в следующий раз вызовете strcat, программа начнет чтение в неинициализированную память в поисках нулевого терминатора.

Если вы хотите объединить строки вместе, может быть проще просто использовать sprintf, например так:

sprintf(new_str, "%s/%s", firstP, secondP);

Это более явно говорит «напишите первую строку, затем разделитель, затем вторую строку» и переносит все управление нулевым терминатором в библиотеку. И библиотеки, за исключением strncat, обычно довольно хорошо обрабатывают нулевые терминаторы. . :-)

Также есть шанс, что sprintf может быть немного быстрее, чем то, что вы делаете. Использование большого количества strcat подряд таким образом, как вы предлагаете, может быть неэффективным из-за накладные расходы на повторное сканирование строк для поиска завершающих нулей, но я бы не стал на это ставить. Однако у него есть очень явное преимущество, заключающееся в более точном сообщении того, что вы пытаетесь сделать, и выигрыш в удобочитаемости редко бывает плохим.

person templatetypedef    schedule 13.10.2017
comment
Отличный способ представить проблему. - person Barmar; 13.10.2017
comment
Но вы также можете показать простое решение, которое добавляет необходимый нуль или использует strcat(new_str, "/") - person Barmar; 13.10.2017
comment
@Barmar Это правда, но для удобочитаемости я думаю, что sprintf - лучший вариант. Существует также (очень незначительная) неэффективность повторного сканирования строк снова и снова, возникающая из-за использования большого количества strcat в строке. - person templatetypedef; 13.10.2017
comment
Хотя верно то, что sprintf() — это правильный способ сделать это, в образовательных целях может быть полезно показать, что нужно делать под одеялом. - person Barmar; 13.10.2017
comment
@templatetypedef Хорошо объяснил! - person Cows42; 13.10.2017