Pertanyaan Array POINTERS ke Beberapa Jenis, C


Apakah mungkin untuk memiliki berbagai macam tipe dengan menggunakan malloc?

EDIT:

Saat ini saya punya:

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

Tapi ini berantakan. Cara lain?

EDIT: Bersihkan sedikit.


4
2017-10-17 19:14


asal


Jawaban:


Anda tidak dapat memiliki berbagai jenis, tepatnya. Tetapi Anda dapat mencapai efek yang serupa (setidaknya untuk beberapa tujuan) dalam beberapa cara yang berbeda.

Jika Anda hanya ingin beberapa nilai dari berbagai jenis yang dikemas bersama, tetapi jumlah dan jenis nilai tidak berubah, Anda hanya perlu struct dan dapat mengaksesnya dengan nama:

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

Jika Anda tahu jenis apa yang mungkin Anda gunakan, Anda dapat membuat penyatuan, atau struct yang mengandung penyatuan sehingga Anda dapat menandainya dengan jenis. Anda kemudian dapat membuat array dari mereka. Itu type anggota memungkinkan Anda memeriksa untuk melihat apa yang Anda simpan di setiap elemen larik nanti.

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

Pendekatan ini mungkin membuang-buang ruang karena:

  • Setiap elemen memiliki nilai ekstra untuk melacak jenis data yang dimilikinya
  • Struct mungkin memiliki padding ekstra antara anggotanya
  • Jenis-jenis di serikat mungkin berbeda ukuran, dalam hal ini serikat akan menjadi besar sebagai tipe terbesar

Jika Anda memiliki cara lain untuk mengetahui jenis apa yang telah Anda simpan di setiap elemen, Anda dapat menggunakan hanya serikat tanpa struct yang membungkusnya. Ini sedikit lebih ringkas, tetapi setiap elemen masih akan setidaknya sebesar jenis terbesar dalam penyatuan.


Anda juga bisa membuat array void * nilai-nilai. Jika Anda melakukan ini, Anda harus mengalokasikan item entah bagaimana dan menetapkan alamat mereka ke elemen array. Kemudian Anda harus mentransmisikannya ke jenis pointer yang sesuai untuk mengakses item. C tidak memberikan informasi jenis runtime apa pun, jadi tidak ada cara untuk mengetahui jenis data apa yang ditunjukkan oleh setiap elemen dari penunjuk itu sendiri - Anda harus melacaknya sendiri. Pendekatan ini jauh lebih kompak daripada yang lain ketika jenis yang Anda simpan besar dan ukurannya sangat bervariasi, karena masing-masing dialokasikan secara terpisah dari array dan hanya dapat diberikan ruang yang diperlukan untuk jenis itu. Untuk tipe sederhana, Anda tidak benar-benar mendapatkan apa-apa dibanding menggunakan serikat pekerja.

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

Jika Anda perlu melacak jenis dalam array, Anda juga dapat menggunakan struct untuk menyimpan tipe bersama dengan pointer, mirip dengan contoh sebelumnya dengan serikat. Ini, sekali lagi, hanya benar-benar berguna ketika jenis yang disimpan besar dan bervariasi dalam ukuran.

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

15
2017-10-17 21:03



Tidak, semua elemen harus memiliki tipe yang sama. Anda mungkin bisa lolos dengan susunan struktur.

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;

Saya sendiri tidak akan pernah melakukan hal seperti itu (kelihatannya berantakan). Tetapi pendekatan array-of-void Anda mirip: Anda harus menyimpan informasi pada jenis di suatu tempat.


3
2017-10-17 19:16



Anda dapat dengan mudah memiliki serangkaian pointer yang mengarah ke berbagai jenis. Tentu saja agar sangat berguna, Anda harus memiliki beberapa cara untuk merekam atau menentukan jenis apa yang saat ini direferensikan oleh setiap elemen.

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

}

2
2017-10-17 19:28



Saya tidak yakin apa yang ingin Anda capai, tetapi ada dua kemungkinan:

1 - Anda sebenarnya tidak ingin sebuah array tetapi struct:

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

Dalam hal ini Anda dapat mengakses nomor tersebut sebagai a.number dan string sebagai a.string.

2 - Anda ingin berbagai jenis varian. Di C, Anda dapat menggunakan serikat (sebaiknya diberi tag) untuk jenis varian:

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

Kemudian Anda dapat menyandikan jenis Anda dengan 0 untuk angka dan 1 untuk string. Menggunakan enum bukan integer untuk tipe akan menjadi cara yang lebih baik tentu saja.


1
2017-10-17 19:32



Itu karena Anda mencoba menyimpan nilai ke dalam slot yang mengharapkan penunjuk. Coba yang berikut ini (pemeriksaan kesalahan dihilangkan karena singkatnya)

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

0
2017-10-17 19:16