Как уменьшить строку с помощью Realloc

#c #string #realloc

#c #строка #перераспределение

Вопрос:

Впервые задаю вопрос здесь: ну, мне нужно взять исходную строку и удалить пробелы и цифры из строки, которая мне нужна, чтобы использовать точный объем памяти.

По какой-то причине строка в порядке в начале, но затем она выводит значения мусора:

исходная строка: "abcd2 34fty 78 jurt#"
что нужно было сделать: abcdftyjurt#

Мой код:

 #define _CRT_SECURE_NO_WARNINGS

#include <malloc.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>    
#include <string.h>

/* Function declarations */

/*-------------------------------------------------------------*/
void Ex1();
char* clearDigitsAndSpaces(char*);
/*-------------------------------------------------------------*/
void Ex2();

/*-------------------------------------------------------------*/
void Ex3();

/*-------------------------------------------------------------*/
/* Declarations of other functions */

int main() {
    int select = 0, i, all_Ex_in_loop = 0;
    printf("Run menu once or cyclically?n(Once - enter 0, cyclically - enter other number) ");
    if (scanf("%d", amp;all_Ex_in_loop) == 1)
        do {
            for (i = 1; i <= 3; i  )
                printf("Ex%d--->%dn", i, i);
            printf("EXIT-->0n");
            do {
                select = 0;
                printf("please select 0-3 : ");
                scanf("%d", amp;select);
            } while ((select < 0) || (select > 3));
            switch (select) {
            case 1: Ex1(); break;
            case 2: Ex2(); break;
            case 3: Ex3(); break;
            }
        } while (all_Ex_in_loop amp;amp; select);
        return 0;
}

/* Function definitions */

void Ex1() {
    char input[] = "abcd2 34fty    78 jurt#";
    char *temp = NULL;
    temp = clearDigitsAndSpaces(input);
    printf("%sn ", temp);
    free(temp);
}

char *clearDigitsAndSpaces(char *old_string) {
    char *new_string;
    int count = 0;
    int i = 0;
    int j = 0;
    int size = strlen(old_string);
    new_string = (char *)malloc(size * sizeof(char));
    assert(new_string); /*Memory allocation check*/
    while (old_string[i]) {
        if (old_string[i] != ' ' amp;amp; (old_string[i] > '9' || old_string[i] < '0')) {
            new_string[j  ] = old_string[i];
        } else {
            //size -= 1;
            new_string = (char *)realloc(new_string, size - 1);
        }
        i  ;
    }
    assert(new_string);
    //printf("%s", new_string);
    return new_string;
}

void Ex2() {
}

void Ex3() {
}
  

Комментарии:

1. Это C или C ? Выберите один.

2. Комментарий без кода: вы завершили свою строку нулевым значением? Я думаю, что OP хотел пометить ее как C.

3. Просто используйте shrink_to_fit в вашем std::string.

4. Как подсказывает @gsamaras, вам нужно: int size = strlen(old_string) 1; разместить nul терминатор. ( strlen Возвращаемое значение не включает его.)

5. Не забудьте посчитать символ, заканчивающийся NUL, таким образом strlen(...) 1 , и не забудьте скопировать его в конце…

Ответ №1:

Проблема в вашем коде в том, что вы должны выделить один дополнительный байт для нулевого ограничителя.

Вы можете избежать использования realloc() , сначала просканировав исходную строку для определения размера выделения, а затем используя отдельный цикл для копирования содержимого:

 char *clearDigitsAndSpaces(const char *src) {
    char *new_string;
    size_t size = 1; // 1 extra byte for the null terminator.

    for (size_t i = 0; src[i] != ''; i  ) {
        if (src[i] != ' ' amp;amp; !(src[i] >= '0' amp;amp; src[i] <= '9'))
            size  ;
    }
    new_string = malloc(size);
    if (new_string) {
        size_t j = 0;
        for (size_t i = 0; src[i] != ''; i  ) {
            if (src[i] != ' ' amp;amp; !(src[i] >= '0' amp;amp; src[i] <= '9'))
                new_string[j  ] = src[i];
        }
        new_string[j] = '';  // set the null terminator
    }
    return new_string;
}
  

Ответ №2:

Во-первых: вам нужно понимать разницу между длиной строки C и размером строки C. Длина не включает нулевой ограничитель. Размер имеет значение. Итак, этот фрагмент:

 int size = strlen(old_string);
new_string = (char*)malloc(size * sizeof(char));
  

должно быть

 int size = strlen(old_string)   1;
new_string = (char*)malloc(size * sizeof(char));
  

(обратите внимание, что если вы используете Unicode в Windows с wchar_t вместо char , то размер в байтах в два раза больше длины, плюс 2 — каждый символ равен двум байтам, а также нулевой ограничитель, он же ‘sentinel’)


Во-вторых: я бы посоветовал вам использовать круглые скобки, чтобы четко указать намерение. Это может быть не «абсолютно необходимо», но не будет никаких сомнений в намерении, когда кто-то другой прочитает ваш код. Также избегайте многократного индексирования одного и того же. Изменить:

 if (old_string[i]!=' ' amp;amp; (old_string[i] > '9' || old_string[i]< '0'))
  

Для:

 char oldChar = old_string[i];
if ((oldChar != ' ') 
    amp;amp; ((oldChar > '9') || (oldChar < '0'))
   )
  

Наконец, вам нужно поместить нулевой символ в конце. Вам не нужно перераспределять; просто используйте не весь буфер. Изменить:

 new_string = (char*)realloc(new_string, size-1);
  

Для:

 new_string[j  ] = '';

// PS: if you really want to realloc, then add "new_string = (char*)realloc(new_string, j);" after writing the null character.
  

Также — если вы измените malloc на calloc , вам не нужно будет писать нулевой ограничитель, поскольку весь буфер будет обнулен до того, как вы скопируете в него что-либо.

Кроме того, я бы добавил проверку защитного предела в i в цикле while, чтобы убедиться, что он не может продолжаться до бесконечности.