Pertanyaan Iterasi atas anggota struct tipe yang sama di C


Apakah mungkin untuk iterasi dari struct C, di mana semua anggota memiliki tipe yang sama, menggunakan pointer. Berikut beberapa contoh kode yang tidak dikompilasi:

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    int mem1 ;
    int mem2 ;
    int mem3 ;
    int mem4 ;
} foo ;

void my_func( foo* data )
{
    int i ;
    int* tmp = data ; // This line is the problem

    for( i = 0; i < 4; ++i )
    {
        ++tmp ;
        printf( "%d\n", *tmp ) ;
    }
}

int main()
{
    foo my_foo ;
    //
    my_foo.mem1 = 0 ;
    my_foo.mem2 = 1 ;
    my_foo.mem3 = 2 ;
    my_foo.mem4 = 3 ;
    //
    my_func( &my_foo ) ;
    return 0 ;
}

Anggota foo harus sejajar dalam memori untuk menjadi satu demi satu, dengan asumsi compiler / kernel Anda tidak mencoba untuk menyediakan perlindungan stack untuk buffer overflow.

Jadi pertanyaan saya adalah:

Bagaimana saya akan mengulang lebih dari anggota struct C yang memiliki tipe yang sama.


4
2017-12-08 20:56


asal


Jawaban:


Cara termudah adalah dengan membuat kesatuan, satu bagian yang berisi setiap anggota secara individual dan satu bagian yang berisi larik. Saya tidak yakin apakah padding yang bergantung pada platform mungkin mengganggu perataan.


3
2017-12-08 21:00



Sebagian besar upaya menggunakan serikat dengan array rentan terhadap kegagalan. Mereka memiliki kesempatan yang baik untuk bekerja selama Anda hanya menggunakan int, tetapi untuk jenis lain, terutama yang lebih kecil, mereka cenderung gagal cukup sering karena kompiler dapat (dan terutama dengan jenis yang lebih kecil sering akan) menambahkan padding di antara anggota Sebuah struct, tetapi tidak diizinkan untuk melakukannya dengan elemen array).

Namun, C memiliki sebuah offsetof() makro yang dapat Anda gunakan. Ini menghasilkan offset item dalam struct, sehingga Anda dapat membuat array offset, kemudian (dengan sedikit perawatan dalam casting) Anda dapat menambahkan offset ke alamat dari struct untuk mendapatkan alamat dari anggota. Perawatan dalam casting adalah karena offset dalam byte, jadi Anda perlu membuang alamat struct untuk char *, lalu tambahkan offset, lalu transmisikan hasilnya ke jenis anggota (int dalam kasus Anda).


14
2017-12-08 21:31



Dari sudut pandang bahasa: Anda tidak bisa. anggota data dari struct tidak ... er .. "iteratible" dalam C, terlepas dari apakah mereka dari tipe yang sama atau tipe yang berbeda.

Gunakan array, bukan sekelompok anggota independen.


5
2017-12-08 21:00



Anda juga dapat menggunakan unnamed union / struct:

struct foo {
    union {
        struct {
            int mem1;
            int mem2;
            int mem3;
            int mem4;
        };
        int elements[4];
    };
};

foo thefoo;
for (int i = 0; i < 4; ++i) {
    thefoo.elements[i] = i;
}

Ini mungkin tidak bekerja pada beberapa compiler, int hal ini Anda harus secara eksplisit menyebutkan nama dan susunan dalam foo,


2
2017-12-08 21:14



    int* tmp = &data->mem1 ;  // explicitly get the address of the first int member

Seperti yang Anda katakan, Anda harus berhati-hati terhadap keselarasan dan masalah tata letak memori lainnya. Anda harus melakukan hal ini dengan baik ints meskipun.

Tetapi saya akan mempertanyakan seberapa mudahnya hal ini membuat kode Anda.

Kebetulan,

    tmp += ( i * sizeof(foo) ) ;

Saya tidak berpikir itu melakukan apa yang Anda pikirkan, tetapi saya tidak sepenuhnya yakin apa yang Anda inginkan. Menggunakan ++tmp; jika Anda ingin melangkah ke yang berikutnya int anggota yang sama foo obyek.


1
2017-12-08 20:59



Inilah pendekatan lain; Saya tidak pernah punya alasan untuk melakukan ini, tetapi memiliki keuntungan untuk tidak merusak definisi struct. Buat array pointer ke anggota yang Anda minati, lalu ulangi di atas array itu:

typedef struct { int mem1; int mem2; int mem3, int mem4; } foo;
...
foo theStruct;
int *structMembers[4] = { &theStruct.mem1, &theStruct.mem2, 
                          &theStruct.mem3, &theStruct.mem4};
...
for (i = 0; i < 4; i++)
{
  printf("%d\n", *structMembers[i]);
}

Dengan cara ini Anda tidak perlu khawatir tentang masalah penyelarasan yang menggigit Anda, dan Anda dapat secara sewenang-wenang memesan bagaimana Anda ingin beralih ke anggota (misalnya, Anda dapat memesannya sehingga berjalan adalah "mem4, mem3, mem2, mem1").


1
2017-12-08 21:35



Anda harus dapat mentransmisikan pointer foo ke pointer int.

Solusi yang lebih bersih adalah mendeklarasikan foo sebagai berikut.

typedef struct
{
    int fooints[ 4 ] ;
} foo ;

Kemudian iterate ke int array.

for ( int i = 0 ; i < 4 ; i++ )
{
    printf( "%d\n" , *( foo->fooints + i ) ) ;
}

Anda dapat melanjutkan untuk membuat fungsi anggota untuk mengakses dan / atau memanipulasi larik int.


0
2017-12-08 21:06