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

问题描述 投票:0回答:7

是否可以使用

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

但是很乱。其他方式?

编辑:稍微清理一下。

c arrays pointers void
7个回答
26
投票

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

如果你只想将几个不同类型的值打包在一起,但值的数量和类型不变,你只需要一个

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

4
投票

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

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

}

3
投票

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

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
投票

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

1 - 你实际上不需要一个数组,而是一个结构体:

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

在这种情况下,您可以通过

a.number
访问数字,并通过
a.string
访问字符串。

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

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

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


0
投票

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

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

0
投票

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

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

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

马克。


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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.