是否可以使用
malloc
拥有多种类型的数组?
编辑:
目前我有:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define int(x) *((int *) x)
int main() {
void *a[10];
a[0] = malloc(sizeof(int));
int(a[0]) = 4;
char *b = "yola.";
a[1] = malloc(strlen(b)*sizeof(char));
a[1] = b;
printf("%d\n", int(a[0]));
printf("%s\n", a[1]);
}
但是很乱。其他方式?
编辑:稍微清理一下。
确切地说,你不能拥有不同类型的数组。但您可以通过多种不同的方式实现类似的效果(至少对于某些目的)。
如果你只想将几个不同类型的值打包在一起,但值的数量和类型不变,你只需要一个
struct
并可以通过名称访问它们:
struct s_item {
int number;
char str[100];
} item;
item.number = 5;
strcpy(item.str,"String less than 100 chars");
如果您知道可能使用什么类型,则可以创建一个联合或包含联合的结构,以便可以用类型标记它。然后您可以创建一个数组。
type
成员可让您稍后检查每个数组元素中存储的内容。
enum ElementType { et_str, et_int, et_dbl };
struct Element {
ElementType type;
union {
char *str;
int i;
double d;
}
};
struct Element *arr = malloc(sizeof(struct Element) * 3);
arr[0].type = et_str;
arr[0].str = strdup("String value"); /* remember to free arr[0].str */
arr[1].type = et_int;
arr[1].i = 5;
arr[2].type = et_dbl;
arr[2].d = 27.3;
/* access the values.. */
for (int i = 0; i < 3; i++) {
switch(arr[i].type) {
case et_str: printf("String: %s\n",arr[i].str); break;
case et_int: printf("Integer: %d\n",arr[i].i); break;
case et_dbl: printf("Double: %f\n",arr[i].d); break;
}
}
/* The strings are dynamically allocated, so free the strings */
for (int i = 0; i < 3; i++)
if (arr[0].type == et_str) free(arr[0].str);
/* free the malloc'ed array */
free(arr);
/* etc., etc. */
这种方法可能会浪费空间,因为:
如果您有另一种方法来了解每个元素中存储的类型,则可以仅使用裸联合,而不用结构体包装它。这更紧凑一些,但每个元素仍然至少与联合中最大的类型一样大。
您还可以创建一个
void *
值数组。如果这样做,则必须以某种方式分配这些项目并将其地址分配给数组元素。然后,您需要将它们转换为适当的指针类型才能访问这些项目。 C 不提供任何运行时类型信息,因此无法从指针本身找出每个元素指向的数据类型 - 您必须自己跟踪。当您存储的类型很大并且它们的大小变化很大时,这种方法比其他方法更紧凑,因为每个类型都是与数组分开分配的,并且只能给出该类型所需的空间。对于简单类型,使用联合并没有真正获得任何好处。
void **arr = malloc(3 * sizeof(void *));
arr[0] = strdup("Some string"); /* is a pointer already */
arr[1] = malloc(sizeof(int));
*((int *)(arr[1])) = 5;
arr[2] = malloc(sizeof(double));
*((double *)(arr[2])) = 27.3;
/* access the values.. */
printf( "String: %s\n", (char *)(arr[0]) );
printf( "Integer: %d\n", *((int *)(arr[1])) );
printf( "Double: %f\n", *((double *)(arr[2])) );
/* ALL values were dynamically allocated, so we free every one */
for (int i = 0; i < 3; i++)
free(arr[i]);
/* free the malloc'ed array */
free(arr);
如果需要跟踪数组中的类型,还可以使用结构体来存储类型和指针,类似于前面使用联合的示例。同样,只有当存储的类型很大并且大小变化很大时,这才真正有用。
enum ElementType { et_str, et_int, et_dbl };
struct Element {
ElementType type;
void *data;
};
struct Element *arr = malloc(sizeof(struct Element) * 3);
arr[0].type = et_str;
arr[0].data = strdup("String value");
arr[1].type = et_int;
arr[1].data = malloc(sizeof(int));
*((int *)(arr[1].data)) = 5;
arr[2].type = et_dbl;
arr[2].data = malloc(sizeof(double));
*((double *)(arr[2].data)) = 27.3;
/* access the values.. */
for (int i = 0; i < 3; i++) {
switch(arr[i].type) {
case et_str: printf( "String: %s\n", (char *)(arr[0].data) ); break;
case et_int: printf( "Integer: %d\n", *((int *)(arr[1].data)) ); break;
case et_dbl: printf( "Double: %f\n", *((double *)(arr[2].data)) ); break;
}
}
/* again, ALL data was dynamically allocated, so free each item's data */
for (int i = 0; i < 3; i++)
free(arr[i].data);
/* then free the malloc'ed array */
free(arr);
您可以轻松拥有指向不同类型的指针数组。当然,要使其非常有用,您需要有某种方法来记录或确定每个元素当前引用的类型。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// This implicitly allocates memory to store 10 pointers
void *a[10];
// The first element will be a pointer to an int
// Allocate the memory it points to, then assign it a value.
a[0] = malloc(sizeof(int));
*( (int *)a[0] ) = 4;
// The second element will be a pointer to char; for simplicity,
// I'm hardcoding the length of the string + 1 for the null byte.
a[1] = malloc( 6*sizeof(char) );
strncpy( a[1], "hello", 5 );
printf( "%d\n", *( (int *)a[0] ) );
printf( "%s\n", a[1] );
}
不,所有元素必须属于同一类型。您可能会摆脱一系列结构。
struct mixed {
enum {
INTEGER,
STRING,
} type;
union {
int num;
char *str;
} value;
};
struct mixed v[10];
v[0].type = INTEGER;
v[0].value.num = 10;
我自己绝不会做这样的事(看起来很乱)。但是你的 array-of-void* 方法是相似的:你必须将类型的信息存储在某处。
我不确定你想要实现什么,但有两种可能性:
1 - 你实际上不需要一个数组,而是一个结构体:
struct {
int number;
char *string;
} a;
在这种情况下,您可以通过
a.number
访问数字,并通过 a.string
访问字符串。
2 - 您需要一个变体类型的数组。在 C 中,您可以对变体类型使用联合(最好是标记的):
struct Variant {
int type;
union {
int number;
char *string;
}
}
然后您可以使用 0 表示数字、1 表示字符串来对您的类型进行编码。使用枚举而不是整数作为类型当然是更好的方法。
这是因为您试图将一个值存储到需要指针的槽中。尝试以下操作(为简洁起见,省略错误检查)
int* pIntTemp = malloc(sizeof(int));
*pIntTemp = 4;
a[0] = pIntTemp;
最大的问题是让 C 编译器以不同的方式处理数组的每个元素。
我可以建议一种混合方法吗?
留出几个指针,每个指针都有其适当的结构定义。
当您决定想要哪种元素时,请使用该指向 malloc 和 setup 的指针,然后再使用。
然后将该指针的值复制到指针数组中。
稍后,当您想要使用该元素时,请将数组元素复制到适当的指针中以使编译器满意。
请记住,这只是一个示例,它有一些缺点,例如难以排序或在中间插入节点,但是...
例如:
struct this_type {
char mod_kind[20];
int this_int;
};
struct that_type {
char mod_kind[20];
char that_string[20];
};
void *list_o_pointers[10];
struct this_type *p_this;
struct that_type *p_that;
p_this = malloc(sizeof(struct this_type));
list_o_pointers[0] = p_this;
strcpy(p_this->mod_kind, "this kind"); // or whatever you want to use to differentate different types
p_that = malloc(sizeof(struct that_type));
list_o_pointers[0] = p_that;
strcpy(p_that->mod_kind, "that kind");
// later
p_this = list_o_pointers[0];
p_that = list_o_pointers[0];
if (strstr(p_this->mod_kind, "this kind")) { /* do this stuff */ }
if (strstr(p_that->mod_kind, "that kind")) { /* do that stuff */}
它解决了诸如必须强制转换 *((double *)(arr[2].data)) = 之类的麻烦,并且还有助于提高可读性。
如果您有许多不同的节点结构,这可能会失败。
这有点蛮力,但是(恕我直言)它对大脑来说更容易一些。该数组是一个简单数组,每个节点都是简单的。节点不需要像链表那样的“下一个”指针。
马克。
#include<stdlib.h>
#define int(x) *((int*)x)
#define float(x) *((float*)x)
#define double(x) *((float*)x)
#define char(x) x // for consistency
#define add_v(x, y) x = malloc(sizeof(y)); x = &y // Add void
#define add_vc(x, y) x = y //Add void char
#define cl_v(x) free(&x) // Clear void
#define cl_vc(x) free((char)x) // Clear void char
int main() {
int n = 4;
void *a[n];
enum {num, name, pay, payment};
int k = 5;
add_v(a[num], k);
char *b = "John";
add_vc(a[name], b);
float p = 13.45;
add_v(a[pay], p);
double o = 13054.5676;
add_v(a[payment], o);
printf("%d\n", int(a[num]));
printf("%s\n", char(a[name]));
printf("%f\n", float(a[pay]));
printf("%f\n", double(a[payment]));
cl_v(int(a[num]));
cl_vc(char(a[name]));
cl_v(float(a[pay]));
cl_v(double(a[payment]));
}
现在看起来不那么凌乱了。我使用枚举来跟踪变量名称。
如果任何宏错误,您可以更正它们,但语法看起来不错。