Обратная строка в 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'
в конце. Вы эффективно пишете один байт слишком много для последнего слова (casei == 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
выход буфера в неопределенном поведении.Возможно, есть и другие проблемы.