Функция 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 шт):

Автор решения: Qwertiy

Нельзя возвращать указатель на локальную переменную.

→ Ссылка
Автор решения: Stanislav Volodarskiy

Что происходит

Компилятор выдаёт две ошибки:

  • В заголовке цикла 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, если вы хотите вернуть строку, есть две (нет три) возможности:

  1. или вы выделите новую строку из свободной памяти;
  2. или вы вернёте указатель на какую-то память, которая уже существовала до вызова функции;
  3. или вы используете статический буфер.

Новая строка

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. Пользуйтесь компилятором для поиска ошибок в коде. Вы сэкономите кучу времени.

→ Ссылка
Автор решения: Damir Hakimof

Работа со строками в Си - та еще морока. Как С++ программист, могу предоставить это решение:

#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;
}

Конечно, рекомендовал бы использовать возможности стандартной библиотеки.

→ Ссылка