Функция reverse в С
Программа иногда выводит мусор или просто падает. В выводе ничего нет. Возможно необходимо про инициализировать временный массив p, но как именно?
#include <stdio.h>
#define MAXLINE 1000
size_t strlen(const char* s){
const char* p = s;
for(;*p;p++)
;
return (size_t)(p - s);
}
char* reverse(char* s){
size_t size_s = strlen(s);
char p[MAXLINE];
int i, j = 0;
for(i = size_s-1; i >= 0; i--,j++){
p[j] = s[i];
}
p[j] = '\0';
return p;
}
int main(void){
char s[MAXLINE] = "Hello";
char *str = reverse(s);
puts(str);
return 0;
}
Ответы (3 шт):
Что происходит
Компилятор выдаёт две ошибки:
- В заголовке цикла
forсмешана знаковая и беззнаковая арифметика. Это обычно не приводит к проблемам, но приведение добавить не помешает. - Оператор
return p;возвращает адрес локальной переменной. Это настоящая причина ваших проблем. Когда функция завершилась, память, которую занимала локальная переменная, уже используется для других нужд. Данные затёрты, а вы их пытаетесь читать:
$ gcc -std=c11 -pedantic -Wall -Wextra -Werror -Wwrite-strings -Wconversion temp.c temp.c: In function ‘reverse’: temp.c:18:13: error: conversion from ‘size_t’ {aka ‘long unsigned int’} to ‘int’ may change value [-Werror=conversion] 18 | for(i = size_s-1; i >= 0; i--,j++){ | ^~~~~~ temp.c:22:12: error: function returns address of local variable [-Werror=return-local-addr] 22 | return p; | ^
Что можно сделать
В C, если вы хотите вернуть строку, есть две (нет три) возможности:
- или вы выделите новую строку из свободной памяти;
- или вы вернёте указатель на какую-то память, которая уже существовала до вызова функции;
- или вы используете статический буфер.
Новая строка
malloc и вперёд.
NB Никода на пишите size = strlen(s). В C надо чётко различать длины строк и размеры буферов. Не мешайте эти понятия.
NB Имя reversed означает что функция строит новую перевёрнутую (reversed) строку.
char* reversed(const char* s){
size_t len = strlen(s);
char *p = malloc(len + 1); // проверка выделения памяти опущена
for (size_t i = 0; i < len; ++i) {
p[len - 1 - i] = s[i];
}
p[len] = '\0';
return p;
}
Использование:
int main(void){
char s[] = "Hello";
char *str = reversed(s);
puts(str);
free(str);
}
Старая память (1)
Можно перевернуть строку на месте. Возвращать ничего не надо, так программист поймёт, что функция что-то делает со строкой s:
void reverse(char* s){
size_t len = strlen(s);
if (len > 0) { // надо защититься от переполнения в цикле
for (size_t i = 0, j = len - 1; i < j; ++i, --j) {
char t = s[i];
s[i] = s[j];
s[j] = t;
}
}
}
Использование даже проще:
int main(void){
char s[] = "Hello";
reverse(s);
puts(s);
}
Старая память (2)
Не всегда выделять память внутри функции – хорошая идея. Тогда потребуем чтобы пользователь функции выделил память заранее:
void reverse(const char* s, char* p){
size_t len = strlen(s);
for (size_t i = 0; i < len; ++i) {
p[len - 1 - i] = s[i];
}
p[len] = '\0';
}
Использование сложное:
int main(void){
char s[] = "Hello";
char* p = malloc(strlen(s) + 1);
reverse(s, p);
puts(p);
free(p);
}
P.S. Пользуйтесь компилятором для поиска ошибок в коде. Вы сэкономите кучу времени.
Работа со строками в Си - та еще морока. Как С++ программист, могу предоставить это решение:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* reverse(const char* s, size_t n) {
char* p = malloc(n + 1); // +1 для нуль-терминатора
if (p == NULL) {
return NULL; // Проверка на успешное выделение памяти
}
for (size_t i = 0; i < n; ++i) {
p[i] = s[n - 1 - i];
}
p[n] = '\0'; // Добавление нуль-терминатора
return p;
}
int main(void) {
char s[] = "Hello";
char *str = reverse(s, strlen(s));
if (str != NULL) {
puts(str);
free(str); // Освобождаем выделенную память
} else {
fprintf(stderr, "Ошибка выделения памяти\n");
}
return 0;
}
Конечно, рекомендовал бы использовать возможности стандартной библиотеки.