指向多种类型的指针数组,C

发布于 2024-12-10 13:10:02 字数 494 浏览 2 评论 0原文

使用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]);
}

但很混乱。其他方式?

编辑:清理了一下。

Is it possible to have an array of multiple types by using malloc?

EDIT:

Currently I have:

#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]);
}

But it's messy. Other ways?

EDIT: Cleaned it up a bit.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

枕梦 2024-12-17 13:10:02

确切地说,您不能拥有不同类型的数组。但是您可以通过多种不同的方式实现类似的效果(至少对于某些目的)。

如果您只想将几个不同类型的值打包在一起,但值的数量和类型不变,您只需要一个 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. */

这种方法可能会浪费空间,因为:

  • 每个元素都有一个额外的值来跟踪它所保存的数据类型
  • 结构体的成员之间可能有额外的填充
  • 联合中的类型可能有不同的大小,在这种情况下联合将如下Large 作为最大类型

如果您有另一种方法来了解每个元素中存储的类型,则可以仅使用裸联合而不用结构体包装它。这更紧凑一些,但每个元素仍然至少与联合中最大的类型一样大。


您还可以创建一个 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);

You can't have an array of different types, exactly. But you can achieve a similar effect (for some purposes at least) in a number of different ways.

If you just want a few values of different types packaged together, but the number and types of values don't change, you just need a struct and can access them by name:

struct s_item {
  int     number;
  char    str[100];
} item;
item.number = 5;
strcpy(item.str,"String less than 100 chars");

If you know what types you might use, you can create a union, or a struct containing a union so you can tag it with the type. You can then create an array of those. The type member lets you check to see what you stored in each array element later.

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. */

This approach may waste space because:

  • Each element has an extra value to keep track of the type of data it holds
  • The struct may have extra padding between its members
  • The types in the union may be different sizes, in which case the union will be as large as the largest type

If you have another way of knowing what type you've stored in each element, you can use just the bare union without the struct wrapping it. This is a little more compact, but each element will still be at least as large as the largest type in the union.


You can also create an array of void * values. If you do this, you'll have to allocate the items somehow and assign their addresses to the array elements. Then you'll need to cast them to the appropriate pointer type to access the items. C doesn't provide any runtime type information, so there's no way to find out what type of data each element points at from the pointer itself -- you must keep track of that on your own. This approach is a lot more compact than the others when the types you're storing are large and their sizes vary a lot, since each is allocated separately from the array and can be given only the space needed for that type. For simple types, you don't really gain anything over using a union.

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);

If you need to keep track of the type in the array, you can also use a struct to store the type along with the pointer, similar to the earlier example with the union. This, again, is only really useful when the types being stored are large and vary a lot in size.

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);
梦归所梦 2024-12-17 13:10:02

您可以轻松拥有指向不同类型的指针数组。当然,要使其非常有用,您需要有某种方法来记录或确定每个元素当前引用的类型。

#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] );

}

You can easily have an array of pointers that point to different types. Of course for it to be very useful, you'd need to have some way of recording or determining what type is currently referenced by each element.

#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] );

}
噩梦成真你也成魔 2024-12-17 13:10:02

不,所有元素必须属于同一类型。您可能会摆脱一系列结构的困扰。

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* 方法是类似的:你必须将类型的信息存储在某处。

No, all the elements have to be of the same type. You might get away with an array of structures.

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;

I myself would never do such a thing (seems messy). But your array-of-void* approach is similar: you have to store the information on the type somewhere.

悲歌长辞 2024-12-17 13:10:02

我不确定您想要实现什么,但有两种可能性:

1 - 您实际上不需要数组,而是结构:

struct {
    int number;
    char *string;
} a;

在这种情况下,您可以将数字作为 a.number 访问,并且字符串为 a.string

2 - 您需要一个变体类型的数组。在 C 中,您可以对变体类型使用联合(最好是标记的):

struct Variant {
    int type;
    union {
        int number;
        char *string;
    }
}

然后您可以使用 0 表示数字、1 表示字符串来编码您的类型。使用枚举而不是整数作为类型当然是更好的方法。

I'm not sure what you want to achieve but there are two possibilities:

1 - You don't actually want an array but a struct:

struct {
    int number;
    char *string;
} a;

In this case you can access the number as a.number and the string as a.string.

2 - You want an array of variant type. In C, you can use unions (preferably tagged) for variant types:

struct Variant {
    int type;
    union {
        int number;
        char *string;
    }
}

Then you can encode your type with 0 for number and 1 for string. Using an enum instead of integer for the type would be a better way of course.

无可置疑 2024-12-17 13:10:02

这是因为您试图将一个值存储到需要指针的槽中。尝试以下操作(为简洁起见,省略了错误检查)

int* pIntTemp = malloc(sizeof(int));
*pIntTemp = 4;
a[0] = pIntTemp;

It's because you're trying to store a value into a slot which is expecting a pointer. Try the following (error checking omitted for brevity)

int* pIntTemp = malloc(sizeof(int));
*pIntTemp = 4;
a[0] = pIntTemp;
残月升风 2024-12-17 13:10:02

最大的问题是让 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)) = 并且也有助于提高可读性。

如果您有许多不同的节点结构,这可能会失败。

这有点蛮力,但是(恕我直言)它对大脑来说更容易一些。该数组是一个简单数组,每个节点都是简单的。节点不需要像链表那样的“下一个”指针。

标记。

The bigest issue is getting the C compiler to treat each element of the array differently.

Might I suggest a hybrid approach.

Set aside several pointers, each with their appropriate structure definitions.

When you decide which kind of element you want, use that pointer to malloc and setup, then later use.

Then copy the value of that pointer into the array of pointers.

Later, when you want to use that element, copy the array element into it's aproprate pointer to make the compiler happy.

Please keep in mind, this is only an example, it has some short commings like difficulty of sorting or inserting a node in the middle, but...

For example:

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 */}

it solves the ulgyness of stuff like having to cast *((double *)(arr[2].data)) = and also helps with readability.

This may break down if you have many different node structures.

It is a bit brute force, but (IMHO) it's a little easier on the brain. The array is a simple array and each node is simple. The nodes have no need for a "next" pointer like a linked list has.

Mark.

人心善变 2024-12-17 13:10:02
#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]));
}

现在看起来不那么凌乱了。我使用枚举来跟踪变量名称。

如果任何宏错误,您可以更正它们,但语法看起来不错。

#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]));
}

Now it looks less messy. I used an enum to keep track of variable names.

If any macros are wrong, you can correct them, but the syntax looks fine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文