Как улучшить код для динамически размещаемых строк в C? - PullRequest
1 голос
/ 13 февраля 2020

Так что я сейчас практикуюсь и изучаю C и натолкнулся на довольно простой вызов от CodeWars, который попросил распечатать строку "Aa ~", "Pa!" И "Aa!" в зависимости от того, было ли п <= 6 или нет. Я знаю, как мы можем сделать это с массивами, но хотел попробовать использовать динамически распределенные массивы символов, такие как mallo c, для повышения эффективности. </p>

Я хочу убедиться, что я разбираюсь с основами этих вопросов

  1. Так что я знаю в других примерах c mallo с int, мы устанавливаем указатель (из введите int), чтобы указать на выделенный блок памяти. Это все еще указатель, когда я объявляю "char * ptr", чтобы он указывал на выделенный блок памяти, потому что извините, я думал, что "char * что угодно" означало, что это было соглашение для представления строки. Поэтому не уверен, почему ниже работает несколько, если я не настраиваю указатель типа "char ** ptr", как я думал.

  2. почему я получаю ошибку типа "mallo c: *** для объекта 0x100000fab: освобожден указатель не был выделен", когда я пытаюсь вернуть ответ, особенно когда "val" равен 1 или 0? Я где-то читал, что изменение указателя (string?) Answer на NULL решит проблему, но не совсем уверен, почему это работает.

3 Для продолжения в целом вышеуказанного вопроса, для освобождения места, каков наилучший способ сделать это, если мы динамически выделяем блок памяти в функции, но должны вернуть значение из этой функции? Как, в, мы освобождаем пространство после или до?

Спасибо всем за ваш вклад.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define val 1
char *sc(int); // function declaration/prototype 

int main(int argc, const char * argv[]) {
    char *answer = sc(val);
    printf("The answer is %s\n", answer);
    answer = NULL; // why does this work
    free(answer);
    return 0;
}

char *sc(int n) {
  // if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
    char *ptr = (char*) malloc(n*4);
    if (ptr == NULL){
        printf("malloc failed");
    }
    char *first = "Aa~ ";
    char *second = "Pa! Aa!";
    char *third = "Pa!";
    if (n <= 6 && n >1) {
        for (int i = 0; i <n-1; i++){
            ptr = strcat(ptr, first);
        }
        ptr = strcat(ptr, second);
        }
    else if (n> 6){
        for (int i = 1; i < n; i++) {
            ptr = strcat(ptr, first);
        }
        ptr = strcat(ptr, third);
    }
    else if (n <= 1){
        ptr = "";
    }
    else {
        printf("Error!");
        exit(0);
    }

    return ptr;
}

Ответы [ 2 ]

1 голос
/ 13 февраля 2020
  1. Я думал, что char * что-нибудь означало, что это было условием для представления строки. Поэтому не уверен, почему нижеприведенное работает несколько, если я не настраиваю указатель типа «char ** ptr», как я думал.

Вы выделяете память для строки, так что это просто должно быть char *. char ** будет использоваться для массива из нескольких строк или для указателя на переменную, которая содержит указатель на строку.

почему я получаю "ошибку mallo c: *** для объекта 0x100000fab: освобожденный указатель не выделен"

Вы получаете это, когда делаете ptr = ""; , После этого ptr больше не указывает на память, выделенную с помощью malloc, оно указывает на этот строковый литерал. Если вы хотите установить в выделенной памяти пустую строку, вы можете сделать

ptr[0] = '\0';

. Это поместит нулевой терминатор в первый элемент строки.

Вы также должны сделать это перед кодом, который использует strcat() для добавления в строку. В противном случае вы добавляете неинициализированные данные. Простейшим было бы сделать это сразу после выделения памяти (тогда вам это не нужно в блоке n <= 1.

Блок else не нужен. Других возможностей нет, кроме 3 проверьте, если процессор не работает со сбоями (в этом случае все ставки отключены). Однако перед вызовом malloc() следует проверить n < 1, так как вы не можете выделить отрицательную память, и malloc(0) может вернуть NULL .

Когда вы выделяете пространство для ptr, вам нужно добавить 1 байт для завершающего ноля строки.

char *sc(int n) {
    // if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
    if (n >= 1) {
        char *ptr = malloc(n*4 + 1);
    } else {
        char *ptr = malloc(1);
    }
    if (ptr == NULL){
        printf("malloc failed");
        exit(1);
    }
    ptr[0] = '\0'; // initialize empty string
    char *first = "Aa~ ";
    char *second = "Pa! Aa!";
    char *third = "Pa!";
    if (n <= 6 && n >1) {
        for (int i = 0; i <n-1; i++){
            ptr = strcat(ptr, first);
        }
        ptr = strcat(ptr, second);
        }
    else if (n> 6){
        for (int i = 1; i < n; i++) {
            ptr = strcat(ptr, first);
        }
        ptr = strcat(ptr, third);
    }
    else if (n <= 1){
        // nothing to do
    }

    return ptr;
}
1 голос
/ 13 февраля 2020

Основная проблема с вашим кодом заключается в том, что вы не обнуляете буфер, полученный из malloc(), поэтому первый strcat() не обязательно будет записываться в начале строки, но в конце .

Вы можете исправить это с помощью strcpy() сразу после вызова malloc() и проверить:

strcpy(ptr, "");

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

ptr[0] = 0;

Вы также, кажется, выделяете свой буфер слишком коротким. Если вы напишите n-1 копии Aa~ (4 байта) плюс одну копию Pa! Aa! (8 байт, если вы добавите конечный ноль!), Вам на самом деле понадобится 4 * (n+1) в качестве пробела. Так что либо всегда выделяйте это, либо делайте так в случае n < 6, где вам нужны дополнительные байты.

Это также проблема:

ptr = "";

Потому что теперь ваш ptr больше не указывает на буфер, возвращаемый malloc(), но на stati c (пустую) строку в вашем двоичном файле. Вполне вероятно, что это то место, где у вас возникают проблемы с free(), поскольку вызывать его для строки c в вашем двоичном коде определенно неверно.

Более того, после установки ptr = "" у вас больше нет ссылок на выделенный вами буфер, что означает, что вы, скорее всего, только что создали утечку памяти!

В этом случае вам следует просто использовать strcpy() или установить первый байт на ноль. Но если вы делаете это в начале программы, вам не нужно делать это здесь.

Наконец, free(NULL); работает (как и не выдает ошибку), потому что это часть его спецификация, вы можете передать ему указатель NULL, и он ничего не будет делать. Но обратите внимание, что это не освобождает выделенный вами буфер, поэтому у вас здесь также есть утечка памяти.

Я бы еще больше реорганизовал вторую часть вашего кода, чтобы у вас не было слишком много повторений, добавляющих строки:

char *sc(int n) {
    /* if n <= 6 then will have an extra "Aa!"
     * after "Pa!" at the nth position.
     */
    char *ptr;
    if (n < 0) {
        printf("Error!");
        return NULL;
    }
    ptr = (char*) malloc(4 * (n+1));
    if (ptr == NULL){
        printf("malloc failed");
        return NULL;
    }
    strcpy(ptr, "");
    if (n > 1) {
        for (int i = 1; i < n; i++){
            strcat(ptr, "Aa~ ");
        }
        strcat(ptr, "Pa!");
        if (n <= 6) {
            strcat(ptr, " Ah!");
        }
    }
    return ptr;
}

Также обратите внимание, что вам не нужно присваивать результат strcat() обратно ptr каждый раз, так как он всегда возвращает свой первый аргумент в любом случае, поэтому присвоение ему там ничего не меняет на самом деле.

...