C — функция возвращает указатель на строку из матрицы символов, используя ТОЛЬКО указатели

#c

#c

Вопрос:

Мне нужно написать функцию с именем MakeString. Функция возвращает указатель на строку, которая содержит по одному слову, составленному из каждой строки малой матрицы по порядку, так что каждый разрыв строки будет выражен как один пробел между словами в строке. (После последнего слова не будет пробела.)

Вывод: в функции не используется [] , но выполняется путем работы с указателями.

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

Что бы это ни стоило, и не будет оставаться в одном и том же месте все время. «Ответ», который возвращает функция, который является указателем, будет введен в указатель в MAIN.

Я попытался выполнить эту функцию, но она НЕ похожа на инструкцию и не очень хороша…

 #define SIZE 4
static char allocbuf[SIZE][];
static char *allocp = allocbuf;

char matrix[SIZE][SIZE]
{
    {U,N,T,E},
    {C,P,G,X},
    {D,L,A,B},
    {J,T,N,N}
};
char MakeString(int n)  /*return pointer to n charachters*/
{
    if (allocbuf   SIZE - allocp >=n)
    {
        allocp  = n;
        return  allocp - n;
    }
    else
        return 0;
}
 

Например:

Маленькая матрица:

 U N T E

C P G X

D L A B

J T N N

pStr = UNTE CPGX DLAB JTNN
 

Спасибо (:

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

1. Что allocbuf должно быть? Массив строк? Будут ли строки иметь фиксированный статический (во время компиляции) размер? Или они должны быть динамически распределены?

2. Кроме того, ваша MakeString функция не «создает» строку. И она также не возвращает ничего, что можно использовать в качестве строки. И я действительно не вижу необходимости во всей этой арифметике указателей, что плохого в простом индексе, с которым сравнивается SIZE ?

3. Наша лекция заставляет нас использовать указатели. Я знаю, что это не нужно…. Моя функция также не подходит : строки не статичны, А динамически распределяются. Не следует использовать [][] ИСПОЛЬЗОВАТЬ ТОЛЬКО УКАЗАТЕЛИ. @Какой-то программист, чувак

4. Итак, тогда allocbuf должен быть массив указателей на самом деле? И ваша MakeString функция должна выделить правильный размер (не забывая о нулевом терминаторе строки), а затем вернуть указатель?

5. Единственный раз, когда вы можете использовать [] объявление массива, — это в параметре функции или при объявлении массива со списком инициализации (размер зависит от количества элементов в списке).

Ответ №1:

Если я понимаю ваш вопрос, вы хотите написать функцию для чтения символов из matrix (2D-массива char ) в выделенную строку, помещая пробел между символами каждой строки и возвращая строку с нулевым завершением обратно вызывающей функции. Вам нужно сделать это с помощью указателей и без [index] обозначения массива.

Для начала, ваше объявление matrix неверно. E это не символ, это переменная. 'E' является символьным литералом. (обратите внимание на одинарные кавычки) Таким образом, правильное объявление matrix было бы:

     char matrix[SIZE][SIZE] = { {'U','N','T','E'},  /* don't use globals   */
                                {'C','P','G','X'},  /* declare in main and */
                                {'D','L','A','B'},  /* pass as a parameter */
                                {'J','T','N','N'} };
 

(примечание: достаточно char matrix[][SIZE] = {{...}}; простого, где количество строк будет определяться в зависимости от вашей инициализации)

Как отмечено в комментарии, избегайте использования глобальных переменных без крайней необходимости. (очень ограниченные случаи — не здесь). Вместо этого объявите matrix в области видимости, где это требуется, и передайте matrix в качестве параметра любой функции, которая должна обрабатывать данные. Напротив, определение является постоянным с #define помощью совершенно правильно, и вы должны определять константы по мере необходимости, чтобы избежать использования магических чисел в вашем коде.

Поскольку matrix это 2D-массив, чтобы передать его в качестве параметра, вы должны включить количество столбцов как часть передаваемого параметра. Вы можете либо объявить параметр как char matrix[SIZE][SIZE] или, что эквивалентно, как char (*matrix)[SIZE] отражающий тот факт, что первый уровень косвенности преобразуется в указатель на первый элемент при доступе. См.: Стандарт C11 — 6.3.2.1 Другие операнды — значения Lvalues, массивы и обозначения функций (p3) (обращая внимание на 4 исключения)

Внутри вашей makestring() функции вы должны выделить хранилище не менее SIZE * SIZE SIZE (пробел для каждого символа 3 пробела завершающий нулем символ). Присвоение начального адреса вашего нового блока памяти указателю, а затем создание второго указателя на блок позволит вам выполнять итерации по нему, копируя в него символы — при сохранении указателя на начало.

Собрав эти части вместе, вы могли бы сделать что-то похожее на:

 char *makestring (char (*a)[SIZE])
{
    char *str = malloc (SIZE * SIZE   SIZE), *p = str;  /* allocate */
    if (!str) {                         /* validate EVERY allocation */
        perror ("malloc-str");
        return NULL;
    }
    for (int i = 0; i < SIZE; i  ) {    /* for each row   */
        if (i)                          /* if row not 1st */
            *p   = ' ';                 /*    add space   */
        for (int j = 0; j < SIZE; j  )  /* for each char  */
            *p   = *(*(a   i)   j);     /*    copy to str */
    }
    *p = 0;         /* nul-terminate string */

    return str;     /* return pointer to allocated string */
}
 

(примечание: хотя это и не ошибка, C обычно избегает использования camelCase MixedCase имен переменных или в пользу всех строчных регистров, сохраняя имена в верхнем регистре для использования с макросами и константами.)

Поместив это в целом в короткий пример, вы могли бы сделать:

 #include <stdio.h>
#include <stdlib.h>

#define SIZE 4

char *makestring (char (*a)[SIZE])
{
    char *str = malloc (SIZE * SIZE   SIZE), *p = str;  /* allocate */
    if (!str) {                         /* validate EVERY allocation */
        perror ("malloc-str");
        return NULL;
    }
    for (int i = 0; i < SIZE; i  ) {    /* for each row   */
        if (i)                          /* if row not 1st */
            *p   = ' ';                 /*    add space   */
        for (int j = 0; j < SIZE; j  )  /* for each char  */
            *p   = *(*(a   i)   j);     /*    copy to str */
    }
    *p = 0;         /* nul-terminate string */

    return str;     /* return pointer to allocated string */
}

int main (void) {

    char matrix[SIZE][SIZE] = { {'U','N','T','E'},  /* don't use globals   */
                                {'C','P','G','X'},  /* declare in main and */
                                {'D','L','A','B'},  /* pass as a parameter */
                                {'J','T','N','N'} },
        *str;

    if ((str = makestring (matrix))) {  /* validate call to makestring */
        printf ("str: '%s'n", str);    /* output string */
        free (str);                     /* free allocated memory */
    }
}
 

(примечание: не забывайте free о выделяемой вами памяти. Хотя он будет освобожден при выходе из программы, приобретите привычку отслеживать ваши выделения и обеспечивать освобождение всех выделяемых вами блоков)

Пример использования / вывода

 $ ./bin/makestring
str: 'UNTE CPGX DLAB JTNN'
 

Использование памяти / проверка ошибок

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

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

Для Linux valgrind это обычный выбор. Для каждой платформы существуют аналогичные средства проверки памяти. Все они просты в использовании, просто запустите свою программу через нее.

 $ valgrind ./bin/makestring
==6576== Memcheck, a memory error detector
==6576== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6576== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6576== Command: ./bin/makestring
==6576==
str: 'UNTE CPGX DLAB JTNN'
==6576==
==6576== HEAP SUMMARY:
==6576==     in use at exit: 0 bytes in 0 blocks
==6576==   total heap usage: 1 allocs, 1 frees, 20 bytes allocated
==6576==
==6576== All heap blocks were freed -- no leaks are possible
==6576==
==6576== For counts of detected and suppressed errors, rerun with: -v
==6576== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
 

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

Просмотрите вещи и дайте мне знать, если у вас возникнут дополнительные вопросы.

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

1. Спасибо (: Я пробовал в моем Linux eclipse… Не работает /: Есть идеи? @David C. Rankin

2. У вас проблема с eclipse. Какой компилятор использует eclipse? MinGW? VS ( cl.exe )? У вас есть командная строка разработчика или оболочка MinGW Msys ? Дайте мне знать, и я покажу вам, как скомпилировать его из командной строки (что вы должны делать в любом случае — таким образом, вы будете знать, что сказать eclipse делать ….)

Ответ №2:

Вам нужно указать размер allocbuf , чтобы он мог содержать весь результат:

 char allocbuf[SIZE * (SIZE   1)];
 

В этом нет необходимости allocp , потому что имя массива при использовании в вычислениях преобразуется в указатель. В MakeString , вам нужно перебирать строки и символы matrix , копируя их allocbuf .

 char *MakeString()
    for (int i = 0; i < SIZE; i  ) {
        memcpy(allocbuf   i * SIZE, matrix   i, SIZE);
        if (i < SIZE - 1) {
            // write space between rows
            *(allocbuf   i * SIZE   SIZE) = ' ';
        } else {
            // write null at end
            *(allocbuf   i * SIZE   SIZE) = 0;
        }
    }
    return allocbuf;
}
 

В инструкциях не упоминается n аргумент to MakeString() , поэтому я удалил его.

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

1. Спасибо (: Как я могу изменить его получение из пользовательской матрицы? Объявление char allocbuf находится в main Или в методе MakeString() @Barmar?

2. Это глобальная переменная, как вы и написали в вопросе.

3. Я поместил символ allocbuf над методом в файле.c, но он ничего не печатает через main .. и я изменил его, получив значения char ** smallMat : я обновлю все. Спасибо @Barmar

4. Не удаляйте исходный код при обновлении вопроса, добавьте этот новый код в качестве дополнения внизу.