#c #concatenation #c-strings #function-definition
#c #конкатенация #c-строки #функция-определение
Вопрос:
У меня есть функция, которая принимает строку из макроса. Она изменяет строку, а затем возвращает новую измененную строку. Я намерен использовать эту возвращаемую строку. Однако она не работает, поскольку не возвращает измененную строку.
#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("%sn",str);
return str;
};
int main()
{
printf("%sn",decrypt()); //output: *** stack smashing detected ***: ./a.out terminated
return 0;
}
Комментарии:
1. Вопрос: Каков размер
str
? Какую длину строки она может удерживать?2. достаточно большой , чтобы вместить «hello_decrypted.txt » строка
3. Как вы думаете, почему это именно так?
4. Я увеличил размер, теперь char str[40]=ENCRYPTED_FILE; все еще ошибка
Ответ №1:
Для начала функция возвращает указатель на первый элемент локального массива 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] = '';
strcat( s n, decrypted_file );
return s;
}
int main(void)
{
char *s = decrypt();
puts( s );
free( s );
return 0;
}
Вывод программы
hello_decrypted.txt
Ответ №2:
Вы возвращаете указатель на временный массив, который становится зависшим после того, как элемент управления покидает функцию. Сначала вам нужно будет выделить его в куче, через malloc
, а затем убедиться, что его выделенный размер достаточен, чтобы содержать исходный размер плюс дополнительный суффикс «_decrypted».
Ответ №3:
Я только что заметил вашу библиотечную нотацию, поскольку #include "string.h"
вместо #include <string.h>
этого это может решить проблему.
Ответ №4:
ИМХО, вы плохо используете strtok()
функцию. Она будет разбивать строку на подстроки каждый раз, когда находит точку .
, a t
или an x
. Поскольку вы написали код, я боюсь, что это не то, что вы хотите (чтобы исключить .txt
суффикс?)
Прочтите страницу руководства strtok()
, поскольку там будет точно объяснено, что на самом деле делает эта функция.
С другой стороны, вы не можете обрезать строку в начале .txt
, а затем добавить к ней более длинную строку. Когда вы объявили str[]
массив (явно не используя длину), компилятор зарезервировал столько символов для хранения текста, исходящего из макроса, плюс еще один для хранения
разделителя. Таким образом, в вашем массиве есть место только для хранения 10 символов (9 из "hello.txt"
плюс один для ''
конца строки). Конечно, там нет места для хранения hello_decripted.txt
, для которого потребовалось бы 19 символов плюс еще один для
. Обходным решением этой проблемы может быть указание в объявлении массива, сколько символов вы хотите использовать компилятору, например:
char str[100] = ENCRYPTED_FILE;
и тогда вы можете расширить его до 100 символов (99 плюс держатель для символа конца строки
).
Если вы найдете искомую строку ( .txt
) и поставите a
в ее первую позицию, вы усечете исходную строку и сможете делать то, что вы на самом деле хотите, то есть:
#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 = ''; /* string truncated */
}
strcat(str,"_decrypted.txt"); /* add new suffix */
//printf("%sn",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("%sn", 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 = ''; /* string truncated */
}
/* from this point on, everything goes the same */
strcat(str,"_decrypted.txt"); /* add new suffix */
//printf("%sn",str);
return strdup(str);
};
в этом случае вам нужно выполнить только одно сравнение строк (которое выполняется несколько раз в теле strstr()
для поиска полного совпадения), и вы узнаете, не удалось или нет, быстро и эффективно.
Примечание
Последнее замечание о #include "string.h"
строке в вашем коде: включение файла с двойными кавычками вместо пары <>
символов допустимо, если у вас есть локальный файл (в вашем локальном каталоге), который вызывается так же, как некоторый файл библиотеки, потому что это позволит найти его раньше, чем файл системной библиотеки. Но это плохая привычка, если вы включаете стандартный библиотечный файл include, потому что, если вы позже решите создать включаемый файл (в другом модуле или программе) и создадите локальный string.h
файл, эта программа внезапно начнет компилироваться с ошибками, и вы не догадаетесь, почему. Будьте осторожны с #include
именами и двумя способами их вызова. Файлы с именами, <file.h>
которые обычно являются стандартной библиотекой, включают файлы и ищутся в фиксированных местах в системе. Файлы с именем as "file.h"
сначала ищутся в рабочем каталоге, и если они не найдены, то они ищутся в библиотеке фиксированных путей. Попробуйте использовать "
только для ваших файлов или файлов, которые у вас есть в каталоге сборки, и искать системные файлы только с <
помощью amp; >
.