web-dev-qa-db-fra.com

C pointeur vers tableau / tableau de pointeurs hors ambiguïté

Quelle est la différence entre les déclarations suivantes:

int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);

Quelle est la règle générale pour comprendre des déclarations plus complexes?

446
George
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers

Le troisième est identique au premier.

La règle générale est priorité des opérateurs . Cela peut devenir encore plus complexe à mesure que les indicateurs de fonction entrent en scène.

423
Mehrdad Afshari

Utilisez le programme cdecl , comme suggéré par K & R.

$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>

Cela fonctionne aussi dans l'autre sens.

cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
258
sigjuice

Je ne sais pas s'il porte un nom officiel, mais je l'appelle le Thingy (Gauche).

Commencez par la variable, puis allez à droite, à gauche et à droite ... et ainsi de suite.

int* arr1[8];

arr1 est un tableau de 8 pointeurs sur des entiers.

int (*arr2)[8];

arr2 est un pointeur (la parenthèse bloque la droite-gauche) sur un tableau de 8 entiers.

int *(arr3[8]);

arr3 est un tableau de 8 pointeurs sur des entiers.

Cela devrait vous aider avec des déclarations complexes.

124
GManNickG
int *a[4]; // Array of 4 pointers to int

int (*a)[4]; //a is a pointer to an integer array of size 4

int (*a[8])[5]; //a is an array of pointers to integer array of size 5 
25
Sunil bn

La réponse pour les deux derniers peut également être déduite de la règle d'or en C:

La déclaration suit l'utilisation.

int (*arr2)[8];

Qu'est-ce qui se passe si vous déréférence arr2? Vous obtenez un tableau de 8 entiers.

int *(arr3[8]);

Que se passe-t-il si vous prenez un élément de arr3? Vous obtenez un pointeur sur un entier.

Cela aide également lorsqu’il s’agit de pointeurs vers des fonctions. Pour prendre l'exemple de sigjuice:

float *(*x)(void )

Qu'est-ce qui se passe quand vous déréférence x? Vous obtenez une fonction que vous pouvez appeler sans argument. Qu'est-ce qui se passe quand tu l'appelles? Cela renverra un pointeur sur un float.

La priorité des opérateurs est toujours délicate. Cependant, l'utilisation de parenthèses peut également être source de confusion car la déclaration suit l'utilisation. Au moins, pour moi, arr2, intuitivement, ressemble à un tableau de 8 pointeurs sur les ints, mais c’est en fait l’inverse. Il suffit de s’y habituer. Raison suffisante pour toujours ajouter un commentaire à ces déclarations, si vous me le demandez :)

edit: example

A propos, je viens de trébucher dans la situation suivante: une fonction qui a une matrice statique et qui utilise l'arithmétique de pointeur pour voir si le pointeur de ligne est hors limites. Exemple:

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

#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))

int *
put_off(const int newrow[2])
{
    static int mymatrix[3][2];
    static int (*rowp)[2] = mymatrix;
    int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);

    memcpy(rowp, newrow, sizeof(*rowp));
    rowp += 1;
    if (rowp == border) {
        rowp = mymatrix;
    }

    return *rowp;
}

int
main(int argc, char *argv[])
{
    int i = 0;
    int row[2] = {0, 1};
    int *rout;

    for (i = 0; i < 6; i++) {
        row[0] = i;
        row[1] += i;
        rout = put_off(row);
        printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
    }

    return 0;
}

Sortie:

0 (0x804a02c): [0, 0] 
 1 (0x804a034): [0, 0] 
 2 (0x804a024): [0, 1] 
 3 (0x804a02c): [1, 2] 
 4 (0x804a034): [2, 4] 
 5 (0x804a024): [3, 7]

Notez que la valeur de border ne change jamais, le compilateur peut donc optimiser cela. Ceci est différent de ce que vous pourriez initialement vouloir utiliser: const int (*border)[3]: qui déclare border en tant que pointeur sur un tableau de 3 entiers qui ne changera pas de valeur tant que la variable existera. Cependant, ce pointeur peut être pointé sur n'importe quel autre tableau à tout moment. Nous voulons plutôt ce type de comportement pour l'argument (car cette fonction ne modifie aucun de ces entiers). La déclaration suit l'utilisation.

(p.s .: n'hésitez pas à améliorer cet échantillon!)

15
Hraban Luyat
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
5
Byron Formwalt

En règle générale, les opérateurs unaires à droite (comme [], (), etc.) ont priorité sur ceux de gauche. Ainsi, int *(*ptr)()[]; serait un pointeur qui pointe vers une fonction qui renvoie un tableau de pointeurs sur int (obtenez les bons opérateurs dès que vous le pouvez en sortant de la parenthèse).

3
Luis Colorado

Voici un site Web intéressant qui explique comment lire des types complexes en C: http://www.unixwiz.net/techtips/reading-cdecl.html

2
Abhishek Jaisingh

Voici comment je l'interprète:

int *something[n];

remarque sur la priorité: l'opérateur d'indice de tableau ('[]') a une priorité plus élevée que l'opérateur de déréférence ('*').

Donc, ici nous allons appliquer le '[]' avant le '*', rendant la déclaration équivalente à:

int *(something[i]);

remarque sur le sens d’une déclaration: int num signifie que (num) est un (int), int *ptr ou int (*ptr) signifie que (valeur à ptr) est un (int), ce qui fait de ptr un pointeur sur int .

Ceci peut être lu en tant que, (valeur de (valeur à son index du quelque chose)) est un entier. Donc, (valeur au ième index de quelque chose) est un (pointeur entier), ce qui fait de quelque chose un tableau de pointeurs entiers.

Dans le second,

int (*something)[n];

Pour donner un sens à cette déclaration, vous devez connaître ce fait:

note sur la représentation du tableau par un pointeur: quelque chose d'Else [i] est équivalent à * (quelque chose d'Else + i)

Donc, en remplaçant quelque chose par (* quelque chose), nous obtenons * (* quelque chose + i), qui est un entier selon la déclaration. Donc, (* quelque chose) nous a donné un tableau, ce qui rend quelque chose d’équivalent à (pointeur sur un tableau).

2

Je pense que nous pouvons utiliser la règle simple ..

example int * (*ptr)()[];
start from ptr 

"ptr est un pointeur indiquant" aller vers la droite ..) "" maintenant, allez à gauche c'est un "(" sortez, allez à droite "()" donc "à une fonction qui ne prend aucun argument," allez à gauche "et renvoie un pointeur "aller à droite" vers un tableau "aller à gauche" d'entiers "

2
simal

Je suppose que la deuxième déclaration est déroutante pour beaucoup. Voici un moyen facile de le comprendre.

Donnons un tableau d’entiers, c’est-à-dire int B[8].

Ayons également une variable A qui pointe vers B. Maintenant, la valeur en A est B, c'est-à-dire (*A) == B. Donc A pointe sur un tableau d'entiers. Dans votre question, arr est similaire à A.

De même, dans int* (*C) [8], C est un pointeur sur un tableau de pointeurs sur un entier.

0