web-dev-qa-db-fra.com

Déclaration et initialisation de tableaux en C

Existe-t-il un moyen de déclarer d'abord puis d'initialiser un tableau en C?

Jusqu'à présent, j'ai initialisé un tableau comme celui-ci:

int myArray[SIZE] = {1,2,3,4....};

Mais je dois faire quelque chose comme ça

int myArray[SIZE];

myArray = {1,2,3,4....};
48
Dave H

En C99, vous pouvez le faire en utilisant un littéral composé en combinaison avec memcpy

memcpy(myarray, (int[]) { 1, 2, 3, 4 }, sizeof myarray);

(en supposant que la taille de la source et la taille de la cible sont les mêmes). 

Dans C89/90, vous pouvez émuler cela en déclarant un tableau "source" supplémentaire

const int SOURCE[SIZE] = { 1, 2, 3, 4 }; /* maybe `static`? */
int myArray[SIZE];
...
memcpy(myarray, SOURCE, sizeof myarray);
29
AnT

Non, vous ne pouvez pas leur attribuer des valeurs arbitraires dans une seule instruction (sauf si cela est fait dans le cadre de la déclaration).

Vous pouvez le faire avec du code, quelque chose comme:

myArray[0] = 1;
myArray[1] = 2;
myArray[2] = 27;
:
myArray[99] = -7;

ou (s'il y a une formule):

for (int i = 0; i < 100; i++) myArray[i] = i + 1;

L’autre possibilité est de conserver certains modèles are définis au moment de la déclaration et de les utiliser pour initialiser votre tableau, par exemple:

static const int onceArr[]  = {  0,  1,  2,  3,  4,..., 99};
static const int twiceArr[] = {  0,  2,  4,  6,  8,...,198};
:
int myArray[7];
:
memcpy (myArray, twiceArr, sizeof (myArray));

Cela a l'avantage (probablement) d'être plus rapide et vous permet de créer des tableaux plus petits que les modèles. J'ai utilisé cette méthode dans des situations où je dois réinitialiser un tableau rapidement, mais dans un état spécifique (si l'état était entièrement nul, j'utiliserais simplement memset).


Vous pouvez même le localiser sur une fonction d'initialisation:

void initMyArray (int *arr, size_t sz) {
    static const int template[] = {2, 3, 5, 7, 11, 13, 17, 19, 21, ..., 9973};
    memcpy (arr, template, sz);
}
:
int myArray[100];
initMyArray (myArray, sizeof(myArray));

Le tableau statique sera (presque certainement) créé au moment de la compilation, il n'y aura donc aucun coût d'exécution, et la variable memcpy devrait être incroyablement rapide, probablement plus rapide que 1 229 instructions d'affectation, mais nettement moins taper de votre côté :-) .

13
paxdiablo

Y at-il un moyen de déclarer en premier et puis initialiser un tableau en C?

Il y a! mais sans utiliser la méthode que vous avez décrite.

Vous ne pouvez pas initialiser avec une liste séparée par des virgules, cela n’est autorisé que dans la déclaration. Vous pouvez cependant initialiser avec ...

myArray[0] = 1;
myArray[1] = 2;
...

ou 

for(int i = 1; i <= SIZE; i++)
{
  myArray[i-1] = i;
}
4
Jacob

Ceci est un addendum à la réponse acceptée par AndreyT, avec le commentaire de Nyan sur les tailles de tableau incompatibles. Je ne suis pas d'accord avec leur mise à zéro automatique du cinquième élément. Il devrait probablement être 5 --le nombre après 1,2,3,4. Donc, je suggérerais un wrapper à memcpy () pour produire une erreur compile-time lorsque nous essayons de copier des tableaux de tailles différentes:

#define Memcpy(a,b) do {                    /* copy arrays */       \
    ASSERT(sizeof(a) == sizeof(b) &&        /* a static assert */   \
           sizeof(a) != sizeof((a) + 0));   /* no pointers */       \
    memcpy((a), (b), sizeof (b));           /* & unnecesary */      \
    } while (0)                             /* no return value */

Cette macro générera une erreur de compilation si votre tableau est de longueur 1. Ce qui est peut-être une fonctionnalité.

Comme nous utilisons une macro, le littéral composé C99 semble nécessiter une paire de parenthèses supplémentaire:

Memcpy(myarray, ((int[]) { 1, 2, 3, 4 }));

Ici, ASSERT () est une "assertion statique". Si vous ne possédez pas déjà le vôtre, j'utilise les logiciels suivants sur plusieurs plates-formes:

#define CONCAT_TOKENS(a, b) a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e) enum {EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}
#define ASSERTM(e,m) /* version of ASSERT() with message */ \
    enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}
2
Joseph Quinsey

Pourquoi ne pouvez-vous pas initialiser lorsque vous déclarez?

Quel compilateur C utilisez-vous? Est-ce qu'il supporte C99?

S'il prend en charge C99, vous pouvez déclarer la variable là où vous en avez besoin et l'initialiser lorsque vous la déclarez.

La seule excuse à laquelle je peux penser pour ne pas le faire serait parce que vous devez le déclarer mais faire une sortie rapide avant de l'utiliser, afin que l'initialiseur soit perdu. Cependant, je soupçonne qu’un tel code n’est pas aussi bien organisé qu’il devrait l’être et pourrait être rédigé de manière à ne pas poser de problème.

1
Jonathan Leffler

Il n'est pas possible d'attribuer des valeurs à un tableau en une seule fois après l'initialisation ..__ La meilleure alternative serait d'utiliser une boucle.

for(i=0;i<N;i++)
{
     array[i] = i;
}

Vous pouvez coder et attribuer des valeurs telles que --array[0] = 1 et ainsi de suite.

Memcpy peut également être utilisé si les données sont déjà stockées dans un tableau.

0
Tush_08

Le PO a omis certaines informations cruciales de la question et les a seulement insérées dans un commentaire pour une réponse.

J'ai besoin d'initialiser après avoir déclaré, parce que sera différent en fonction d'une condition, je veux dire quelque chose comme ceci int myArray [SIZE]; if (condition1) {myArray {x1, x2, x3, ...}} sinon si (condition2) {mon tableau {y1, y2, y3, ...}}. . etc...

En gardant cela à l'esprit, tous les tableaux possibles devront être stockés quelque part dans les données, donc aucune mémoire n'est requise (ou souhaitée), seuls un pointeur et un tableau 2D sont nécessaires.

//static global since some compilers build arrays from instruction data
//... notably not const though so they can later be modified if needed
#define SIZE 8
static int myArrays[2][SIZE] = {{0,1,2,3,4,5,6,7},{7,6,5,4,3,2,1,0}};

static inline int *init_myArray(_Bool conditional){
  return myArrays[conditional];
}

// now you can use:
//int *myArray = init_myArray(1 == htons(1)); //any boolean expression

La version non-alignée donne cet assemblage résultant sur x86_64:

init_myArray(bool):
        movzx   eax, dil
        sal     rax, 5
        add     rax, OFFSET FLAT:myArrays
        ret
myArrays:
        .long   0
        .long   1
        .long   2
        .long   3
        .long   4
        .long   5
        .long   6
        .long   7
        .long   7
        .long   6
        .long   5
        .long   4
        .long   3
        .long   2
        .long   1
        .long   0

Pour des conditionnels/des tableaux supplémentaires, il suffit de modifier le nombre 2 de myArrays sur le nombre souhaité et d'utiliser une logique similaire pour obtenir un pointeur sur le tableau de droite.

0
technosaurus

Vous ne pouvez pas initialiser le tableau après l'avoir déclaré une seule fois.

Il n'y a que trois options:

1.) les initialiser dans différentes lignes:

int array[SIZE];

array[0] = 1;
array[1] = 2;
array[2] = 3;
array[3] = 4;
//...
//...
//...

Mais ce n'est pas ce que vous voulez, je suppose.

2.) Initialisez-les en utilisant une boucle for ou while:

for (i = 0; i < MAX ; i++)  {
    array[i] = i;
}

C’est la meilleure façon d’atteindre votre objectif.

3.) Si vous souhaitez initialiser le tableau sur une seule ligne, vous devez définir au moins un tableau avec initialisation. Puis copiez-le dans votre tableau de destination, mais je pense que cela ne présente aucun avantage. Dans ce cas, vous devez définir et initialiser votre tableau sur une seule ligne.

Et puis-je vous demander pourquoi précisément vous voulez le faire ???

0
Kumar Alok