Чистый ANSI-C: сделать общий массив - PullRequest
5 голосов
/ 12 декабря 2010

Можно ли реплицировать общий массив в чистом ANSI-C?

У меня есть эта структура, которая содержит массив (для чисел с плавающей точкой на данный момент) и некоторые переменные, такие как размер и емкость для мутации в массиве.

typedef struct _CustomArray
{
    float* array; //the array in which the objects will be stored
    int size; //the current size of the array
    int capacity; //the max capacity of the array
} CustomArray; 

Я использую эту структуру, чтобы я мог создать массив в чистом C, где я могу добавлять / удалять элементы, динамически увеличивать размер массива при необходимости и т. Д. Все, что делает «стандартный» массив, кромеэто сделано только на Си.И теперь я хочу сделать это так, чтобы при инициализации этой структуры вы могли установить тип данных элементов, которые он должен содержать, в данный момент он способен хранить только типы данных с плавающей точкой, но я хочу сделать так, чтобы он мог хранить любой тип данных./ другие структуры.Но я не знаю, возможно ли это вообще.

На данный момент функция для создания этого массива:

CustomArray* CustomArray_Create(int initCapacity, /*type elementType*/)
{
    CustomArray* customArray_ptr; //create pointer to point at the structure
    float* internalArray = (float*)malloc(sizeof(float) * initCapacity); //create the internal array that holds the items
    if(internalArray != NULL)
    {
        CustomArray customArray = { internalArray, 0, initCapacity }; //make the struct with the data
        customArray_ptr = &customArray; //get the adress of the structure and assign it to the pointer
        return customArray_ptr; //return the pointer
    }
    return NULL;
}

Можно ли дать тип данных в качестве параметра, чтобы я могmalloc памяти для этого типа данных и динамически преобразовать его в заданный тип данных в массиве?

Заранее спасибо,

Marnix van Rijswijk

Ответы [ 5 ]

8 голосов
/ 12 декабря 2010

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

О том, чтобы сделать тип параметром, вы можете немного приблизиться с помощью макросов ... например, что-то вроде:

#include <stdlib.h> 
#define DefArray(type) \
typedef struct T_##type##Array {\
    type *array; \
    int size, capacity; \
} type##Array; \
static type##Array *type##ArrayCreate(int capacity)\
{\
    type##Array *s = malloc(sizeof(type##Array));\
    if (!s) return NULL;\
    s->array = malloc(sizeof(type) * capacity);\
    if (!s->array) { free(s); return NULL; }\
    s->size=0; s->capacity = capacity;\
    return s;\
}

Тогда вы можете использовать его таким образом

#include "customarray.h"
DefArray(float);
DefArray(double);

void foo()
{
    floatArray *fa = floatArrayCreate(100);
    ...
}

Обратите внимание, что вы должны использовать макросы для определения всех ваших пользовательских функций. Также обратите внимание, что этот подход будет дублировать код в каждом модуле (я бы сказал, что это не большая проблема, но если вы не можете использовать C ++, вероятно, ваша целевая платформа довольно мала). При более сложном подходе вы можете создать отдельный файл .h и .c для реализации.

2 голосов
/ 12 декабря 2010

Одним из способов достижения этого является использование так называемых X-макросов .

Здесь - (возможно, с ошибками) универсальная векторная реализация, использующая эту технику.

Затем используется как

// defining generic parameters
#define PREFIX tv
#define ITEM token
#define NAME token_vector
#include "vector.h"

...
token_vector tv = tv_new(100);
*(tv.front) = some_token;
tv_push_back(&tv, other_token);
2 голосов
/ 12 декабря 2010

Мальчик, это действительно звучит как работа для C ++.

Я думаю, что самое близкое к этому в C это не передача типа, а его размер (sizeof (тип)).

Вы можете сделать вашу функцию более общей, чтобы она могла делать то, что ей нужно, если все, что она знает, это размер каждого элемента в массиве. Вот как работают такие функции, как bsearch ().

0 голосов
/ 07 мая 2019

Итак, есть понятие «эффективный тип» для объектов без собственного объявленного типа. (поскольку это вытряхивает, те в значительной степени состоят только из "другого конца указателя * alloc" и пары странных правил объединения) По сути, «эффективным типом» такого объекта является то, что вы в последний раз использовали для его назначения, не считая раз, которые были char или char[] по причинам.

Одно интересное взаимодействие связано с правилами объявления типов структур. А именно, вы можете свободно повторно объявлять одно и то же имя тега (или отсутствие имени тега), и каждое объявление вводит совершенно новый тип (прошлые имена затемняются, но объекты со старыми типами не интерпретируются повторно).

Так что вы можете сделать что-то вроде этого:

# define DECL_VECTOR(NAME,TYPE,SIZE) PUN_STRUCT(NAME,TYPE,SIZE)  INIT_STRUCT(NAME,TYPE,SIZE) 

# define PUN_SIZE sizeof(void*)+sizeof(int)*2


# define PUN_STRUCT(NAME,TYPE,SIZE)                      \
   struct {                                              \
      TYPE (*p)[(SIZE)];                                 \
      int size;                                          \
      int capacity;                                      \
   } *NAME = malloc(PUN_SIZE);                        


# define INIT_STRUCT(NAME,TYPE,SIZE)  do {               \
   if (!NAME) {                                          \
        perror("malloc fail");                           \
        abort();                                         \
   }else {                                               \
        NAME->size = (SIZE);                             \
        NAME->capacity = 0;                              \
        NAME->p = malloc(sizeof(*NAME->p));              \
        if (!NAME->p) {                                  \
            perror("malloc fail");                       \
            abort();                                     \
        }                                                \
        NAME->p = (TYPE(*)[(SIZE)])(NAME->p);            \
   }                                                     \
   }while(false)


int main(int argc, char *argv[]) 
 {

   DECL_VECTOR(vec1,int,8);


    printf("size of handle struct:  %zu,\n\
size of vector array:   %zu,\n\
size of vector element: %zu\n\
address of handle struct: %p,\n\
address of vector array:  %p\n",  sizeof(*vec1),       \
                                  sizeof(*vec1->p),    \
                                  sizeof(*vec1->p[0]), \
                                  vec1,                \
                                  vec1->p);
   free(vec1->p);
   free(vec1);
   return 0;
 }

(однако, люди могут обвинить вас в злоупотреблении вашими привилегиями макросов, и они могут не ошибаться)

0 голосов
/ 02 сентября 2015

Я возился с общим программированием на C несколько лет назад, просто ради этого.

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

То, чего я определенно НЕ ДЕЛАЛ (по крайней мере, любым автоматическим способом), - это рекурсивный запуск макросов -т.е. создание массива массивов или массива хэшей и т. д. Это связано с интересной кашель сумасшедший кашель семантика макросов препроцессора C.

ЕслиВы заинтересованы, вот код: https://github.com/christianfriedl/CGenerics/blob/master/src/cgArray.h

...