Массив указателей и многое другое в C - PullRequest
2 голосов
/ 06 апреля 2011

Вот кусок кода, где я не понимаю

#include "malloc.h"

/*some a type A and type for pointers to A*/
typedef struct a
{
    unsigned long x;
} A, *PA;

/*some a type B and type for pointers to B*/
typedef struct b
{
    unsigned char length;
    /*array of pointers of type A variables*/
    PA * x;
} B, *PB;

void test(unsigned char length, PB b)
{
    /*we can set length in B correctly*/

    b->length=length;

    /*we can also allocate memory for the array of pointers*/

    b->x=(PA *)malloc(length*sizeof(PA));

    /*but we can't set pointers in x*/

    while(length!=0)
        b->x[length--]=0; /*it just would not work*/
}

int main()
{
    B b;
    test(4, &b);
    return 0;
}

Может ли кто-нибудь концептуально объяснить мне, почему мы не можем установить указатели в массиве x в test ()?

Ответы [ 5 ]

4 голосов
/ 06 апреля 2011

В последней строке test () вы инициализируете местоположение в конце вашего массива.Если ваша длина равна 4, то ваш массив длиной 4 указателя.b-> x [4] является 5-м элементом массива, а 1-й является b-> x [0].Вам нужно изменить цикл while, чтобы перебирать значения от 0 до длины - 1.

1 голос
/ 06 апреля 2011

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

typedef struct a
{
    unsigned long x;
    ...and other members...
} A, *PA;

typedef struct b
{
    unsigned char length;
    PA * x;
} B, *PB;

Последний член B - это struct a **, который может понадобиться, но редко бывает.Вы, вероятно, должны упростить все, используя:

typedef struct a
{
    unsigned long x;
} A;

typedef struct b
{
    unsigned length;
    A       *array;
} B;

Это переписывание отражает личные предубеждения против typedefs для указателей (поэтому я исключил PA и PB).Я изменил тип length в B на unsigned с unsigned char;использование unsigned char экономит место в показанном дизайне, хотя это, возможно, могло бы сэкономить пространство, если бы вы отслеживали выделенную длину отдельно от используемой длины (но даже тогда я бы, вероятно, использовал unsigned short вместо unsigned char).

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

Код в вашей функции test() упрощает:

void init_b(unsigned char length, B *b)
{
    b->length = length;
    b->x = (A *)malloc(length*sizeof(*b->x));
    for (int i = 0; i < length; i++)
        b->x[i] = 0;
}

int main()
{
    B b;
    init_b(4, &b);
    return 0;
}

Использование идиоматического цикла for позволяет избежать выхода за пределы(одна из проблем в оригинальном коде).Цикл инициализации для выделенной памяти может быть заменен операцией memset(), или вы можете использовать calloc() вместо malloc().

Ваш исходный код устанавливал указатели в массиве указателей наноль;вы не могли тогда получить доступ к каким-либо данным, потому что не было никаких данных;Вы не выделили пространство для фактических struct a значений, просто пространство для массива указателей на такие значения.

Конечно, код должен либо проверять, не удалось ли выделить память, либо использовать функцию покрытия дляРаспределитель памяти, который гарантирует, что никогда не вернется, если выделение памяти не удается.Не безопасно предполагать, что выделение памяти будет выполнено без проверки где-либо.Функции покрытия для распределителей часто идут по именам, таким как xmalloc() или emalloc().

Кто-то еще указал, что malloc.h является нестандартным.Если вы используете средства настройки, которые он предоставляет, или средства отчетности, которые он предоставляет, то malloc.h - это хорошо (но оно не доступно везде, поэтому оно ограничивает переносимость вашего кода).Тем не менее, большинство людей в большинстве случаев должны просто забыть о malloc.h и использовать вместо него #include <stdlib.h>;использование malloc.h является признаком размышлений со времен до появления стандарта C89, когда не было заголовка, объявляющего malloc() и др., и это long время назад.


См. Также Освобождение 2D-массива стека ;код был изоморфен с этим кодом (вы в том же классе?).И я рекомендовал и проиллюстрировал то же упрощение там.

1 голос
/ 06 апреля 2011

Если вы хотите установить значение NULL для каждого PA в b->x, тогда следует написать --length вместо length--.
Очевидно, попытка выяснить, где принадлежит --, сбивает с толку. Вы лучше напишите:

unsigned i;
for (i = 0; i < length; i++)
   b->x[i] = 0;

Но на самом деле в этом случае вы можете просто использовать:

memset(b->x, 0, length*sizeof(PA));
0 голосов
/ 06 апреля 2011

Я просто добавил printf в main, чтобы проверить значения b.length и b.x [] и все работает. Просто добавил это так printf("%d, %d %d %d %d", b.length, b.x[0], b.x[1], b.x[2], b.x[3]); до return.

Это дает 4, 0, 0, 0, 0, что я думаю, что вы ожидаете, нет? Или это алгоритмическая ошибка

0 голосов
/ 06 апреля 2011

Я предполагаю, что вы пытаетесь обнулить все беззнаковые длинные в массиве A, на которые указывает B.

Есть ли проблема с приоритетом с операторами -> и [] здесь?

Попробуйте:

(b->x)[length--] = 0;

И, возможно, измените

typedef struct a
{
    unsigned long x;
} A, *PA;

на

typedef struct a
{
    unsigned long x;
} A;
typedef A * PA;

и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...