reservar punteros puntero para memoria matriz matrices estatica doble dinamica con arreglo c dynamic-arrays

para - punteros y matrices en c



Haciendo una matriz dinĂ¡mica que acepte cualquier tipo en C (5)

Ampliando esta respuesta con respecto a una solución de polimorfismo, también podemos hacer que incluya tipos de punteros o tipos definidos por el usuario. La principal ventaja con este método es deshacerse de la enumeración del "tipo de datos" y con ella todas las declaraciones del interruptor de verificación en tiempo de ejecución.

variante.h

#ifndef VARIANT_H #define VARIANT_H #include <stdio.h> #include <stdint.h> typedef void print_data_t (const void* data); typedef void print_type_t (void); typedef struct { void* data; print_data_t* print_data; print_type_t* print_type; } variant_t; void print_data_char (const void* data); void print_data_short (const void* data); void print_data_int (const void* data); void print_data_ptr (const void* data); void print_data_nothing (const void* data); void print_type_char (void); void print_type_short (void); void print_type_int (void); void print_type_int_p (void); void print_type_void_p (void); void print_type_void_f_void (void); void print_data (const variant_t* var); void print_type (const variant_t* var); #define variant_init(var) { / .data = &var, / / .print_data = _Generic((var), / char: print_data_char, / short: print_data_short, / int: print_data_int, / int*: print_data_ptr, / void*: print_data_ptr, / void(*)(void): print_data_nothing), / / .print_type = _Generic((var), / char: print_type_char, / short: print_type_short, / int: print_type_int, / int*: print_type_int_p, / void*: print_type_void_p, / void(*)(void): print_type_void_f_void) / } #endif /* VARIANT_H */

variante.c

#include "variant.h" void print_data_char (const void* data) { printf("%c", *(const char*) data); } void print_data_short (const void* data) { printf("%hd", *(const short*) data); } void print_data_int (const void* data) { printf("%d", *(const int*) data); } void print_data_ptr (const void* data) { printf("%p", data); } void print_data_nothing (const void* data) {} void print_type_char (void) { printf("char"); } void print_type_short (void) { printf("short"); } void print_type_int (void) { printf("int"); } void print_type_int_p (void) { printf("int*"); } void print_type_void_p (void) { printf("void*"); } void print_type_void_f_void (void) { printf("void(*)(void)"); } void print_data (const variant_t* var) { var->print_data(var->data); } void print_type (const variant_t* var) { var->print_type(); }

C Principal

#include <stdio.h> #include "variant.h" int main (void) { char c = ''A''; short s = 3; int i = 5; int* iptr = &i; void* vptr= NULL; void (*fptr)(void) = NULL; variant_t var[] = { variant_init(c), variant_init(s), variant_init(i), variant_init(iptr), variant_init(vptr), variant_init(fptr) }; for(size_t i=0; i<sizeof var / sizeof *var; i++) { printf("Type: "); print_type(&var[i]); printf("/tData: "); print_data(&var[i]); printf("/n"); } return 0; }

Salida:

Type: char Data: A Type: short Data: 3 Type: int Data: 5 Type: int* Data: 000000000022FD98 Type: void* Data: 000000000022FDA0 Type: void(*)(void) Data:

Las desventajas con _Generic para este propósito es que nos impide utilizar la encapsulación privada, ya que debe usarse como macro para pasar información de tipo.

Por otro lado, la "variante" en este caso debe mantenerse para todos los tipos nuevos que se presenten, por lo que no es tan práctico o genérico.

Aún así, estos trucos son buenos para saber para varios propósitos similares.

Estoy intentando encontrar una manera de hacer una estructura para mantener una matriz dinámica que pueda funcionar con cualquier tipo de datos (incluidos los tipos de datos definidos por el usuario), hasta ahora esto es lo que se me ocurrió.

#define Vector(DATATYPE) struct { DATATYPE* data; size_t size; size_t used; } typedef Vector(int) int_Vector; int main(int argc, char* argv[]){ int_Vector vec; return 0; }

Mientras esto funciona, me preguntaba, ¿es esta buena práctica? ¿Debería estar haciendo algo como esto o hay un método mejor? También hay un método para implementar esto sin la parte typedef Vector(int) int_vector . Básicamente, una forma que me permitiría usar la matriz de la misma manera que c ++ usa plantillas donde se vería algo como esto:

#define Vector(DATATYPE) struct { DATATYPE* data; size_t size; size_t used; } int main(int argc, char* argv[]){ Vector(int) vec; return 0; }

Principalmente para evitar tantos typedefs y tenerlo todo bajo un nombre.


Bueno, no, C no tiene un sistema de plantillas, así que no puedes usar uno.

Puede imitar los efectos con macros como lo hizo (una solución bastante inteligente), pero eso es un poco no estándar y requiere que los usuarios de su código aprendan la macro y sus limitaciones.

Normalmente el código C no lo intenta, ya que es tan incómodo.

El vector típico más "genérico" es algo como el GArray de glib, pero eso no pretende saber el tipo de cada elemento. En lugar de eso, el usuario debe preocuparse al acceder, y la matriz simplemente modela cada elemento como n bytes.

Hay _Generic() en C11 que podría ayudar un poco, honestamente no tengo mucha experiencia en eso.


El segundo ejemplo no funcionará porque las dos variables se definen como tipos distintos a pesar de que sus miembros son iguales. Por qué esto es así, está cubierto en mi respuesta existente .

Sin embargo, la sintaxis puede mantenerse igual utilizando un enfoque ligeramente diferente:

#include <stdlib.h> #define vector(type) struct vector_##type struct vector_int { int* array; size_t count; } ; int main(void) { vector(int) one = { 0 }; vector(int) two = { 0 }; one = two; ( void )one ; return 0; }

El uso sorprendentemente similar al vector<int> C ++ y un ejemplo completo se puede ver aquí:

#include <stdlib.h> #define vector_var(type) struct vector_##type struct vector_int { int* array; size_t count; }; void vector_int_Push( struct vector_int* object , int value ) { //implement it here } int vector_int_Pop( struct vector_int* object ) { //implement it here return 0; } struct vector_int_table { void( *Push )( struct vector_int* , int ); int( *Pop )( struct vector_int* ); } vector_int_table = { .Push = vector_int_Push , .Pop = vector_int_Pop }; #define vector(type) vector_##type##_table int main(void) { vector_var(int) one = { 0 }; vector_var(int) two = { 0 }; one = two; vector(int).Push( &one , 1 ); int value = vector(int).Pop( &one ); ( void )value; return 0; }


No está mal. Y no veo ninguna desventaja. Solo para explicar otro método, el más utilizado comúnmente en este caso usa union:

typedef union { int i; long l; float f; double d; /*(and so on)*/} vdata; typedef enum {INT_T,LONG_T,FLOAT_T, /*(and so on)*/} vtype; typedef struct { vtype t; vdata data } vtoken; typedef struct { vtoken *tk; size_t sz; size_t n; } Vector;

Así que esto es posible. La enumeración del tipo de datos, puede evitarse con typedefs, pero si usa mixta (ej: suma larga, para duplicar, para flotar y así sucesivamente) debe usarlas, ya que int + double no es igual a double + int; Esta es también una razón, porque es más fácil ver a los sindicatos hacer este trabajo. Dejas intactas todas las reglas aritméticas.


Vector(DATATYPE) struct { DATATYPE* data; size_t size; size_t used; } Vector(DATATYPE) struct { DATATYPE* data; size_t size; size_t used; } también falla para los punteros a las funciones.

void* es suficiente y está bien definido para un puntero a cualquier objeto , pero no así para un puntero a una función.

C permite que un puntero a una función de un tipo se guarde como un puntero a una función de otro tipo. Al usar una union de los dos a continuación, el código tiene suficiente espacio para guardar el puntero a cualquier tipo. La gestión de qué tipo y qué miembro utiliza permanece abierta.

union u_ptr { void *object; void (*function)(); }