Comment déclarer un tableau unidimensionnel c. Tableaux

Veuillez suspendre AdBlock sur ce site.

Un tableau est le type de données composite le plus simple. Lorsque nous avons discuté des variables, nous avons eu une bonne analogie avec une boîte. Revenons à elle. Si une variable est une case, alors un tableau est constitué de plusieurs cases identiques numérotées qui portent le même nom et ne diffèrent que par leur numéro de série.

Fig.1 Variables et tableaux. Analogie avec les boîtes.

L'image ci-dessus montre trois tableaux :

  • un tableau d'entiers à 8 éléments nommé arr_int
  • tableau float de 11 éléments nommé arr_float
  • un tableau de caractères à 6 éléments nommé arr_char

Un tableau, comme une variable, a son propre nom et son propre type de données. De plus, le tableau a une caractéristique supplémentaire : la taille du tableau. La taille d'un tableau est le nombre d'éléments pouvant y être stockés. Dans notre analogie avec les boîtes, il s’agit du nombre de boîtes.

Note!

La numérotation des éléments du tableau commence à zéro et non à un.

Déclarer et initialiser un tableau

Déclarer un tableau est très similaire à déclarer une variable. La seule différence est que vous devez en outre indiquer la taille du tableau entre crochets. Voici quelques exemples:

Inscription 1.

Int arr_int; double arr_float ; nombre flottant;

Le nom du tableau est soumis à des restrictions similaires à celles imposées sur le nom de la variable.

Règle de dénomination des tableaux

Un nom de tableau est une séquence de caractères, de chiffres et le trait de soulignement « _ » commençant par une lettre. La casse des lettres est importante.

Voici quelques exemples supplémentaires de déclarations de tableau :

Inscription 2.

Notes int, ordre ; prix doubles;

Un tableau, comme toute variable, peut se voir attribuer des valeurs initiales lorsqu'il est déclaré. Si les éléments du tableau ne reçoivent aucune valeur, ils stockeront des déchets, tout comme les variables normales.

Inscription 3.

Int arr_int = (2, 5, 5, 3, 4); double arr_float = (1,2, -2,3, 4,5, 3,83, 0,01, -0,12, 44,2, 123,7, 23,44, -3,7, 7) ;

Si vous devez attribuer des valeurs nulles à tous les éléments d'un tableau, vous pouvez le faire comme ceci :

Inscription 4.

Double arrêt = (0);

Travailler avec des éléments de tableau individuels

Pour faire référence à un élément individuel du tableau, vous devez écrire son nom et son numéro de série entre crochets. N'oubliez pas que la numérotation commence à zéro et non à un.

Affichons, par exemple, les éléments d'un tableau de cinq éléments à l'écran.

Inscription 5.

#inclure int main(void)( int arr = (2, 4, 3, 5, 5); printf("%d %d %d %d %d\n",arr, arr, arr, arr, arr); return (0); )

Bien sûr, si le tableau est très grand, le sortir élément par élément de cette manière est toujours un plaisir. Et personne ne fait cela avec de petits tableaux. Il est préférable et plus correct d'utiliser des boucles. Par exemple:

Inscription 6.

#inclure int main(void)(int arr = (0); pour(int i = 0; i< 100; i = i + 1){ arr[i] = 2*i; } for(int i = 0; i < 100; i = i + 1){ printf("%d\t",arr[i]); } return(0); }

Le programme de la première boucle enregistre les cent premiers nombres pairs dans un tableau et, dans la deuxième boucle, les affiche à l'écran.

Armés de nos nouveaux outils, réécrivons notre programme depuis le début de la leçon afin qu'il utilise un tableau pour stocker les statistiques de nombres aléatoires.

Inscription 7.

#inclure #inclure #inclure int main(void) ( srand(time(NULL)); int count = (0); int rand_number; for (int i = 0; i< 100000; i = i + 1){ rand_number = rand()%3; count = count + 1; } for(int i = 0; i < 3; i = i + 1){ printf("%d - %d\n", i, count[i]); } return 0; }

Faites attention à la technique utilisée dans ce programme.
L'élément zéro du tableau stocke le nombre d'occurrences du nombre 0, le premier élément - le nombre d'occurrences du nombre 1, le deuxième élément - le nombre 2. Autrement dit, le nombre généré lui-même vous permet de déterminer à quel élément du tableau vous devez en ajouter un. Par conséquent, une instruction de sélection de commutateur n’est pas nécessaire. Pratique, n'est-ce pas ?

  • Didacticiel

Dans cet article, je vais essayer de comprendre enfin des concepts aussi subtils en C et C++ que les pointeurs, les références et les tableaux. En particulier, je répondrai à la question de savoir si les tableaux C sont des pointeurs ou non.

Notations et hypothèses

  • Je supposerai que le lecteur comprend que, par exemple, C++ a des références, mais pas C, donc je ne vous rappellerai pas constamment de quel langage (C/C++ ou C++) je parle, le lecteur comprendra cela à partir du contexte ;
  • De plus, je suppose que le lecteur connaît déjà le C et le C++ à un niveau basique et connaît, par exemple, la syntaxe pour déclarer un lien. Dans cet article, je traiterai d'une analyse méticuleuse des petites choses ;
  • Je désignerai les types à quoi ressemblerait la déclaration de la variable TYPE du type correspondant. Par exemple, je désignerai le type « tableau de longueur 2 entiers » par int TYPE ;
  • Je suppose que nous avons principalement affaire à des types de données ordinaires tels que int TYPE , int *TYPE , etc., pour lesquels les opérateurs =, &, * et autres ne sont pas remplacés et désignent les choses habituelles ;
  • « Objet » signifiera toujours « tout ce qui n'est pas une référence », et non « une instance d'une classe » ;
  • Partout, sauf indication contraire, C89 et C++98 sont implicites.

Pointeurs et liens

Panneaux d'affichage. Je ne vous dirai pas ce que sont les pointeurs. :) Supposons que vous le sachiez. Permettez-moi juste de vous rappeler les choses suivantes (tous les exemples de code sont supposés se trouver dans une fonction, par exemple main) :

Intx ; int *y = // Vous pouvez prendre une adresse à partir de n'importe quelle variable en utilisant l'opération de prise d'adresse "&". Cette opération renvoie un pointeur int z = *y; // Un pointeur peut être déréférencé à l'aide de l'opération de déréférencement "*". Cette opération renvoie l'objet pointé par le pointeur

Permettez-moi également de vous rappeler ce qui suit : char vaut toujours exactement un octet et dans tous les standards C et C++ sizeof (char) == 1 (mais les standards ne garantissent pas qu'un octet contient exactement 8 bits :)). De plus, si vous ajoutez un nombre à un pointeur vers un type T, alors la valeur numérique réelle de ce pointeur augmentera de ce nombre multiplié par sizeof (T) . Autrement dit, si p est de type T *TYPE , alors p + 3 est équivalent à (T *)((char *)p + 3 * sizeof (T)) . Des considérations similaires s’appliquent à la soustraction.

Liens. Parlons maintenant des liens. Les liens sont la même chose que les pointeurs, mais avec une syntaxe différente et quelques autres différences importantes, qui seront abordées plus tard. Le code suivant n'est pas différent du précédent, sauf qu'il utilise des liens au lieu de pointeurs :
entier x ; int &y = x; int z = y;

S'il y a une référence à gauche du signe d'affectation, alors il n'y a aucun moyen de savoir si nous voulons attribuer à la référence elle-même ou à l'objet auquel elle fait référence. Par conséquent, une telle affectation est toujours attribuée à un objet et non à une référence. Mais cela ne s'applique pas à l'initialisation du lien : bien entendu, le lien lui-même est initialisé. Par conséquent, une fois qu’une référence est initialisée, il n’y a aucun moyen de la modifier elle-même, c’est-à-dire que la référence est toujours persistante (mais pas son objet).

Lvaleur. Les expressions qui peuvent être attribuées sont appelées lvalues ​​​​en C, C++ et dans de nombreux autres langages (c'est l'abréviation de « valeur gauche », c'est-à-dire à gauche du signe égal). Les expressions restantes sont appelées rvalues. Les noms de variables sont évidemment des lvalues, mais ce ne sont pas les seules. Les expressions a, some_struct.some_field, *ptr, *(ptr + 3) sont également des lvalues.

Le fait surprenant est que les références et les valeurs sont, dans un sens, la même chose. Spéculons. Qu'est-ce que la valeur ? C’est quelque chose qui peut être approprié. C'est-à-dire qu'il s'agit d'une sorte d'endroit fixe en mémoire où vous pouvez mettre quelque chose. Autrement dit, l'adresse. C’est-à-dire un pointeur ou un lien (comme nous le savons déjà, les pointeurs et les liens sont deux manières syntaxiquement différentes d’exprimer le concept d’adresse en C++). De plus, il s'agit plus d'un lien que d'un pointeur, puisque le lien peut être placé à gauche du signe égal et cela signifiera une affectation à l'objet vers lequel pointe le lien. Lvalue est donc une référence.

D'accord, mais (presque toutes) variables peuvent également être à gauche du signe égal. Alors, (telle) variable est une référence ? Presque. Une expression représentant une variable est une référence.

En d’autres termes, disons que nous avons déclaré int x . Maintenant, x est une variable int TYPE et rien d'autre. C'est un int et c'est tout. Mais si j'écris maintenant x + 2 ou x = 3 , alors dans ces expressions la sous-expression x est de type int &TYPE . Parce que sinon ce x ne serait pas différent, disons, de 10, et rien ne pourrait lui être attribué (comme dix).

Ce principe (« une expression qui est une variable est une référence ») est mon invention. Autrement dit, je n'ai vu ce principe dans aucun manuel, norme, etc. Cependant, cela simplifie beaucoup et il est pratique de le considérer comme correct. Si j'implémentais un compilateur, je traiterais simplement les variables dans les expressions comme des références, et c'est très probablement ce que les vrais compilateurs sont censés faire.

Le principe « toute valeur est une référence » est aussi mon invention. Mais le principe « toute référence est une lvalue » est un principe tout à fait légal et généralement accepté (bien entendu, le lien doit être une référence à un objet mutable, et cet objet doit permettre l'affectation).

Maintenant, en tenant compte de nos conventions, nous formulerons strictement les règles pour travailler avec des références : si, disons, int x est déclaré, alors maintenant l'expression x est de type int &TYPE . Si maintenant cette expression (ou toute autre expression de type lien) est à gauche du signe égal, alors elle est utilisée exactement comme un lien ; dans presque tous les autres cas (par exemple, dans la situation x + 2) x est automatiquement converti en type int TYPE (par une autre opération, à côté de laquelle la référence n'est pas convertie en son objet est &, comme nous le verrons plus tard). A gauche du signe égal il ne peut y avoir qu'un lien. Seule une référence peut initialiser une référence (non const).

Opérations * et &. Nos conventions nous donnent une nouvelle façon d’envisager les opérations * et &. Maintenant, ce qui suit devient clair : l'opération * ne peut être appliquée qu'à un pointeur (cela a toujours été notamment connu) et elle renvoie une référence au même type. & est toujours appliqué à une référence et renvoie un pointeur du même type. Donc * et & transforment les pointeurs et les références les uns dans les autres. Autrement dit, ils ne font rien du tout et ne font que remplacer l’essence d’une syntaxe par l’essence d’une autre ! Ainsi, & en général, il n'est pas tout à fait correct d'appeler l'opération de prise d'adresse : elle ne peut s'appliquer qu'à une adresse déjà existante, elle change simplement l'incarnation syntaxique de cette adresse.

Notez que les pointeurs et les références sont déclarés comme int *x et int &x . Ainsi, le principe « la déclaration incite à l'utilisation » est une nouvelle fois confirmé : la déclaration d'un pointeur rappelle comment le transformer en lien, et la déclaration d'un lien fait le contraire.

Notez également que &*EXPR (ici EXPR est une expression arbitraire, pas nécessairement un identifiant unique) ​​est équivalent à EXPR chaque fois que cela a du sens (c'est-à-dire chaque fois que EXPR est un pointeur), et *&EXPR est également équivalent à EXPR chaque fois qu'il a du sens. sens (c'est-à-dire lorsque EXPR est un lien).

Tableaux

Il existe donc un tel type de données - un tableau. Les tableaux sont définis, par exemple, comme ceci :
entier x ;
L’expression entre crochets doit être une constante de compilation en C89 et C++98. Dans ce cas, il doit y avoir un nombre entre crochets ; les crochets vides ne sont pas autorisés.

Tout comme toutes les variables locales (rappelez-vous, nous supposons que tous les exemples de code se trouvent dans des fonctions) sont sur la pile, les tableaux sont également sur la pile. Autrement dit, le code ci-dessus a conduit à l'allocation d'un énorme bloc de mémoire de taille 5 * sizeof (int) directement sur la pile dans laquelle se trouve l'ensemble de notre tableau. Vous n’avez pas besoin de penser que ce code déclare une sorte de pointeur pointant vers une mémoire située quelque part au loin sur le tas. Non, nous avons déclaré un tableau, un vrai. Ici sur la pile.

À quoi sera sizeof (x) ? Bien entendu, elle sera égale à la taille de notre tableau, soit 5 * sizeof (int) . Si nous écrivons
struct foo (int a; int b; );
puis, encore une fois, l'espace pour le tableau sera entièrement alloué directement à l'intérieur de la structure, et la taille de cette structure le confirmera.

Vous pouvez prendre l'adresse (&x) du tableau, et ce sera un véritable pointeur vers l'endroit où se trouve ce tableau. Le type de l'expression &x , comme il est facile à comprendre, sera int (*TYPE) . Au début du tableau, son élément zéro est situé, donc l'adresse du tableau lui-même et l'adresse de son élément zéro sont numériquement les mêmes. C'est-à-dire que &x et &(x) sont numériquement égaux (ici j'ai écrit l'expression célèbre &(x), en fait, tout n'y est pas si simple, nous y reviendrons plus tard). Mais ces expressions ont des types différents - int (*TYPE) et int *TYPE , donc les comparer en utilisant == ne fonctionnera pas. Mais vous pouvez utiliser l'astuce void * : l'expression suivante sera vraie : (void *)&x == (void *)&(x) .

D'accord, supposons que je vous ai convaincu qu'un tableau n'est qu'un tableau, et pas autre chose. Alors d’où vient toute cette confusion entre pointeurs et tableaux ? Le fait est que le nom du tableau est converti en pointeur vers son élément zéro dans presque toutes les opérations.

Nous avons donc déclaré int x . Si nous écrivons maintenant x + 0, alors cela convertit notre x (qui était de type int TYPE, ou plus précisément int (&TYPE)) en &(x), c'est-à-dire un pointeur vers l'élément zéro du tableau x. Maintenant, notre x est de type int *TYPE .

La conversion d'un nom de tableau en void * ou l'application de == pré-convertit également ce nom en pointeur vers le premier élément, donc :
&x == x // erreur de compilation, différents types : int (*TYPE) et int *TYPE (void *)&x == (void *)x // true x == x + 0 // true x == &( x) // vrai

Opération. La notation a[b] est toujours équivalente à *(a + b) (je vous rappelle que nous n'envisageons pas de redéfinir l'opérateur et les autres opérations). La notation x signifie donc ce qui suit :

  • x est équivalent à *(x + 2)
  • x + 2 fait référence aux opérations qui convertissent le nom d'un tableau en pointeur vers son premier élément, donc cela se produit
  • Ensuite, d'après mes explications ci-dessus, x + 2 équivaut à (int *)((char *)x + 2 * sizeof (int)) , c'est-à-dire que x + 2 signifie « décaler le pointeur x de deux entiers »
  • Finalement, une opération de déréférencement est effectuée sur le résultat et on récupère l'objet qui se trouve à ce pointeur décalé.

Les types des expressions participantes sont les suivants :
x // int (&TYPE), après conversion de type : int *TYPE x + 2 // int *TYPE *(x + 2) // int &TYPE x // int &TYPE

Je noterai également qu'il n'est pas nécessaire qu'il y ait un tableau à gauche des crochets ; il peut y avoir n'importe quel pointeur à cet endroit. Par exemple, vous pouvez écrire (x + 2) et cela équivaudra à x. Je noterai également que *a et a sont toujours équivalents, aussi bien dans le cas où a est un tableau que lorsque a est un pointeur.

Maintenant, comme je l'ai promis, je reviens à &(x) . Maintenant, il est clair que dans cette expression, x est d'abord converti en pointeur, puis l'algorithme ci-dessus est appliqué à ce pointeur pour produire une valeur de type int &TYPE , et enfin il est converti en type int *TYPE en utilisant & . Par conséquent, utiliser cette expression complexe (dans laquelle une conversion tableau en pointeur est déjà effectuée) pour expliquer le concept légèrement plus simple de conversion tableau en pointeur était un peu une triche.

Et maintenant la dernière question: qu'est-ce que &x + 1 ? Eh bien, &x est un pointeur vers l'ensemble du tableau, + 1 entraîne une étape vers l'ensemble du tableau. Autrement dit, &x + 1 est (int (*))((char *)&x + sizeof (int )), c'est-à-dire (int (*))((char *)&x + 5 * sizeof (int )) ( ici int (*) est int (*TYPE)). Donc &x + 1 est numériquement égal à x + 5, et non à x + 1 comme on pourrait le penser. Oui, nous finissons par pointer vers la mémoire qui se trouve en dehors du tableau (juste après le dernier élément), mais peu importe ? Après tout, en C, il n'existe toujours pas de contrôle permettant de dépasser les limites d'un tableau. Notez également que l'expression *(&x + 1) == x + 5 est vraie. Vous pouvez également l'écrire comme ceci : (&x) == x + 5 . Ce sera également vrai *((&x)) == x , ou, ce qui revient au même, (&x) == x (à moins, bien sûr, que nous attrapions une erreur de segmentation en essayant d'accéder aux limites de notre mémoire : )).

Un tableau ne peut pas être passé en argument à une fonction. Si vous écrivez int x ou int x dans l'en-tête de la fonction, alors cela sera équivalent à int *x et un pointeur sera toujours passé à la fonction (la taille de la variable passée sera la même que celle du pointeur). Dans ce cas, la taille du tableau spécifiée dans l'en-tête sera ignorée. Vous pouvez facilement spécifier int x dans l'en-tête et y transmettre un tableau de longueur 3.

Cependant, en C++, il existe un moyen de transmettre une référence de tableau à une fonction :
void f (int (&x)) ( // sizeof (x) voici 5 * sizeof (int) ) int main (void) ( int x; f (x); // OK f (x + 0); // Vous ne pouvez pas int y ; f (y); // Vous ne pouvez pas, ce n'est pas la bonne taille)
Avec un tel transfert, vous ne transmettez toujours qu'un lien, pas un tableau, c'est-à-dire que le tableau n'est pas copié. Mais vous obtenez encore quelques différences par rapport au passage de pointeur normal. Une référence au tableau est passée. Vous ne pouvez pas passer un pointeur à la place. Vous devez transmettre exactement le tableau de la taille spécifiée. À l'intérieur de la fonction, une référence à un tableau se comportera exactement comme une référence à un tableau, par exemple, elle aura la taille d'un tableau.

Et ce qui est le plus intéressant c’est que ce transfert peut être utilisé comme ceci :
// Calcule la longueur du tableau modèle size_t len ​​​​(t (&a)[n]) ( return n; )
La fonction std::end en C++11 pour les tableaux est implémentée de la même manière.

"Pointeur vers un tableau". À proprement parler, un « pointeur vers un tableau » n’est qu’un pointeur vers un tableau et rien d’autre. Autrement dit:
int(*a); // Ceci est un pointeur vers un tableau. Le plus réel. Il est de type int (*TYPE) int b; int *c = b; // Ce n'est pas un pointeur vers un tableau. C'est juste un pointeur. Pointeur vers le premier élément d'un tableau int *d = new int; // Et ce n'est pas un pointeur vers un tableau. Ceci est un pointeur
Cependant, parfois l'expression « pointeur vers un tableau » signifie de manière informelle un pointeur vers la zone mémoire dans laquelle se trouve le tableau, même si le type de ce pointeur est inapproprié. Selon cette compréhension informelle, c et d (et b + 0) sont des pointeurs vers des tableaux.

Tableaux multidimensionnels. Si int x est déclaré, alors x n'est pas un tableau de longueur 5 de certains pointeurs pointant quelque part au loin. Non, x est désormais un seul bloc monolithique de 5 x 7 placé sur une pile. sizeof(x) est égal à 5 ​​* 7 * sizeof(int) . Les éléments sont disposés en mémoire comme ceci : x, x, x, x, x, x, x, x et ainsi de suite. Lorsque nous écrivons x, les événements se déroulent comme ceci :
x // int (&TYPE), après conversion : int (*TYPE) x // int (&TYPE), après conversion : int *TYPE x // int &TYPE
La même chose s'applique à **x . Notez que dans les expressions, disons, x + 3 et **x + 3, en réalité la récupération de mémoire n'a lieu qu'une seule fois (malgré la présence de deux astérisques), au moment de la conversion de la référence finale de type int &TYPE simplement en int TAPER . Autrement dit, si nous examinions le code assembleur généré à partir de l'expression **x + 3, nous y verrions que l'opération de récupération des données de la mémoire n'y est effectuée qu'une seule fois. **x + 3 peut également être écrit différemment sous la forme *(int *)x + 3 .

Regardons maintenant cette situation :
int **y = nouveau int *; pour (int i = 0; i != 5; ++i) ( y[i] = new int; )

Qu'est-ce que tu es maintenant ? y est un pointeur vers un tableau (au sens informel !) de pointeurs vers des tableaux (encore une fois, au sens informel). Nulle part un seul bloc de taille 5 x 7 n'apparaît ici, il y a 5 blocs de taille 7 * sizeof (int) qui peuvent être très éloignés les uns des autres. Qu'est-ce que tu ?
y // int **&TYPE y // int *&TYPE y // int &TYPE
Maintenant, lorsque nous écrivons y + 3 , la récupération de mémoire se produit deux fois : une récupération depuis le tableau y et une récupération ultérieure depuis le tableau y, qui peut être loin du tableau y. La raison en est qu'il n'y a pas de conversion du nom du tableau en pointeur vers son premier élément, contrairement à l'exemple du tableau multidimensionnel x. Par conséquent, **y + 3 ici n'est pas équivalent à *(int *)y + 3 .

Je vais l'expliquer une fois de plus. x est équivalent à *(*(x + 2) + 3) . Et y est équivalent à *(*(y + 2) + 3) . Mais dans le premier cas, notre tâche est de trouver le « troisième élément de la deuxième rangée » dans un seul bloc de taille 5 x 7 (bien sûr, les éléments sont numérotés à partir de zéro, donc ce troisième élément sera en un sens le quatrième :)). Le compilateur calcule que l'élément souhaité se trouve effectivement à la 2 * 7 + 3ème place dans ce bloc et l'extrait. Autrement dit, x ici équivaut à ((int *)x) , ou, ce qui est identique, *((int *)x + 2 * 7 + 3) . Dans le second cas, il récupère d'abord le 2ème élément du tableau y, puis le 3ème élément du tableau résultant.

Dans le premier cas, quand on fait x + 2 , on décale immédiatement de 2 * sizeof (int ) , c'est à dire de 2 * 7 * sizeof (int) . Dans le deuxième cas, y + 2 est un décalage de 2 * sizeof (int *) .

Dans le premier cas (void *)x et (void *)*x (et (void *)&x !) sont le même pointeur, dans le second ils ne le sont pas.

Que sont les tableaux en C ?

Comment déclarer des tableaux en C ?

Comment initialiser des tableaux en C ?

Des tableaux en C pour les nuls.

Tableaux en C

Un tableau en C est une collection d’éléments du même type accessibles par index. Les éléments des tableaux en C se situent les uns après les autres dans la mémoire de l'ordinateur.

Un exemple simple de création et de remplissage d'un tableau en C :

// @author Subbotin B.P..h> void main(void) ( int nArr; nArr = 1; nArr = 2; nArr = 3; printf("\n\tArray\n\n"); printf("nArr\t =\t%d\n", nArr); printf("nArr\t=\t%d\n", nArr); printf("nArr\t=\t%d\n", nArr); renvoie 0 ; )

On a:

Dans l'exemple, nous déclarons un tableau contenant des éléments de type int :

ici, le nom du tableau est nArr, le nombre d'éléments du tableau est de trois, le type d'élément du tableau est int.

Un tableau est une collection d'éléments. Chaque élément du tableau peut être référencé par son numéro. Le numéro est généralement appelé un index. Les éléments du tableau sont numérotés à partir de zéro. Attribuons une valeur au premier élément du tableau, et le premier élément a l'indice zéro :

Attribuons une valeur au deuxième élément du tableau, et le deuxième élément a l'indice un :

Attribuons une valeur au troisième élément du tableau, et le troisième élément a l'indice deux :

Lorsque nous affichons des éléments d’un tableau à l’écran, nous obtenons leurs valeurs. Comme ça:

printf("nArr\t=\t%d\n", nArr);

Pour obtenir un élément de tableau, vous devez spécifier le nom du tableau et l'index de l'élément :

Il s'agit du premier élément du tableau, car le premier élément a l'indice zéro.

Attribuons la valeur du troisième élément du tableau à la variable int a :

l'indice du troisième élément du tableau est égal à deux, puisque les indices sont comptés à partir de zéro.

Maintenant la règle générale pour déclarer des tableaux en C : lors de la déclaration d'un tableau, vous devez indiquer son nom, le type d'éléments et le nombre d'éléments. Le nombre d'éléments est un nombre naturel, c'est-à-dire l'ensemble est positif. Zéro ne peut pas être le nombre d’éléments. Vous ne pouvez pas spécifier un nombre variable d'éléments de tableau. Voici des exemples de déclarations de tableaux en C :

int nArr; // Un tableau a été déclaré pour contenir cent entiers ;
flotter fArr; // Un tableau conçu pour stocker 5 nombres flottants a été déclaré ;
char cArr; // Un tableau a été déclaré pour stocker deux caractères ;

Ce serait une erreur de déclarer un tableau avec un nombre variable d'éléments :

Int varElem;
int nArr; // Erreur! Le nombre d'éléments ne peut pas être défini sur une variable ;

Mais vous pouvez fixer le nombre d'éléments avec une valeur constante : soit un entier positif direct 1, 2, 3... soit une constante :

Const int arrayLength = 3;
int nArr;

Lorsque vous déclarez un tableau en C, vous pouvez immédiatement l'initialiser :

int nMassiv = (1, 2, 3);

Vous pouvez omettre le nombre d'éléments du tableau entre crochets si tous les éléments du tableau sont initialisés :

int nMassiv = (1, 2, 3);

le nombre d'éléments sera déterminé automatiquement dans ce cas.

Vous ne pouvez définir qu'une partie des éléments d'un tableau lors de sa déclaration :

int nMassiv = (1, 2);

dans cet exemple, les deux premiers éléments du tableau sont initialisés, mais le troisième n'est pas défini.

Exemple de tableau de caractères :

char cArr = ("S", "B", "P");

Lors de la déclaration d'un tableau, vous ne pouvez pas spécifier le nombre d'éléments d'une variable. Mais vous pouvez utiliser des variables pour accéder aux éléments du tableau :

Int ind = 0 ;
char cr = cArr;

Ceci est utilisé lorsque vous travaillez avec des boucles. Exemple:

// @author Subbotin B.P..h> void main(void) ( const int arrayLength = 3; int nArr; for(int inn = 0; inn< 3; inn++) { nArr = inn + 1; } printf("\n\tArray\n\n"); for(int inn = 0; inn < 3; inn++) { printf("nArr[%d]\t=\t%d\n", inn, nArr); } return 0; }

Dans l'exemple, dans la première boucle, nous remplissons le tableau avec des éléments de type int, et dans la deuxième boucle, nous affichons ces éléments à l'écran.

Les tableaux sont un sujet extrêmement important en C++. Ils sont très souvent utilisés dans les programmes et il est nécessaire de bien comprendre ce sujet. Je vais vous rendre heureux tout de suite - comprendre et apprendre à utiliser les tableaux est assez simple, même pour un débutant.

Alors, pourquoi les tableaux sont-ils nécessaires et quels sont-ils ? Vous savez maintenant que les données du programme sont stockées dans le fichier . Mais il arrive qu'un programme doive stocker des centaines (voire plus) de variables du même type de données, et doive également travailler avec elles - attribuer des valeurs, les modifier, etc.

Par exemple, vous devez stocker les numéros de série des lignes. D'accord - n'importe qui serait effrayé à l'idée de créer cinq cents variables de type int, en donnant à chacune un nom unique et en attribuant une valeur de 1 à 500. (J'ai déjà peur :) Dans ce cas, les tableaux nous sauveront simplement.

Notons les points principaux et passons à un exemple pratique :

  • Un tableau en C++ est une collection d'un certain nombre de variables du même type qui portent le même nom. Par exemple, tableau int ;. Cette entrée signifie que nous avons déclaré un tableau nommé tableau, lequel contient 3 taper des variables int ;
  • les variables du tableau sont appelées éléments ;
  • Chaque élément possède son propre index unique - son propre numéro de série. À l'aide d'un index, nous pouvons accéder à un élément spécifique. IMPORTANT - l'indexation des éléments du tableau commence à 0 . Donc dans le tableau tableau entier le premier élément a un index 0 , et le dernier est 2 . Pour accéder, par exemple, à l'élément zéro d'un tableau et modifier sa valeur, vous devez préciser le nom du tableau et indiquer l'index de l'élément entre crochets - tableau = 33 .

Regardons un exemple :

Tableaux C++

// dans ce programme, nous créons un tableau avec size size, // en utilisant une boucle for, nous entrons des données dans toutes les cellules // du tableau et affichons leur contenu à l'écran #include en utilisant l'espace de noms std ; int main() ( setlocale(LC_ALL, "rus"); const int SIZE = 10; //déclare une constante int firstArray; //déclare un tableau avec le nombre d'éléments SIZE pour (int i = 0; i< SIZE ; i++) //заполняем и отображаем значения на экран { firstArray[i] = i + 1; // на первом шаге цикла firstArray присвоить 1 (0 + 1) cout << i << "-я ячейка хранит число " << firstArray[i] << endl; } cout << endl; return 0; }

// dans ce programme nous créons un tableau avec size size,

// utilise une boucle for pour saisir des données dans toutes les cellules

// tableau et affiche son contenu à l'écran

#inclure

en utilisant l'espace de noms std ;

int main()

setlocale(LC_ALL, "rus");

const int TAILLE = 10 ; //déclare une constante

int premierArray[TAILLE]; //déclare un tableau avec le nombre d'éléments SIZE

pour (int je = 0 ; je< SIZE ; i ++ ) //remplir et afficher les valeurs à l'écran

premierTableau[i] = i+1; // à la première étape de la boucle, attribuez firstArray 1 (0 + 1)

cout<< i << "-ème cellule stocke le numéro"<< firstArray [ i ] << endl ;

cout<< endl ;

renvoie 0 ;

A la ligne 12 nous définissons une constante entière TAILLE, qui stockera la taille du tableau (défini par nos soins, le nombre de ses éléments). A la ligne 13 nous déclarons un tableau : nous indiquons le type de données qui seront stockées dans les cellules du tableau, donnons un nom et indiquons la taille entre crochets.

Il est important que nous ne puissions écrire des valeurs entières constantes qu'entre crochets. Vous devez soit saisir immédiatement un entier entre crochets lors de la déclaration d'un tableau ( int premierArray;), soit définir une constante entière avant de déclarer le tableau et saisir le nom de cette constante entre crochets (comme dans notre exemple).

La deuxième méthode est préférable à utiliser si au cours du programme vous devrez accéder plusieurs fois au tableau via une boucle. Ceci s'explique par le fait que lorsqu'on déclare une boucle, on peut y préciser la condition de passage du compteur à la valeur TAILLE .

Imaginez simplement que nous devions changer la taille du tableau de 10 éléments à 200. Dans ce cas, tout ce que nous avons à faire est de changer la valeur de la constante entière, et ainsi nous substituerons automatiquement de nouvelles valeurs de taille à la fois dans le tableau et dans toutes les boucles du programme.

Dans notre exemple, vous pouvez essayer d'ajouter n'importe quel autre nombre à la constante TAILLE. Et vous verrez que le programme fonctionnera très bien - il créera un tableau avec autant d'éléments que vous spécifiez, saisira les données et les affichera à l'écran.

Et si le tableau est très petit, par exemple avec 5 éléments, vous pouvez l'initialiser immédiatement lors de la déclaration :

Donc l'élément d'indice 0 – premierTableau– se verra attribuer une valeur 11 , et le dernier élément du tableau premierTableau- signification 1 5 . Il existe une telle astuce : vous ne pouvez pas indiquer la taille du tableau entre crochets et écrire ceci :

L'entrée précédente est équivalente à celle-ci. Ce n'est que dans le second cas que le compilateur calculera automatiquement la taille du tableau en fonction de la quantité de données entre accolades.

De plus, lors de l'initialisation initiale des éléments du tableau, lorsque le tableau doit être débarrassé des « déchets » (données résiduelles d'autres programmes en mémoire), il est préférable d'attribuer immédiatement une valeur à tous les éléments. 0 . Cela ressemble à ceci :

Il ne faut pas oublier qu'une telle initialisation n'est possible qu'en remplissant par des zéros. Si vous devez remplir les éléments du tableau avec d'autres nombres, il est préférable d'utiliser une boucle. En C++11 (standard de codage), lors de l'utilisation de l'initialisation de liste (initialisation avec accolades), vous êtes même autorisé à supprimer le signe = .

Je voudrais montrer une autre technique d'initialisation lors de la création d'un tableau. Par exemple, pour un tableau de 30 éléments, nous devons saisir les valeurs 33 Et 44 uniquement aux cellules avec un index 0 Et 1 en conséquence, et remplissez le reste avec des zéros. Ensuite, nous faisons ceci :

ces données seront saisies dans les cellules zéro et première, et le reste prendra automatiquement la valeur 0 .

Vous pouvez également organiser le remplissage d'un tableau à l'aide de l'opérateur cin:

pour (int je = 0; je< size; i++) //заполняем и выводим значения на экран { cout << "Введите значение в ячейку №" << i << " :"; cin >> premierArray[i]; )

pour (int je = 0 ; je< size ; i ++ ) //remplir et afficher les valeurs à l'écran

Déclarer un tableau en C
Le tableau est un type de données secondaire. Un tableau en C est une collection d’éléments d’un type spécifique de taille explicite. c'est-à-dire que contrairement aux tableaux en Ruby, les tableaux en C sont du même type (ils stockent des données d'un seul type) et ont une longueur (taille) prédéterminée.

En C, les tableaux peuvent être grossièrement divisés en 2 types : un tableau de nombres et un tableau de caractères. Bien entendu, une telle division est absolument arbitraire car les symboles sont également des nombres entiers. Les tableaux de caractères ont également une syntaxe légèrement différente. Vous trouverez ci-dessous des exemples de déclarations de tableau :

Int arr; int a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10) ; char ch = ("R", "u", "b", "y", "D", "e", "v", ".", "r", "u"); char ch2 = "site";

Dans le premier cas, nous déclarons un tableau d'entiers (4 octets par nombre) de taille 100 éléments. Plus précisément, nous réservons de la mémoire pour stocker 100 éléments de type int.

Dans le second cas, nous définissons un tableau de 10 éléments entiers et attribuons immédiatement des valeurs aux éléments du tableau.

Dans le troisième cas, nous définissons un tableau de caractères. Il n'y a pas de chaînes en C, mais il existe des tableaux de caractères qui remplacent les chaînes.

Dans ce dernier cas, nous déclarons également un tableau de caractères en utilisant une syntaxe spéciale, plus concise. Les tableaux ch et ch2 sont presque identiques, mais il y a une différence. Lorsque nous utilisons la syntaxe de constante de chaîne pour créer un tableau, un caractère \0 est automatiquement ajouté à la fin du tableau de caractères ; lorsque nous utilisons la syntaxe de déclaration de tableau standard, nous devons nous-mêmes ajouter un \0 comme dernier élément du tableau de caractères. Le caractère \0 (nul) est utilisé pour identifier la fin d'une ligne. Nous parlerons des virures plus en détail dans un article séparé.

Accéder aux éléments du tableau en C

En C, l’accès aux éléments d’un tableau est assez trivial et est similaire à la façon dont cela se fait dans la plupart des autres langages de programmation. Après le nom de la variable référençant le tableau, on indique l'index (aussi appelé clé) de l'élément entre crochets. L'exemple ci-dessous montre comment accéder au premier élément du tableau :

#inclure int main() ( int arr; int a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); char ch = ("R", "u", "b", y","D","e","v",".","r","u") ; char ch2 = "site"; printf("%d\n", arr); printf(" %c\n", ch); )

Code printf("%d\n", a); imprimera 2, pas 1 car l'indexation des tableaux commence à 0 et une autre confirmation de ceci est la ligne printf("%c\n", ch); , qui imprimera le caractère "R" - le zéroième élément du tableau ch.

En général, une déclaration de tableau a la syntaxe suivante :

type_données nom_variable[<количество_элементов>] = <список, элементов, массива>

Le nombre d'éléments du tableau et la liste des éléments sont des attributs obligatoires d'une déclaration de tableau ; plus précisément, n'importe lequel d'entre eux est obligatoire, mais pas les deux à la fois.

Afin de comprendre la structure des tableaux, vous devez vous familiariser avec le concept de pointeurs en C.

Pointeurs en C
Les types de données sont nécessaires pour pouvoir allouer un morceau de mémoire d'une certaine taille pour stocker des données et déterminer de quel type de données il s'agit, car sans définition explicite, il n'est pas clair si un ensemble de zéros et de uns est un nombre. un symbole, ou autre chose. Dans ce cas, la variable est une référence à un morceau de mémoire d'une certaine taille et d'un certain type, par exemple, une variable int fait référence à une zone mémoire spécifique de 4 octets dans laquelle un entier est stocké, et une variable char fait référence à un Zone mémoire de 1 octet dans laquelle est stocké un caractère (code de caractère ).

Pour obtenir l'adresse à laquelle fait référence une variable, on utilise un opérateur spécial & - l'opérateur d'adresse, exemple :

#inclure int main() ( int arr; int a = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); char ch = ("R", "u", "b", y","D","e","v",".","r","u") ; char ch2 = "site"; int num = 100500; printf("%p\n", &arr ); )

Ligne printf("%p\n", &arr); imprimera 0xbfbbe068. 0xbfbbe068 est une représentation hexadécimale de l'adresse mémoire où le nombre 100500 est stocké.

Les pointeurs sont des types spéciaux de variables qui stockent non pas des valeurs ordinaires, mais leurs adresses en mémoire.

#inclure int main() ( int a, b; b = a = 10; printf("A: %p\n", &a); printf("B: %p\n", &b); )

$./programme
R : 0xbfe32008
B : 0xbfe3200c

Dans l'exemple ci-dessus, nous attribuons aux variables a et b la même valeur - le nombre 10, mais les variables a et b font référence à deux zones de mémoire différentes, c'est-à-dire que nous stockons le nombre 10 en mémoire deux fois. Si nous changeons la valeur de la variable b, cela n'affectera pas la variable a et vice versa. Ceci est différent de la façon dont nous travaillons avec les variables dans Ruby, où les variables sont des références à des objets stockés en mémoire, et lors de l'attribution dans le style a = b = 10, nous obtenons un objet - le nombre 10 et deux références à celui-ci.

#inclure int main() ( int a = 10; int * b = printf("A:\n\tadresse : %p\n\tvaleur : %d\n",&a, a); printf("B:\n\ adresse : %p\n\tvaleur : %d\n",b, *b); )

Résultat de l'exécution :

$./programme
UN:
adresse : 0xbfed0fa8
valeur : 10
B :
adresse : 0xbfed0fa8
valeur : 10

Pointeurs et tableaux

En fait, C n’a pas de tableaux au sens où beaucoup de gens le connaissent. Tout tableau en C est simplement une référence à l'élément zéro du tableau. Exemple:

#inclure int main() ( int a = (10,20,30); printf("a-Address:%p\n", &a); printf("a-Address:%p\n", &a); printf( "une-Valeur :%d\n", a); printf("a-Size :%d\n", sizeof(a)); )

Résultat:

$./programme
Adresse a : 0xbfc029b4
Adresse a : 0xbfc029b4
Valeur a : 10
taille : 12

Comme vous pouvez le constater, je ne vous ai pas trompé, une variable référençant un tableau fait en réalité référence uniquement à son élément zéro, c'est-à-dire qu'il s'agit d'un pointeur vers l'adresse de stockage du premier élément.

Lorsque nous exécutons un programme, le système d'exploitation fournit au programme deux quantités spéciales de mémoire : la pile et le tas. Notre programme utilise uniquement une pile. Une pile stocke les valeurs de manière ordonnée. Lorsque nous créons un tableau, nous créons en fait un pointeur vers l’élément zéro d’une collection d’éléments et réservons de la mémoire pour un nombre N d’éléments. Dans l'exemple ci-dessus, nous avons créé une collection de 3 éléments de type int, c'est-à-dire Chaque élément occupe 4 octets de mémoire. Lorsque nous avons utilisé la fonction sizeof(), qui renvoie la taille en octets de l'argument qui lui est passé, nous avons obtenu la valeur 12, c'est-à-dire le tableau occupe 12 octets de mémoire : 3 éléments * 4 octets. Puisqu'une pile est utilisée pour stocker les éléments d'une collection, les éléments sont stockés dans l'ordre, c'est-à-dire qu'ils occupent des zones adjacentes de la pile, ce qui signifie que nous pouvons naviguer dans la collection en connaissant la position de l'élément et la taille de la collection. Exemple:

#inclure int main() ( int a = (10,20,30,40,10), je; pour(i = 0; je<= sizeof(a)/sizeof(int); i++) printf("a[%d] has %d in %p\n", i, a[i], &a[i]); }

Résultat:

$./programme
a en a 10 dans 0xbfbeda88
a en a 20 dans 0xbfbeda8c
a en a 30 dans 0xbfbeda90
a en a 40 dans 0xbfbeda94
a en a 10 dans 0xbfbeda98
a a 5 dans 0xbfbeda9c

Le programme nous a imprimé des informations sur un tableau de 5 éléments : le numéro de l'élément, la valeur et l'adresse mémoire. Faites attention aux adresses des éléments - c'est ce dont je vous ai parlé. Les adresses sont alignées et chacune suivante est supérieure de 4 à la précédente. Le 5ème élément de la collection, que nous n'avons effectivement pas déclaré, stocke le nombre total d'éléments de la collection. La chose la plus intéressante est que nous pouvons également utiliser des pointeurs pour parcourir un tableau. Exemple:

#inclure int main() ( int a = (10,20,30,40,10), i; int * b = a; pour(i = 0; i<= sizeof(a)/sizeof(int); i++) printf("a[%d] has %d in %p\n", i, *(b + i), b + i); }

Remarques

1. Veuillez noter que nous attribuons au pointeur b non pas l'adresse du tableau a, mais la valeur de la variable a elle-même, car a est en fait un pointeur.

2. L'utilisation de crochets indiquant les indices des éléments du tableau est un sucre syntaxique en C pour un accès plus pratique et compréhensible aux éléments de la collection.

3. Comme je l'ai déjà dit, il n'existe pas de tableaux traditionnels en C, je les appelle donc collections afin de souligner cette fonctionnalité du C.

4. L'adresse 1 de l'élément du tableau est supérieure à l'adresse 0 de l'élément du tableau de la quantité de mémoire allouée pour stocker un élément de ce type. Nous travaillons avec des éléments de type int, dont chacun utilise 4 octets pour stocker. L'adresse d'un élément du tableau en mémoire et de toute donnée en général est l'adresse du premier octet de mémoire alloué pour son stockage.

5. Pour faciliter la compréhension, imaginez que la mémoire de l’ordinateur est une immense salle de cinéma, où les sièges sont numérotés de 0 à, disons, 1_073_741_824. Les données du type char ont des fesses de taille normale et tiennent dans une chaise (occupent un octet), et les gros visiteurs du type double long ont des fesses énormes et chacun d'eux ne tient que dans 10 sièges. Lorsqu'on demande à un gros cinéphile son numéro de siège, il répond uniquement au numéro du premier siège, et le nombre et le nombre de tous les autres sièges peuvent être facilement calculés en fonction de la constitution du visiteur (type de données). Les tableaux peuvent être représentés comme des groupes du même type de visiteurs de cinéma, par exemple, un groupe de ballerines minces de type char de 10 personnes prendra 10 places car char tient dans une chaise, et un groupe d'amateurs de bière composé de 5 personnes de tapez long int prendra 40 octets.

6. Les opérateurs & et * portent plusieurs noms populaires, mais vous pouvez les appeler Vasya et Petya. La principale chose à retenir est :

& — indique le numéro de la première place occupée par un visiteur de cinéma. C'est-à-dire l'adresse du premier octet occupé.

* — vous permet de vous adresser à un visiteur assis à un certain endroit. Autrement dit, il vous permet d'obtenir une valeur stockée à une adresse spécifique en mémoire.

Ceci conclut l'article, mais le sujet des tableaux et des pointeurs n'est pas terminé, et encore moins l'étude de l'ensemble du langage C.

Réponses

  1. anonyme dit :

    Vérifiez si ces deux tableaux sont vraiment identiques :
    char ch = ('R',',',',','',',',',',',',',','.', ,'r',',');
    char ch2 = "site";
    Dans le second cas, le tableau contient un élément supplémentaire, /0, qui est utilisé comme délimiteur lors de l'impression, de la copie de chaînes, etc.

  2. l'administrateur dit :

    En fait, les deux tableaux contiennent le caractère \0 comme 10ème élément, ils sont donc vraiment identiques, mais je parlerai du caractère \0 dans un article séparé dédié aux tableaux de caractères et aux chaînes.

  3. anonyme dit :

    Oui, vous aviez raison, j'ai écrit ce commentaire avant de vérifier moi-même ce code dans GCC :
    #inclure
    #inclure

    int principal (vide)
    {
    char ch = ('R', 'u', 'b', 'y', 'D', 'e', ​​'v', '.', 'r', 'u');
    char ch2 = "site";

    printf("%x\n", ch[ strlen(ch) ]);

    renvoie 0 ;
    }
    Imprime zéro.

  4. l'administrateur dit :

    La chose la plus intéressante est que si vous croyez à la spécification ANSI C, alors vous avez raison car elle ne dit rien sur l'ajout automatique d'un caractère nul à la fin d'un tableau de caractères créé de la manière standard pour les tableaux (et dans K&R cela est fait explicitement dans les deux versions). Je pense que c'est soit une différence dans C99, soit dans le compilateur, car les fabricants de compilateurs implémentent les capacités de C99 pour la plupart partiellement, et certains ajoutent quelque chose qui leur est propre. On comprend désormais pourquoi le choix du compilateur est si important. Je devrai travailler sur ce problème plus tard et écrire un article sur les différences entre les compilateurs C, leur prise en charge du C99 et les différences entre ANSI C et C 99.

  5. l'administrateur dit :

    J'ai mené une enquête, mais je vous ai quand même mal informé. Dans la syntaxe traditionnelle, \0 n'est pas ajouté, c'est juste une coïncidence si le caractère suivant sur la pile est \0, mais il ne fait pas partie du tableau de caractères. Si vous utilisez strlen(), vous pouvez clairement voir la différence de 1 caractère entre la syntaxe traditionnelle de création de tableau. où les caractères sont simplement répertoriés et utilisent une constante de chaîne. Le caractère nul est ajouté automatiquement uniquement à la fin du tableau de caractères créé à l'aide d'une constante chaîne.

  6. andré dit :

    Beaucoup de déclarations trompeuses. De tels articles ne gâtent que les programmeurs débutants. :)
    Par exemple, "Le cinquième élément de la collection, que nous n'avons pas déclaré en réalité, contient le nombre total d'éléments de la collection." - ce sont des histoires sans précédent. Le langage C ne stocke nulle part la longueur des tableaux. Dans cet exemple, le tableau « a » est hors limites car pour 5 éléments d'un tableau, le dernier index == 4, et vous l'indexez à 5. Ainsi, vous accédez à l'adresse de la variable i, que le compilateur a placée en mémoire immédiatement après le tableau, donc à la dernière boucle (quand i == 5) vous obtenez 5 en conséquence.

    "Comme je l'ai dit, il n'y a pas de tableaux traditionnels en C, je les appelle donc collections afin de souligner cette fonctionnalité du C." - c'est quelque chose de complètement incompréhensible. Que sont les « tableaux traditionnels » ? Soit dit en passant, les collections sont un terme plus large. Les tableaux, listes, matrices, piles et même tables de hachage correspondent à la définition des collections. Pourquoi introduire des termes inappropriés et induire les lecteurs en erreur ?

  7. l'administrateur dit :

    merci pour la note andr. J'ai récemment commencé à apprendre le C et telles étaient mes hypothèses. C est quelque peu inhabituel pour moi, c'est pourquoi j'obtiens de telles erreurs. Je vais le réparer bientôt.

  8. Faustman dit :

    Bien dit sur les ballerines maigres et un groupe d'amateurs de bière !))

  9. Mon nom dit :

    Et j'ai gcc a, qui, comme vous le dites, stocke le nombre d'éléments, produit la valeur 32767.