Malloc - массив структурных указателей
У меня есть следующая структура:
typedef struct _chess {
int **array;
int size;
struct _chess *parent;
} chess;
и у меня есть:
typedef struct _chess *Chess;
Теперь я хочу создать массив динамической длины для хранения указателей на шахматную структуру, поэтому я делаю следующее:
Chess array [] = malloc(size * sizeof(Chess));
Это дает мне ошибку: недействительный инициализатор.
И если я уронил [] и сделаю следующее:
Chess array = malloc(size * sizeof(Chess));
он компилируется без ошибок, но когда я пытаюсь установить элемент этого массива в NULL, сделав:
array[i]=NULL;
Я получаю сообщение об ошибке: несовместимые типы при назначении типа 'struct _chess from type' void *
Любая идея, что я делаю неправильно?
Спасибо.
Ответы
Ответ 1
array
- немного вводящее в заблуждение имя. Для динамически выделенного массива указателей malloc
вернет указатель на блок памяти. Вам нужно использовать Chess*
, а не Chess[]
, чтобы удерживать указатель на ваш массив.
Chess *array = malloc(size * sizeof(Chess));
array[i] = NULL;
и, возможно, позже:
/* create new struct chess */
array[i] = malloc(sizeof(struct chess));
/* set up its members */
array[i]->size = 0;
/* etc. */
Ответ 2
Здесь много typedef
. Лично я против "сокрытия звездочки", т.е. typedef
: включение типов указателей во что-то, что не похоже на указатель. В C указатели очень важны и действительно влияют на код, существует большая разница между foo
и foo *
.
Многие из ответов также смущены об этом, я думаю.
Ваше выделение массива значений Chess
, которые являются указателями на значения типа Chess
(опять же, очень запутанная номенклатура, которую я действительно не могу рекомендовать) должна быть такой:
Chess *array = malloc(n * sizeof *array);
Затем вам нужно инициализировать фактические экземпляры путем циклизации:
for(i = 0; i < n; ++i)
array[i] = NULL;
Это предполагает, что вы не хотите выделять какую-либо память для экземпляров, вы просто хотите, чтобы массив указателей со всеми указателями изначально указывал ни на что.
Если вы хотите выделить пространство, простейшей формой будет:
for(i = 0; i < n; ++i)
array[i] = malloc(sizeof *array[i]);
Посмотрите, как использование sizeof
соответствует 100% и никогда не упоминает явные типы. Используйте информацию о типе, присущую вашим переменным, и пусть компилятор беспокоится о том, какой тип есть. Не повторяйте себя.
Конечно, вышесказанное делает бесполезно большое количество вызовов malloc()
; в зависимости от шаблонов использования можно было бы сделать все вышеперечисленное только с одним вызовом malloc()
после вычисления необходимого общего размера. Тогда вам все равно нужно пройти и инициализировать указатели array[i]
, чтобы указать на большой блок, конечно.
Ответ 3
ИМХО, это выглядит лучше:
Chess *array = malloc(size * sizeof(Chess)); // array of pointers of size `size`
for ( int i =0; i < SOME_VALUE; ++i )
{
array[i] = (Chess) malloc(sizeof(Chess));
}
Ответ 4
Я согласен с @maverik выше, я предпочитаю не скрывать детали с typedef. Особенно, когда вы пытаетесь понять, что происходит. Я также предпочитаю видеть все вместо частичного фрагмента кода. С учетом сказанного здесь находится malloc и не имеет сложной структуры.
В коде используется детектор течей ms visual studio, поэтому вы можете поэкспериментировать с потенциальными утечками.
#include "stdafx.h"
#include <string.h>
#include "msc-lzw.h"
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
// 32-bit version
int hash_fun(unsigned int key, int try_num, int max) {
return (key + try_num) % max; // the hash fun returns a number bounded by the number of slots.
}
// this hash table has
// key is int
// value is char buffer
struct key_value_pair {
int key; // use this field as the key
char *pValue; // use this field to store a variable length string
};
struct hash_table {
int max;
int number_of_elements;
struct key_value_pair **elements; // This is an array of pointers to mystruct objects
};
int hash_insert(struct key_value_pair *data, struct hash_table *hash_table) {
int try_num, hash;
int max_number_of_retries = hash_table->max;
if (hash_table->number_of_elements >= hash_table->max) {
return 0; // FULL
}
for (try_num = 0; try_num < max_number_of_retries; try_num++) {
hash = hash_fun(data->key, try_num, hash_table->max);
if (NULL == hash_table->elements[hash]) { // an unallocated slot
hash_table->elements[hash] = data;
hash_table->number_of_elements++;
return RC_OK;
}
}
return RC_ERROR;
}
// returns the corresponding key value pair struct
// If a value is not found, it returns null
//
// 32-bit version
struct key_value_pair *hash_retrieve(unsigned int key, struct hash_table *hash_table) {
unsigned int try_num, hash;
unsigned int max_number_of_retries = hash_table->max;
for (try_num = 0; try_num < max_number_of_retries; try_num++) {
hash = hash_fun(key, try_num, hash_table->max);
if (hash_table->elements[hash] == 0) {
return NULL; // Nothing found
}
if (hash_table->elements[hash]->key == key) {
return hash_table->elements[hash];
}
}
return NULL;
}
// Returns the number of keys in the dictionary
// The list of keys in the dictionary is returned as a parameter. It will need to be freed afterwards
int keys(struct hash_table *pHashTable, int **ppKeys) {
int num_keys = 0;
*ppKeys = (int *) malloc( pHashTable->number_of_elements * sizeof(int) );
for (int i = 0; i < pHashTable->max; i++) {
if (NULL != pHashTable->elements[i]) {
(*ppKeys)[num_keys] = pHashTable->elements[i]->key;
num_keys++;
}
}
return num_keys;
}
// The dictionary will need to be freed afterwards
int allocate_the_dictionary(struct hash_table *pHashTable) {
// Allocate the hash table slots
pHashTable->elements = (struct key_value_pair **) malloc(pHashTable->max * sizeof(struct key_value_pair)); // allocate max number of key_value_pair entries
for (int i = 0; i < pHashTable->max; i++) {
pHashTable->elements[i] = NULL;
}
// alloc all the slots
//struct key_value_pair *pa_slot;
//for (int i = 0; i < pHashTable->max; i++) {
// // all that he could see was babylon
// pa_slot = (struct key_value_pair *) malloc(sizeof(struct key_value_pair));
// if (NULL == pa_slot) {
// printf("alloc of slot failed\n");
// while (1);
// }
// pHashTable->elements[i] = pa_slot;
// pHashTable->elements[i]->key = 0;
//}
return RC_OK;
}
// This will make a dictionary entry where
// o key is an int
// o value is a character buffer
//
// The buffer in the key_value_pair will need to be freed afterwards
int make_dict_entry(int a_key, char * buffer, struct key_value_pair *pMyStruct) {
// determine the len of the buffer assuming it is a string
int len = strlen(buffer);
// alloc the buffer to hold the string
pMyStruct->pValue = (char *) malloc(len + 1); // add one for the null terminator byte
if (NULL == pMyStruct->pValue) {
printf("Failed to allocate the buffer for the dictionary string value.");
return RC_ERROR;
}
strcpy(pMyStruct->pValue, buffer);
pMyStruct->key = a_key;
return RC_OK;
}
// Assumes the hash table has already been allocated.
int add_key_val_pair_to_dict(struct hash_table *pHashTable, int key, char *pBuff) {
int rc;
struct key_value_pair *pKeyValuePair;
if (NULL == pHashTable) {
printf("Hash table is null.\n");
return RC_ERROR;
}
// Allocate the dictionary key value pair struct
pKeyValuePair = (struct key_value_pair *) malloc(sizeof(struct key_value_pair));
if (NULL == pKeyValuePair) {
printf("Failed to allocate key value pair struct.\n");
return RC_ERROR;
}
rc = make_dict_entry(key, pBuff, pKeyValuePair); // a_hash_table[1221] = "abba"
if (RC_ERROR == rc) {
printf("Failed to add buff to key value pair struct.\n");
return RC_ERROR;
}
rc = hash_insert(pKeyValuePair, pHashTable);
if (RC_ERROR == rc) {
printf("insert has failed!\n");
return RC_ERROR;
}
return RC_OK;
}
void dump_hash_table(struct hash_table *pHashTable) {
// Iterate the dictionary by keys
char * pValue;
struct key_value_pair *pMyStruct;
int *pKeyList;
int num_keys;
printf("i\tKey\tValue\n");
printf("-----------------------------\n");
num_keys = keys(pHashTable, &pKeyList);
for (int i = 0; i < num_keys; i++) {
pMyStruct = hash_retrieve(pKeyList[i], pHashTable);
pValue = pMyStruct->pValue;
printf("%d\t%d\t%s\n", i, pKeyList[i], pValue);
}
// Free the key list
free(pKeyList);
}
int main(int argc, char *argv[]) {
int rc;
int i;
struct hash_table a_hash_table;
a_hash_table.max = 20; // The dictionary can hold at most 20 entries.
a_hash_table.number_of_elements = 0; // The intial dictionary has 0 entries.
allocate_the_dictionary(&a_hash_table);
rc = add_key_val_pair_to_dict(&a_hash_table, 1221, "abba");
if (RC_ERROR == rc) {
printf("insert has failed!\n");
return RC_ERROR;
}
rc = add_key_val_pair_to_dict(&a_hash_table, 2211, "bbaa");
if (RC_ERROR == rc) {
printf("insert has failed!\n");
return RC_ERROR;
}
rc = add_key_val_pair_to_dict(&a_hash_table, 1122, "aabb");
if (RC_ERROR == rc) {
printf("insert has failed!\n");
return RC_ERROR;
}
rc = add_key_val_pair_to_dict(&a_hash_table, 2112, "baab");
if (RC_ERROR == rc) {
printf("insert has failed!\n");
return RC_ERROR;
}
rc = add_key_val_pair_to_dict(&a_hash_table, 1212, "abab");
if (RC_ERROR == rc) {
printf("insert has failed!\n");
return RC_ERROR;
}
rc = add_key_val_pair_to_dict(&a_hash_table, 2121, "baba");
if (RC_ERROR == rc) {
printf("insert has failed!\n");
return RC_ERROR;
}
// Iterate the dictionary by keys
dump_hash_table(&a_hash_table);
// Free the individual slots
for (i = 0; i < a_hash_table.max; i++) {
// all that he could see was babylon
if (NULL != a_hash_table.elements[i]) {
free(a_hash_table.elements[i]->pValue); // free the buffer in the struct
free(a_hash_table.elements[i]); // free the key_value_pair entry
a_hash_table.elements[i] = NULL;
}
}
// Free the overall dictionary
free(a_hash_table.elements);
_CrtDumpMemoryLeaks();
return 0;
}