Функция, которая изменяет строку и возвращает измененную строку

У меня есть функция, которая берет строку из макроса. Он изменяет строку, а затем возвращает новую измененную строку. Я намерен использовать эту возвращаемую строку. Однако это не работает, так как не возвращает измененную строку.

#include <stdio.h>
#include "string.h"

#define ENCRYPTED_FILE  "hello.txt"

char *decrypt(){
    char str[]=ENCRYPTED_FILE;
    strtok(str,".txt");
    strcat(str,"_decrypted.txt");
    //printf("%s\n",str);
    return str;
};
   
int main()
{        
    printf("%s\n",decrypt());     //output: *** stack smashing detected ***: ./a.out terminated 
    return 0;
}

person Kvd Ledki    schedule 02.01.2021    source источник
comment
В: Каков размер str? Как долго струна может держаться?   -  person Mat    schedule 02.01.2021
comment
достаточно большой, чтобы вместить строку hello_decrypted.txt   -  person Kvd Ledki    schedule 02.01.2021
comment
Как вы думаете, почему именно так?   -  person Mat    schedule 02.01.2021
comment
Теперь я увеличил размер char str[40]=ENCRYPTED_FILE; все еще ошибка   -  person Kvd Ledki    schedule 02.01.2021


Ответы (4)


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

В результате функция возвращает неверный указатель.

Вам нужно выделить память для массива динамически.

Также этот звонок strtok

strtok(str,".txt");

не имеет смысла. Функция не ищет подстроку ".txt". Он ищет первый символ набора символов, заданного строкой ".txt". Вместо этого вы можете использовать функцию strstr.

И этот код strcat

 strcat(str,"_decrypted.txt");

вызывает неопределенное поведение, поскольку в целевом массиве недостаточно места для хранения добавленного строкового литерала.

Функция может выглядеть, например, так, как показано в демонстрационной программе ниже.

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define ENCRYPTED_FILE  "hello.txt"

char *decrypt( void )
{
    const char *encrypted_file = ENCRYPTED_FILE;
    
    const char *p = strstr( encrypted_file, ".txt" );
    
    if ( p == NULL ) p = encrypted_file + strlen( encrypted_file );
    size_t n = p - encrypted_file;
    
    const char *decrypted_file = "_decrypted.txt";
    
    char *s = malloc( n + strlen( decrypted_file ) + 1 );
    
    memmove( s, encrypted_file, n );
    s[n] = '\0';

    strcat( s + n, decrypted_file );

    return s;
}

int main(void) 
{
    char *s = decrypt();
    
    puts( s );
    
    free( s );
    
    return 0;
}

Вывод программы

hello_decrypted.txt
person Vlad from Moscow    schedule 02.01.2021

Вы возвращаете указатель на временный массив, который становится висящим после того, как управление покидает функцию. Во-первых, вам нужно выделить его в куче через malloc, а затем убедиться, что его выделенного размера достаточно, чтобы вместить исходный размер плюс дополнительный суффикс _decrypted.

person bipll    schedule 02.01.2021

Я только что заметил, что ваша библиотека обозначена как #include "string.h" вместо #include <string.h>, что может решить проблему.

person Onat Güzelsin    schedule 02.01.2021

ИМХО, вы плохо используете функцию strtok(). Он будет разбивать строку на подстроки каждый раз, когда находит точку ., t или x. Поскольку вы написали код, я боюсь, что это не то, что вы хотите (чтобы исключить суффикс .txt?)

Прочтите справочную страницу strtok(), так как там объясняется, что на самом деле делает эта функция.

С другой стороны, вы не можете обрезать строку в начале .txt, а затем добавить к ней более длинную строку. Когда вы объявили массив str[] (явно не используя длину), компилятор зарезервировал столько символов для хранения текста, поступающего из макроса, плюс еще один для хранения разделителя \0. Таким образом, в вашем массиве есть место только для 10 символов (9 из "hello.txt" плюс один для конца строки '\0'). Конечно, там нет места для хранения hello_decripted.txt, для которого потребовалось бы 19 символов плюс еще один для \0. Обходной путь для этой проблемы может состоять в том, чтобы указать в объявлении массива, сколько символов вы хотите, чтобы компилятор использовал, например:

    char str[100] = ENCRYPTED_FILE;

а затем вы можете расширить до 100 символов (99 плюс держатель для конца строки char \0).

Если вы найдете искомую строку (.txt) и поместите \0 в ее первую позицию, вы обрежете исходную строку и сможете делать то, что на самом деле хотите, а именно:

#include <stdio.h>
#include <stdlib.h>
#include "string.h"  /* is this what you actually mean and not <string.h>? */

#define ENCRYPTED_FILE  "hello.txt"

char *decrypt(){
    char str[100]=ENCRYPTED_FILE;
    char *p = strstr(str,".txt");
    if (p != NULL) {  /* the string actually has a .txt suffix */
        *p = '\0'; /* string truncated */
    }
    strcat(str,"_decrypted.txt"); /* add new suffix */
    //printf("%s\n",str);
    /* you cannot return str as str is a local variable,
     * and it will cease to exist as soon as we leave this
     * function body, better return a new dynamically
     * allocated string (that need to be freed with free(3)
     */
    return strdup(str);
};
   
int main()
{        
    /* the stack smashing probably is due to returning the
     * address of a local variable, that ceased to exist.
     */
    char *name = decrypt();
    printf("%s\n", name);
    free(name); /* return the memory allocated in decrypt() */
    return 0;
}

Это решит проблему с соблюдением ваших намерений. Но вы ошибаетесь в другом:

Что делать, если строка .txt появляется непосредственно перед концом исходного имени? На мой взгляд, то, что вы ищете, это суффикс .txt (то, что ранее было известно как расширение). Что мешает вашему файлу назвать что-то вроде blahblah.txt01.txt? --который имеет два вхождения подстроки .txt--) Это неправильный алгоритм для поиска суффикса .txt. Правильный способ — искать, если .txt находится в конце строки, и для этого используется другой алгоритм (и гораздо более эффективный):

char *decrypt(){
    char str[100]=ENCRYPTED_FILE;
    char *suff = ".txt";
    /* go to the point that is strlen(str) further than
     * the beginning of the string minus the string
     * of the suffix */
    char *p = str + strlen(str) - strlen(suff);
    if (strcmp(p, suff) == 0) {  /* the string actually has a .txt suffix */
        *p = '\0'; /* string truncated */
    }
    /* from this point on, everything goes the same */
    strcat(str,"_decrypted.txt"); /* add new suffix */
    //printf("%s\n",str);
    return strdup(str);
};

в этом случае вам нужно выполнить только одно сравнение строк (которое выполняется несколько раз в теле strstr() для поиска полного совпадения), и вы быстро и эффективно узнаете, сработает оно или нет.

Примечание

Последнее замечание о строке #include "string.h" в вашем коде: включение файла с двойными кавычками вместо пары символов <> допустимо, если у вас есть локальный файл (в вашем локальном каталоге), который называется так же, как какой-либо библиотечный файл, потому что это заставит его быть найденным перед системной библиотекой. Но это плохая привычка, если вы включаете включаемый файл стандартной библиотеки, потому что, если вы позже решите создать включаемый файл (в другом модуле или программе) и создать локальный string.h файл, эта программа вдруг начнет компилироваться с ошибками, и вы не угадаю почему. Будьте осторожны с #include именами и двумя способами их вызова. Файлы с именем <file.h> обычно являются стандартными файлами включения библиотек и ищутся в фиксированных местах в системе. Файлы с именем "file.h" сначала ищутся в рабочем каталоге, а если не найдены, то ищутся в фиксированных путях библиотеки. Попробуйте использовать " только для ваших файлов или файлов, которые есть в вашем каталоге сборки, и ищите системные файлы только с < и >.

person Luis Colorado    schedule 03.01.2021