Domanda Risolto [C] Accedere ai sotto elementi di un array sia tramite nome che indice

DanyDollaro

Utente Electrum
14 Luglio 2018
148
41
58
138
Salve, sono alle prese con un programma in C in cui ho una struttura molto simile a questa:
C:
typedef struct MyStruct_
{
    int arr[3];
} MyStruct;

e per accedere agli elementi utilizzo un'enumerazione come questa:
C:
typedef enum MyIndex_
{
    A,
    B,
    C
} MyIndex;

quindi faccio così:
C:
/* Per accedere all'elenemento che ho 'nominato' A */
MyStruct ms;
ms.arr[A] = 0;

Però quello che voglio fare è impostare la struttura MyStruct in modo da accettare anche l'accesso tramite nome:
C:
MyStruct ms;
ms.arr.a = 0;
ms.arr.b = 1;
/* ecc... */

/* oppure anche così non sarebbe male */
ms.a = 0;
ms.b = 1;

Voglio fare ciò perchè nel progetto reale avrò bisogno di accedere dinamicamente all'array, ed in altri casi saprò già a priori quali elementi toccare, e dato che i nomi delle enumerazioni nel progetto reale sono molto più prolisse rispetto a quelle nell'esempio, riterrei più comodo e bello da leggere l'accesso tramite nome piuttosto che ll'accesso tramite indice hard-coded, quinid mi chiedo, è possibile fare una cosa del genere in C?
 
Very very bad design, ma puoi fare una cosa di questo tipo:
C:
typedef struct MyStruct_ {
  int arr[3];
  const int *a;
} MyStruct;

MyStruct new_MyStruct(void) {
  MyStruct result;
  result.a = result.arr + A;
  return result;
}
Sarebbe molto molto molto meglio evitare di avere nella struttura dei puntatori interni alla (stessa istanza della) struttura stessa. Io ti ho scritto la soluzione per fare esattamente ciò che hai chiesto di fare, ma ti consiglio caldamente di riconsiderare il design del tuo progetto.

PS. Buon natale.
 
  • Love
Reazioni: DanyDollaro
Grazie St3ve, e buon Natale :rofl:.

Comprendo che avere un puntatore all'interno della struttura stessa che punta ad un suo stesso membro faccia abbastanza pena, però nel frattempo mi sono venuti in mente delle soluzioni diverse, come:

C:
typedef struct MyStruct1_
{
    int arr[3];
} MyStruct1;

typedef struct MyStruct2_
{
    int a;
    int b;
    int c;
} MyStruct2;

int main()
{
    MyStruct1 ms1;

    for (int i = 0; i < 3;i++)
    {
        ms1.arr[i] = i;
    }
   
    /* Terribile dato che devo definire una struttura simile a quella originale */
    MyStruct2* ms2 = (MyStruct2*)&ms1;

    printf("%d %d %d", ms2->a, ms2->b, ms2->c);
}

Pensai anche a fare qualcosa con le unioni (union), ma non sarebbe possibile accedere agli elementi con l'indice più alto.

In fine realizzando che tutti i design che mi sono venuti in mente fanno uno più pena dell'altro, sono giunto con fermezza al fatto che utilizzero gli indici, grazie ancora St3ve per il tuo parere :D.
 
Comprendo che avere un puntatore all'interno della struttura stessa che punta ad un suo stesso membro faccia abbastanza pena, però nel frattempo mi sono venuti in mente delle soluzioni diverse, come:

C:
    /* Terribile dato che devo definire una struttura simile a quella originale */
    MyStruct2* ms2 = (MyStruct2*)&ms1;

    printf("%d %d %d", ms2->a, ms2->b, ms2->c);
}
È undefined behavior. Non escluso che ci sia una soluzione pulita, ma dal tuo esempio faccio fatica a capire dov'è effettivamente il problema.
 
  • Mi piace
Reazioni: DanyDollaro
È undefined behavior. Non escluso che ci sia una soluzione pulita, ma dal tuo esempio faccio fatica a capire dov'è effettivamente il problema.
Forse mi hai frainteso (oppure io ho frainteso te), il commento che ho scritto nel codice si riferisce al fatto che ho dovuto definire la struttura MyStruct1 che permette l'accesso tramite indice, e poi la struttura MyStruct2 che permette l'accesso tramite nome, quindi nel codice si avranno 2 strutture pressochè identiche, il che non mi piace, del resto non mi pare ci siano errori nell'esecuzione del codice.
 
Ultima modifica:
Forse mi hai frainteso (oppure io ho frainteso te), il commento che ho scritto nel codice si riferisce al fatto che ho dovuto definire la struttura MyStruct1 che permette l'accesso tramite indice, e poi la struttura MyStruct2 che permette l'accesso tramite nome, quindi nel codice si avranno 2 strutture pressochè identiche, il che non mi piace, del resto non mi pare ci siano errori nell'esecuzione del codice.
Sì, ho capito. Non vedi errori nell'esecuzione, ma potrebbero essercene: è undefined behavior per strict aliasing.

Come capita spesso con gli undefined behavior, vedi il comportamento che ti aspetti e ti sembra di aver scritto codice corretto. Mi è difficile scriverti un codice dove si vede un comportamento diverso da quello che credi, ma sono abbastanza sicuro che sia undefined behavior.

EDIT:
Eccoti l'esempio
C:
#include <stdio.h>

typedef struct MyStruct1_ {
  int arr[3];
} MyStruct1;

typedef struct MyStruct2_ {
  int a;
  int b;
  int c;
} MyStruct2;

int foo(MyStruct1 *ms1, MyStruct2 *ms2) {
  ms2->a = 0;
  ms1->arr[0] = 1;
  return ms2->a;
}

int main() {
  MyStruct1 ms1;
  printf("%d\n", foo(&ms1, (MyStruct2*)&ms1));
  return 0;
}

Compila con -O0 e il tuo programma stampa 1, come ti aspetti. Compila con -O2 e il programma stampa 0. Testato con GCC 10.3.0 perché, ad esempio, Clang 13.0.0 stampa 1 anche con O2 e O3. Negli undefined behavior il compilatore può fare ciò che vuole.
 
  • Geniale
Reazioni: DanyDollaro