Код не работает, когда я ссылаюсь на двойной указатель - PullRequest
0 голосов
/ 22 января 2019

Почему я не могу правильно хранить и ссылаться на двойной указатель?

Этот код работает:

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

typedef struct _node{
    int nodeNumber;
    int weight;
}* Node;

int main(int argc, char **argv){
    Node nodeList = calloc(3, sizeof(struct _node));

    // Used for testing
    nodeList->nodeNumber = 9;
    printf("Node Number: %d\n", nodeList->nodeNumber);

    return 0;
}

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

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

typedef struct _node{
        int nodeNumber;
        int weight;
}** Node;



int main(int argc, char **argv){
        Node nodeList = calloc(3, sizeof(struct _node));

        // Used for testing
        nodeList[0]->nodeNumber = 9;
        printf("Node Number: %d\n", nodeList[0]->nodeNumber);

        return 0;
}

Моя программа работает секунду, а затем вылетает.Нет ошибок или чего-то еще.Я думал, что ссылка на структуру с

nodeList[0]->nodeNumber = 9;

будет работать, но, очевидно, это не так.

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

------------------------------- РЕДАКТИРОВАТЬ ------------------------------------

Iизменил мой код на это:

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

typedef struct _node{
        int nodeNumber;
        int weight;
}** Node;



int main(int argc, char **argv){
        Node nodeList = malloc(sizeof(struct _node *));

        // Used for testing
        nodeList[0]->nodeNumber = 9;
        printf("Node Number: %d\n", nodeList[0]->nodeNumber);

        return 0;
}

но моя программа все еще падает.

Ответы [ 3 ]

0 голосов
/ 22 января 2019

Если вы настаиваете на использовании двойных указателей, то вы должны действовать следующим образом:

typedef struct _node{
    int nodeNumber;
    int weight;
} Node;

и в своей функции:

    Node **nodelist = malloc(3 * sizeof(Node *));
    for (int i=0; i<3; i++)
        nodelist[i]= calloc(1,sizeof(Node));

Обратите внимание, что я не использую указатели вtypedef, потому что это очень запутанно.Вместо этого я объявляю nodelist как двойной указатель.


Так что вы, профессор, настаиваете на использовании двойного указателя в typedef (я предлагаю вам сказать своему профессору посетить stackoverflow.com ....).Затем действуйте следующим образом:
typedef struct _node{
    int nodeNumber;
    int weight;
} **Node;

и в вашей функции:

    Node nodelist = malloc(3 * sizeof(*nodelist));
    for (int i=0; i<3; i++)
        nodelist[i]= calloc(1,sizeof(*nodelist[i]));

Здесь я не использую имя типа, а имя переменной, чтобы определить размер для выделения: *nodelist разыменовывает список узлов для struct _node *, а *nodelist[i] разыменовывает это для фактического struct _node (обратите внимание, что значение i здесь не важно; оно используется только для указания компилятору, что элемент массивапредназначено).

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

0 голосов
/ 22 января 2019

Другие ответили на вопрос, но в случае, если кто-то сталкивается с этим и интересуется, как это сделать правильно:

  • Никогда не прятайте указатели за typedefs.
  • Никогда не используйте type**, когда вы действительно хотите 2D массив. Это следует использовать только для таких вещей, как таблица строк переменной длины, которая не является двумерным массивом. Подробнее см. Правильное размещение многомерных массивов .
  • Всегда free() что вы malloc(). Конечно, ОС сделает это за вас в большинстве случаев. Но, вызывая free(), мы можем выявлять и обнаруживать ошибки в другом месте кода, такие как утечки памяти, висячие указатели и т. Д., И все они проявятся как сбой программы при вызове free().

Вот примеры правильного кода, один пример с массивом 1D и один с массивом 2D:

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

typedef struct {
  int nodeNumber;
  int weight;
} Node;

int main (void)
{
  Node* nodeList = calloc(3, sizeof *nodeList);

  nodeList[0].nodeNumber = 9;
  printf("Node Number: %d\n\n", nodeList->nodeNumber);

  free(nodeList);


  /****************************************************************************/

  const size_t x = 2;
  const size_t y = 3;
  Node (*nodeList2D)[y] = calloc(x, sizeof *nodeList2D);

  int count = 0;
  for(int i=0; i<x; i++)
  {
    for(int j=0; j<y; j++)
    {
      nodeList2D[i][j].nodeNumber = count++;
      printf("Node (%d, %d): %d\n", i, j, nodeList2D[i][j].nodeNumber);
    }
  }

  free(nodeList2D);

  return 0;
}

Обратите внимание, что трюк sizeof *nodeList2D при вызове malloc / calloc адаптируется к используемому типу. В случае двумерного массива это даст нам размер одного одномерного массива (такой же, как 3 * sizeof(Node)), а затем мы выделим 2 таких блока памяти с помощью calloc.

0 голосов
/ 22 января 2019

С

typedef struct _node{
    int nodeNumber;
    int weight;
}* Node;

и

Node nodeList = calloc(3, sizeof(struct _node));

вы указываете nodeList на первый элемент массива struct _node элементов.

С

typedef struct _node{
        int nodeNumber;
        int weight;
}** Node;

, тогда nodeList будет указателем на первый элемент массива из указателей на struct _node, но так как вы используете calloc все эти указатели в массиве будет NULL. Разыменование указателя NULL недопустимо и приводит к неопределенному поведению .

Кроме того, ваше распределение неверно, поскольку вы все еще выделяете память для трех struct _node элементов вместо struct _node * элементов.

И, как правило: никогда не скрывайте указатели за псевдонимами. Это затрудняет чтение, понимание и поддержку кода.

...