Pourquoi la taille d'un paramètre de tableau n'est-elle pas la même que dans main?

104

Pourquoi la taille d'un tableau envoyé en tant que paramètre n'est-elle pas la même que dans main?

#include <stdio.h>

void PrintSize(int p_someArray[10]);

int main () {
    int myArray[10];
    printf("%d\n", sizeof(myArray)); /* As expected, 40 */
    PrintSize(myArray);/* Prints 4, not 40 */
}

void PrintSize(int p_someArray[10]){
    printf("%d\n", sizeof(p_someArray));
}
Chris_45
la source

Réponses:

103

Un type tableau est implicitement converti en type pointeur lorsque vous le transmettez à une fonction.

Alors,

void PrintSize(int p_someArray[10]) {
    printf("%zu\n", sizeof(p_someArray));
}

et

void PrintSize(int *p_someArray) {
    printf("%zu\n", sizeof(p_someArray));
}

sont équivalents. Donc, ce que vous obtenez est la valeur desizeof(int*)

Prasoon Saurav
la source
1
En C ++, vous pouvez passer le tableau en référence à la fonction mais vous ne pouvez pas le faire en C.
Prasoon Saurav
13
Vous devez transmettre la taille du tableau en tant que paramètre distinct. Ensuite, la taille du tableau serait sizeof (* p_someArray) * length
Aric TenEyck
13
Minor nit: sizeofoperator renvoie un objet de type size_t, vous devez donc l'imprimer avec %zu(C99), ou le convertir intsi vous utilisez %dcomme ci-dessus dans vos printfappels.
Alok Singhal
4
La déclaration d'Alok est correcte. L'utilisation d'un spécificateur de format incorrect dans printf (..) est UB.
Prasoon Saurav
1
@ Chris_45: C n'a pas de références, mais en C , vous pouvez passer un tableau par pointeur sur le tableau entier comme dans: void PrintSize(int (*p_someArray)[10]). L'intérieur de la fonction que vous pouvez accéder au tableau en utilisant l' opérateur de déréférencement *: sizeof(*p_someArray). Cela aura le même effet que l'utilisation de références en C ++.
Du
18

C'est un pointeur, c'est pourquoi c'est une implémentation courante de passer la taille du tableau comme deuxième paramètre à la fonction

user240312
la source
16

Comme d'autres l'ont indiqué, les tableaux se désintègrent en pointeurs vers leur premier élément lorsqu'ils sont utilisés comme paramètres de fonction. Il convient également de noter que sizeof n'évalue pas l'expression et ne nécessite pas de parenthèses lorsqu'il est utilisé avec une expression, de sorte que votre paramètre n'est pas du tout utilisé, vous pouvez donc aussi bien écrire le sizeof avec le type plutôt que la valeur.

#include <stdio.h>

void PrintSize1 ( int someArray[][10] );
void PrintSize2 ( int someArray[10] );

int main ()
{
    int myArray[10];
    printf ( "%d\n", sizeof myArray ); /* as expected 40 */
    printf ( "%d\n", sizeof ( int[10] ) ); /* requires parens */
    PrintSize1 ( 0 ); /* prints 40, does not evaluate 0[0] */
    PrintSize2 ( 0 ); /* prints 40, someArray unused */
}

void PrintSize1 ( int someArray[][10] )
{
    printf ( "%d\n", sizeof someArray[0] );
}

void PrintSize2 ( int someArray[10] )
{
    printf ( "%d\n", sizeof ( int[10] ) );
}
Pete Kirkham
la source
12

Donc, vous devrez passer la longueur du tableau comme deuxième paramètre. Lorsque vous écrivez du code, dans lequel vous déclarez tous les deux un tableau de taille constante, puis passez ce tableau à une fonction, il est pénible de voir la constante de longueur de tableau apparaître à plusieurs endroits dans votre code ...

K&R à la rescousse:

#define N_ELEMENTS(array) (sizeof(array)/sizeof((array)[0])) 

Alors maintenant, vous pouvez faire par exemple:

int a[10];
...
myfunction(a, N_ELEMENTS(a));
SC Madsen
la source
Et si la taille du tableau n'est pas disponible au moment du codage, mais uniquement disponible au moment de l'exécution? Existe-t-il un autre moyen de calculer la taille du tableau sans coder en dur sa taille?
weefwefwqg3
La méthode montrée ne fonctionne que lorsque la déclaration du tableau est "en vue". Dans tous les autres cas, vous devez transmettre manuellement la longueur du tableau.
SC Madsen
5

Parce que les tableaux se désintègrent en pointeurs lorsqu'ils sont passés en paramètres. C'est ainsi que fonctionne C, bien que vous puissiez passer des «tableaux» en C ++ par référence et résoudre ce problème. Notez que vous pouvez passer des tableaux de différentes tailles à cette fonction:

 // 10 is superfluous here! You can pass an array of different size!
void PrintSize(int p_someArray[10]);
AraK
la source
5

Le comportement que vous avez trouvé est en fait une grosse verrue en langage C. Chaque fois que vous déclarez une fonction qui prend un paramètre de tableau, le compilateur vous ignore et change le paramètre en pointeur. Donc, ces déclarations se comportent toutes comme la première:

void func(int *a)
void func(int a[])
void func(int a
typedef int array_plz[5];
void func(array_plz a)

a sera un pointeur vers int dans les quatre cas. Si vous passez un tableau à func, il se désintégrera immédiatement en un pointeur vers son premier élément. (Sur un système 64 bits, un pointeur 64 bits est deux fois plus grand qu'un entier 32 bits, donc votre ratio sizeof renvoie 2.)

Le seul objectif de cette règle est de maintenir la compatibilité ascendante avec les compilateurs historiques qui ne prenaient pas en charge le passage de valeurs d'agrégation en tant qu'arguments de fonction.

Cela ne signifie pas qu'il est impossible de passer un tableau à une fonction. Vous pouvez contourner cette verrue en incorporant le tableau dans une structure (c'est essentiellement le but de std :: array de C ++ 11):

struct array_rly {
int a[5];
};
void func(struct array_rly a)
{
printf("%zd\n", sizeof(a.a)/sizeof(a.a[0]));  /* prints 5 */
}

ou en passant un pointeur vers le tableau:

void func(const int (*a)[5])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints 5 */
}

Dans le cas où la taille du tableau n'est pas une constante de compilation, vous pouvez utiliser la technique du pointeur vers un tableau avec des tableaux de longueur variable C99:

void func(int n, const int (*a)[n])
{
printf("%zd\n", sizeof(*a)/sizeof((*a)[0]));  /* prints n */
}
Walter
la source
4

En C ++, vous pouvez passer un tableau par référence dans ce but précis:

void foo(int (&array)[10])
{
    std::cout << sizeof(array) << "\n";
}
Alexandre C.
la source
1
Comment cela aiderait-il avec une question C?
alk
3

En langage C, il n'y a pas de méthode pour déterminer la taille d'un tableau inconnu, donc la quantité doit être transmise ainsi qu'un pointeur vers le premier élément.


la source
2
En général, vous devriez toujours transmettre la taille (nombre d'éléments) d'un tableau avec un tableau à une fonction, sauf si vous avez d'autres moyens de déterminer sa taille (par exemple, un terminateur de caractère nul à la fin des char[]tableaux de chaînes).
David R Tribble
S'il vous plaît, qu'est-ce qu'un " tableau inconnu "?
alk
3

Vous ne pouvez pas passer de tableaux à des fonctions.

Si vous voulez vraiment imprimer la taille, vous pouvez passer un pointeur vers un tableau, mais ce ne sera pas du tout générique car vous devez également définir la taille du tableau pour la fonction.

#include <stdio.h>

void PrintSize(int (*p_anArray)[10]);

int main(void) {
    int myArray[10];
    printf("%d\n", sizeof(myArray)); /* as expected 40 */
    PrintSize(&myArray);/* prints 40 */
}

void PrintSize(int (*p_anArray)[10]){
    printf("%d\n", (int) sizeof(*p_anArray));
}
MONSIEUR.
la source
0

Le comportement est voulu par la conception.

La même syntaxe dans la déclaration des paramètres de fonction signifie une chose complètement différente de la définition de variable locale.

La raison est décrite dans d'autres réponses.

Pavel Radzivilovsky
la source
0

En langage C, lorsque vous passez le tableau en argument à la fonction, il est automatiquement converti en pointeur, le tableau passant d'une fonction à une autre fonction est appelé appel par référence. C'est la raison pour laquelle la fonction appelée ne reçoit que le pointeur qui pointe vers le premier élément de la fonction C'est la raison

fun (int a []) est similaire à fun (int * a);

Ainsi, lorsque vous imprimez la taille du tableau, il imprimera la taille du premier élément.

Dagrawal
la source
En C, il n'y a pas d '" appel par référence ".
alk
" lorsque vous imprimez la taille du tableau, il imprimera la taille du premier élément. " Non, il imprime la taille d'un pointeur.
alk
-1

Dans la langue de programmation 'C' 'sizeof ()' est l'opérateur et il renvoie la taille de l'objet en octets.L'argument de l'opérateur 'sizeof ()' doit être un type à valeur gauche (entier, nombre flottant, struct, tableau Donc, si vous voulez connaître la taille d'un tableau en octets, vous pouvez le faire très simplement.Utilisez simplement l'opérateur 'sizeof ()' et pour son argument utilisez le nom du tableau.Par exemple:

#include <stdio.h>

main(){

 int n[10];
 printf("Size of n is: %d \n", sizeof(n)); 

}

La sortie sur un système 32 bits sera: La taille de n est: 40.Parce que l'ineteger sur le système 32 est de 4 octets.Sur 64x, il est de 8 octets.Dans ce cas, nous avons 10 entiers déclarés dans un tableau.Le résultat est donc '10 * sizeof ( int) '.

Quelques conseils:

Si nous avons un tableau déclaré comme celui-ci 'int n [] = {1, 2, 3, ... 155 ..};'. Nous voulons donc savoir combien d'éléments sont stockés dans ce tableau. Utilisez cet alghorithme:

sizeof (nom_du_array) / sizeof (type_tableau)

Code: #include

principale(){

int n[] = { 1, 2, 3, 44, 6, 7 };
printf("Number of elements: %d \n", sizeof(n) / sizeof(int));
return 0;

}

RichardGeerify
la source
2
Bienvenue sur StackOverflow et merci d'avoir rédigé une réponse. Malheureusement, cela ne répond pas à la question, qui concerne spécifiquement la différence entre sizeof(n)pour une variable locale et sizeof(arg)pour un argument d'une fonction, même si les deux sont apparemment de type int[10].
MicroVirus
-3

Les tableaux ne sont que faiblement dimensionnés. Pour la plupart, un tableau est un pointeur vers la mémoire. La taille dans votre déclaration indique uniquement au compilateur la quantité de mémoire à allouer pour le tableau - elle n'est pas associée au type, donc sizeof () n'a rien à faire.

socle
la source
3
Désolé, cette réponse est trompeuse. Les tableaux ne sont pas non plus «de taille lâche», ni «pointeurs vers la mémoire». Les tableaux ont une taille très exacte et les endroits où un nom de tableau représente un pointeur vers son premier élément sont spécifiés avec précision par le standard C.
Jens