Я пытался решить проблему возврата самого длинного палиндрома в строке. Сначала я копирую строку в обратном порядке, а затем пытаюсь найти самую длинную подстроку. Вот код, который я написал:
char *longestPalindrome(char *s) {
int len = strlen(s);
char c[len];
for (int i = 0; i < len; i++) {
c[i] = s[len - 1 - i];
}
int st = 0;
int length = 0;
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
int l = 0;
for (int k = 0; ((i + k) < len) && ((j + k) < len); k++) {
if (s[i + k] == c[j + k])
l++;
else
break;
}
if (l > length) {
length = l;
st = i;
}
}
}
char *ans = (char *)calloc(length, sizeof(char));
for (int i = 0; (i < length) && (i + st < len); i++) {
ans[i] = s[i + st];
}
return ans;
}
Я продолжаю получать эту ошибку:
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000033
at pc 0x559567cee1ab bp 0x7ffdab22ea70 sp 0x7ffdab22ea60
Теперь, когда я комментирую условия if-else внутри третьего цикла for, я не получаю никаких ошибок. Почему это происходит?
Несмотря на добавление условий (i + k) < len и (j + k) < len?
Я попытался закомментировать условие if-else, и код не выдает ошибки.
Вы помните, что строка в C действительно называется строкой с завершающим нулем? Если вы хотите, чтобы массив символов использовался как строка, вам нужно убедиться, что он завершается нулем, и что вы выделили место для нулевого терминатора.
Кроме того, создайте минимальный воспроизводимый пример , который вы можете собрать (с включенным множеством дополнительных предупреждений и с дезинфицирующими средствами) локально. Затем вы можете легко отладить его самостоятельно, чтобы увидеть, что происходит.
Воспользуйтесь отладчиком и проанализируйте ситуацию, когда возникает ошибка.
Я попробую скомпилировать и отладить с помощью «-wall», а также посмотреть на случай «\ 0». Спасибо за предложения :)
Примечание: не используйте результат malloc() и семьи. Эти функции выделения памяти возвращают общий void *, который неявно преобразуется в любой другой указатель, например. Приведение типов является излишним и просто служит для загромождения кода.
Вы уверены, что сообщение об ошибке исходит из опубликованного кода? Я так не думаю.
Этот вариант должен быть -Wall. И вы также должны добавить -Wextra -pedantic
@Gerhardh Если предположить, что «настоящий» код включает stdlib.h, для этого кода не будет предупреждений.



Похоже, вам просто нужно завершить строку результата перед ее печатью. Просто измените calloc(length, ... на calloc(length + 1, ....
Опубликованный код не выдаст ошибку, указанную в вопросе. В опубликованном коде нет доступа за пределы.
Другими словами, ошибка возникает из-за кода, который не опубликован. Возможно/вероятно, вызывающая сторона longestPalindrome использует возвращенный указатель ans таким образом, что это вызывает опубликованную ошибку.
Ключ здесь:
...возвращение самого длинного палиндрома в строке.
Ваш код не возвращает строку стиля C. Выделенная память не содержит символ завершения строки (завершение NUL). (примечание: в настоящее время даже нет места для завершения в выделенной памяти)
Итак, если caller сделать что-то вроде:
...
char* str = longestPalindrome(someString);
if (str) puts(str);
вызов puts будет обращаться к памяти за пределами границ.
Решение: убедитесь, что ans указывает на память, содержащую символ завершения.
Также код не обрабатывает случай, когда ввод является пустой строкой. Это также может привести к ошибкам.
Кстати: будьте осторожны с использованием VLA, таких как char c[len];. Если входная строка очень большая, это может привести к переполнению стека.
Хорошее замечание о VLA
Интересный побочный вопрос: можно ли решить эту задачу за линейное время?
В коде есть несколько проблем:
вы должны выделить один дополнительный байт для совпадения и сохранить нулевой терминатор в конце блока, чтобы сделать его правильной строкой C:
char *ans = calloc(length + 1, sizeof(char));
for (int i = 0; i < length; i++) {
ans[i] = s[st + i];
}
ans[length] = '\0';
return ans;
обратите внимание, что вы также можете использовать strndup() и заменить весь блок кода на
return strndup(st + i, length);
без ограничителя null вызывающий код вызовет доступ за пределы, поскольку он пытается найти конец строки, например, при ее печати.
Алгоритм не находит встроенные палиндромы в строке аргумента, он находит подстроки, которые имеют обратную версию, встроенную где-то в строку аргумента. Например, for "a dog has no god" вернет "dog ", что не является палиндромом.
Вот модифицированная версия:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *longestPalindrome(const char *s) {
size_t len = strlen(s);
size_t st = 0;
size_t length = 0;
size_t i, n, k;
for (i = 0; i + length < len; i++) {
for (n = length + 1; i + n < len; n++) {
for (k = 0; k < n; k++) {
if (s[i + k] != s[i + n - 1 - k])
break;
}
if (k == n) {
/* found a palindrome of length n */
length = n;
st = i;
}
}
}
return strndup(s + st, length);
}
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
char *p = longestPalindrome(argv[i]);
printf("'%s' -> '%s'\n", argv[i], p);
free(p);
}
return 0;
}
Функции strdup и strndup долгое время были частью стандарта POSIX, и, наконец, они были включены в новый стандарт C23. Если strndup() недоступен в вашей системе, его можно записать так:
#include <stdlib.h>
#include <string.h>
char *strndup(const char *s, size_t n) {
char *p = malloc(n + 1);
if (p != NULL) {
memcpy(p, s, n);
p[n] = '\0';
}
return p;
}
Хороший ответ. AFAIK strndup (все еще) нестандартен. Пожалуй, стоит упомянуть.
@SupportUkraine: Хорошие новости: функция POSIX strndup наконец-то вошла в следующий стандарт C: C23 7.26.2.7 Функция strndup.
Звучит здорово. Извините, но я не могу проголосовать дважды ;-)
скомпилировать с -wall