Обратная строка в malloc

Мне нужно определить "слово" в этом вопросе, чтобы быть любой последовательностью символов, которая не содержит пробела или нулевого символа. Например, строка “Hello World” будет содержать 2 слова. Однако на самом деле слово может быть пустым, т. Е. Ноль символов. Предложение будет представлять собой серию слов, разделенных 1 пробелом. Так “Hello World” будет предложение из двух слов. Цель ReverseSentence было бы изменить предложение в терминах слова. Прямо сейчас у меня возникает ошибка, из-за которой программы переходят к вызову функции и распечатывают от a1 до a5. По достижении а5 кажется, что программа прерывается и ядро ​​сбрасывается. Если я заменю пробел пробелом, он будет считан в предыдущем вводе и заменен согласно количеству пробелов. Куда я иду не так?

ReverseSentence.c

#include <stdlib.h>  /* malloc */
#include <string.h>  /* strcat, strcpy */

void ReverseSentence(char *str)
{
    char *newSentence;
    int i, j, start, len;
    /* contains the string length of the input */
    len = strlen(str);
    /* position or index in the array */
    start = strlen(str);
    /* malloc */
    newSentence = malloc(len + 1);

    /* loop checks from the right of the sentences */
    for (i = len; i >= 0; i--) {
        /* if index reach the array with a space or zero */
        if (str[i] == ' ' || i == 0) {
            /* allocates memory */
            char *word = malloc((start - i) + 1);
            int c = 0;

            if (i == 0) 
                /* index remains same */
                j = i;
            else
                j = i + 1;

            /* j smaller or equal than the start position */
            for (; j <= start; j++) {
                /*do a incremental*/
                word[c++] = str[j];
            }
            /* hits a null char */
            word[c] = '\0';
            /* string concatenate */
            strcat(newSentence, word);
            /* if index hits a space */
            if (str[i] == ' ')
                strcat(newSentence, " "); /* concatenate space to newSentence */
            else
                strcat(newSentence, "\0");
            start = i - 1;

            /* free memory */
            free(word);
        }
    }
    newSentence[len] = '\0';
    /* string copy */
    /* str is destination, newSentence is the source */
    /* copy new string to original string */
    strcpy(str, newSentence);
    /* free memory */
    free(newSentence);
}

main.c

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

int main()
{
    char a1[] = "Hello World ";
    char a2[] = "abcdefghi ";
    char a3[] = " ";
    char a4[] = "C programming is a dangerous activity";
    char a5[] = "a "; /* a sentence with only empty words */
    ReverseSentence(a1);

    printf("Test case 1:\"%s\"\n", a1); /* prints "World Hello" */
    ReverseSentence(a2);

    printf("Test case 2:\"%s\"\n", a2); /* prints "abcdefghi" */
    ReverseSentence(a3);

    printf("Test case 3:\"%s\"\n", a3); /* prints "" */

    ReverseSentence(a4);
    printf("Test case 4:\"%s\"\n", a4); /* prints "activity dangerous a is pro Cgramming" */

    ReverseSentence(a5);
    printf("Test case 5:\"%s\"\n", a5); /* prints " " */

    return 0;
}

РЕДАКТИРОВАТЬ: новая версия

void ReverseSentence(char *str)
{
    /* holder */
    /* pointer to char */
    char *newSentence;
    int i, start, len, lastindex, size;

    /* contains the string length of the input */
    len = strlen(str);
    lastindex = strlen(str);
    /* starting position */
    start = 0;
    i = 0;
    /* malloc */
    newSentence = malloc(sizeof(char) * strlen(str));

    while (i >= 0) {
        for (i = len - 1; str[i] != '\0' && str[i] != ' '; i--) {
            lastindex--;
        }

        /* number of chars in string size */
        size = len - lastindex;
        /* Copy word into newStr at startMarker */
        strncpy(&newSentence[start], &str[lastindex], size);

        /* pointer move to right */
        start = start + size;
        /* Space placed into memory slot */
        newSentence[start] = ' ';
        /* start position moves by 1 towards the right */
        start = start + 1;
        /* pointer at len moves to left */
        lastindex = lastindex - 1;
        /* lastindex moves to where len is */
        len = lastindex;
    }

    /* Copy new string into old string */
    for (i = 0; str[i] != '\0'; i++) {
        str[i] = newSentence[i];
    }

    /* free memory */
    free(newSentence);
}

4 ответа

В дополнение к ответу Матиаса: вы не выделяете достаточно памяти, я просто сделал дикое предположение и добавил 1 к аргументам, передаваемым malloc,

newSentence = malloc(len + 2);   // +2 instead of +1

а также

char *word = malloc((start - i) + 2);  // +2 instead of +1

И теперь он больше не падает. Так что здесь определенно переполнение буфера.

Я не претендую на то, что программа сейчас совершенно правильная. Вы должны взглянуть на это.

Ваш код не является безопасным. Вы никогда не инициализируете newSentence, поскольку malloc() только выделяет, но не инициализирует память (в отличие от calloc()). Таким образом, вы начинаете с предложения мусора, где вы добавляете что-то новое (strcat()). В зависимости от мусора, даже в выделенной памяти может не быть 0, и вы получаете доступ к некоторой нераспределенной области памяти.

Ваш метод слишком сложен. У него есть несколько проблем:

  • вы не инициализируете newSentence: поскольку malloc память неинициализирована, вы вызываете неопределенное поведение, когда копируете слова в конце strcat, Вы можете исправить это с *newSentence = '\0';

  • когда вы копируете слово в выделенный word буфер, вы перебираете до startЗатем вы добавляете '\0' в конце. Вы эффективно пишете один байт слишком много для последнего слова (case i == 0). Это вызывает неопределенное поведение.

  • strcat(newSentence, "\0"); ничего не делает.

  • выделение буфера для каждого найденного слова расточительно, вы можете просто скопировать слово с помощью memcpy или с простым for петля.

Вы можете упростить с помощью следующих шагов:

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

Вот код:

char *ReverseSentence(char *str) {
    int len = strlen(tmp);    /* length of the string */
    char *tmp = strdup(str);  /* copy of the string */
    int i;        /* index into the copy */
    int j = len;  /* index into the string */
    int n;        /* length of a word */
    for (i = 0; i < len; ) {
        n = strcspn(tmp + i, " ");   /* n is the length of the word */
        j -= n;                      /* adjust destination offset */
        memcpy(str + j, tmp + i, n); /* copy the word */
        i += n;                      /* skip the word */
        if (tmp[i] != '\0') {        /* unless we are at the end */
            j--;
            str[j] = tmp[i];         /* copy the separator */
            i++;
        }
    }
    free(tmp);                       /* free the copy */
    return str;
}

В новой версии вашей программы есть как минимум две проблемы:

  • Вы не выделяете достаточно памяти, вы не учитываете нулевой терминатор. Вы должны выделить еще один байт.

  • в вашем первом for цикл вы позволяете i стать -1. Цикл должен остановиться, когда i ноль: изменить ваш for утверждение как это: for(i=len-1; tr[i] != ' ' && i >= 0; i--), Вы неправильно предполагаете, что первый байт перед str буфер равен нулю, поэтому str[i]!='\0' неправильно. Кстати, доступ к одному байту до str выход буфера в неопределенном поведении.

  • Возможно, есть и другие проблемы.

Другие вопросы по тегам