ОК, я свел к минимуму то, что вы пытались сделать в своем примере. Одна проблема с логами c, с которой вы столкнулись, находится в initGraph
. Там вы хотите выделить struct Graph
, а не struct Vertices
или struct Edge **adjMat;
. Если вы не добавляете значение ни для одного из них, просто установите эти указатели на NULL
до тех пор, пока вам фактически не потребуется выделить их для обоих.
Во-первых, давайте переупорядочим ваши определения struct
, чтобы любая структура требовала как часть другой определяется до того, как она потребуется, например,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
struct Vertices {
int id;
char name[15];
float xPos;
float yPos;
};
struct Edge {
int id;
struct Vertices *start;
struct Vertices *end;
};
struct Graph {
int VertexCounter;
struct Vertices *vertex;
struct Edge **adjMat;
};
( примечание: ваш экземпляр MyGraph
удален - это не нужно, если вы выделяете для graph
)
Давайте перейдем вперед и посмотрим, как ваш указатель будет обрабатываться в main()
, например,
Изменить измененный параметр на addVertex
Обратите внимание, что во всем приведенном ниже коде параметр для addVertex
был изменен с struct Graph **graph
на struct Graph *graph
, поскольку нет перераспределения graphpointer
в addVertex
.
int main (void) {
struct Graph *graphPointer = NULL;
initGraph (&graphPointer);
test(graphPointer);
addVertex (graphPointer);
test(graphPointer);
freeGraph (graphPointer); /* don't forget to free what you allocate */
}
Выше, graphpointer
- единственный необходимый указатель. Затем вы передаете адрес graphpointer
на initGraph
, чтобы initGraph
мог работать с оригиналом, а выделение в initGraph
было доступно обратно в main()
.
Чтобы это произошло, Вы можете сделать следующее в initGraph
(примечание: выделение для любых других участников в struct Graph
задерживается до тех пор, пока вам действительно не будет чего добавить). Если вы попытаетесь предварительно выделить что-то, что не произойдет до тех пор, пока в вашем коде не появится неизвестная точка - вы создадите рецепт для катастрофы. Выделите то, что вам нужно, когда вам это нужно. (это не мешает вам выделять для каждого блока и хранить счетчик для уменьшения необходимого количества realloc
)
Ваш initGraph
становится:
void initGraph (struct Graph **graph)
{
*graph = calloc (1, sizeof **graph);
if (!*graph) { /* validate EVERY allocation */
perror ("calloc-*graph");
exit (EXIT_FAILURE);
}
(*graph)->vertex = NULL; /* set pointers NULL until you add_vertex */
(*graph)->adjMat = NULL; /* or you add_adjmat */
(*graph)->VertexCounter = 0; /* initialize counter */
}
Выше вы инициализировали один struct Graph
- больше ничего - как и должно быть. В то время как с initGraph
вы должны были передать адрес graphpointer
, чтобы распределение можно было увидеть в main()
, с addVertex
все, что вам нужно передать, это graphpointer
- потому что адрес этого указателя делает НЕ изменяется в addVertex
. Все, что вы делаете, это выделяете элемент struct Graph
в graphpointer
, который можно просто назначить указателю vertex
. Теперь, когда вы готовы добавить вершину, вы можете сделать:
void addVertex (struct Graph *graph)
{
/* always realloc using a temporary pointer */
void *tmp = realloc (graph->vertex, (graph->VertexCounter + 1) *
sizeof *graph->vertex);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-(*graph)->vertex");
exit (EXIT_FAILURE);
}
graph->vertex = tmp;
graph->VertexCounter++;
}
Также отметьте в addVertex
, что все, над чем вы работаете, это struct Vertices
член graphpointer
- также как и должно быть. Если у вас есть вершина для добавления в этой точке, рассмотрите возможность передачи ее в качестве параметра (или данных в качестве параметров), чтобы данные вершины можно было назначать / копировать в addVertex
, чтобы хранить весь код добавления-вершины в одном месте. .
В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанностей в отношении любого выделенного блока памяти: (1) всегда сохраняется указатель на начальный адрес для блока памяти, таким образом, (2) он может быть освобожден , когда он больше не нужен. Быстрая функция freeGraph
для free
, которая была выделена на данный момент, может быть:
void freeGraph (struct Graph *graph)
{
free (graph->vertex);
free (graph);
}
(когда вы добавляете выделение для adjMat
, не забудьте добавить соответствующий free
в freeGraph
)
Теперь, в целом, вы можете сделать:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
struct Vertices {
int id;
char name[15];
float xPos;
float yPos;
};
struct Edge {
int id;
struct Vertices *start;
struct Vertices *end;
};
struct Graph {
int VertexCounter;
struct Vertices *vertex;
struct Edge **adjMat;
};
void initGraph (struct Graph **graph)
{
*graph = calloc (1, sizeof **graph);
if (!*graph) { /* validate EVERY allocation */
perror ("calloc-*graph");
exit (EXIT_FAILURE);
}
(*graph)->vertex = NULL; /* set pointers NULL until you add_vertex */
(*graph)->adjMat = NULL; /* or you add_adjmat */
(*graph)->VertexCounter = 0; /* initialize counter */
}
void test (struct Graph *graph)
{
printf("Number of Vertices: %d\n", graph->VertexCounter);
}
void addVertex (struct Graph *graph)
{
/* always realloc using a temporary pointer */
void *tmp = realloc (graph->vertex, (graph->VertexCounter + 1) *
sizeof *graph->vertex);
if (!tmp) { /* validate EVERY reallocation */
perror ("realloc-(*graph)->vertex");
exit (EXIT_FAILURE);
}
graph->vertex = tmp;
graph->VertexCounter++;
}
void freeGraph (struct Graph *graph)
{
free (graph->vertex);
free (graph);
}
int main (void) {
struct Graph *graphPointer = NULL;
initGraph (&graphPointer);
test(graphPointer);
addVertex (graphPointer);
test(graphPointer);
freeGraph (graphPointer); /* don't forget to free what you allocate */
}
/* move to add_adjmat functions:
(*graph)->adjMat = calloc (1, sizeof *(*graph)->adjMat);
if (!(*graph)->adjMat) {
perror ("calloc-(*graph)->adjMat");
exit (EXIT_FAILURE);
}
*(*graph)->adjMat = calloc (1, sizeof **(*graph)->adjMat);
*/
( примечание: комментарии внизу к выделению для adjMat
к какой функции будет иметь отношение add_adjmat()
, et c ..)
Пример использования / Вывод
$ ./bin/graphalloc
Number of Vertices: 0
Number of Vertices: 1
Использование памяти / проверка ошибок
Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределами / за пределами выделенного блока, пытаться прочитать или основать условный переход на неинициализированное значение и, наконец, подтверждение того, что вы освобождаете всю выделенную память.
Для Linux valgrind
это нормальный выбор. Есть похожие проверки памяти для каждой платформы. Все они просты в использовании, просто запустите вашу программу через него.
$ valgrind --leak-check=full ./bin/graphalloc
==19442== Memcheck, a memory error detector
==19442== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19442== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19442== Command: ./bin/graphalloc
==19442==
Number of Vertices: 0
Number of Vertices: 1
==19442==
==19442== HEAP SUMMARY:
==19442== in use at exit: 0 bytes in 0 blocks
==19442== total heap usage: 3 allocs, 3 frees, 1,076 bytes allocated
==19442==
==19442== All heap blocks were freed -- no leaks are possible
==19442==
==19442== For counts of detected and suppressed errors, rerun with: -v
==19442== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Это подход, который позволит вам добавить то, что вам нужно, когда вам нужно. Обратите внимание, чтобы минимизировать перераспределение, вы можете добавить VertexAllocated
член и выделить начальный блок 8, 16, 32, etc..
, а затем, когда VertexCounter == VertexAllocated
, вы можете позвонить realloc
тогда и realloc
2 раза VertexAllocated
и продолжать.
Посмотрите вещи и дайте мне знать, если у вас есть вопросы.