Pourquoi sizeof (x ++) n'incrémente-t-il pas x?

505

Voici le code compilé dans les fenêtres dev c ++:

#include <stdio.h>

int main() {
    int x = 5;
    printf("%d and ", sizeof(x++)); // note 1
    printf("%d\n", x); // note 2
    return 0;
}

Je m'attends xà avoir 6 ans après avoir exécuté la note 1 . Cependant, la sortie est:

4 and 5

Quelqu'un peut-il expliquer pourquoi xn'augmente pas après la note 1 ?

Neigyl R. Noval
la source
37
Je noterais que DevC ++ utilise un très ancien compilateur obsolète, vous voudrez peut-être mettre à niveau vers un IDE plus récent, par exemple Codeblocks Eclipse ou Visual Studio
Tom J Nowell
4
++ x produit le même résultat que x ++, cygwin et gcc 3.4.4.
yehnan
6
@Tim Pletzcker Parce que la première valeur est la taille de la variable et non la variable elle-même.
Surfbutler
Ma réponse a été ajoutée en raison d'une fusion avec celle-ci, ma réponse montre comment vous pouvez obtenir une évaluation au moment de l'exécution dans le cas VLAsoù aucune des autres ne le fait.
Shafik Yaghmour
2
ce type d'écriture doit être évité car il peut provoquer des effets secondaires indésirables. Votre question en fait déjà partie.
user666412

Réponses:

537

De la norme C99 (l'accent est mis sur moi)

6.5.3.4/2

L'opérateur sizeof donne la taille (en octets) de son opérande, qui peut être une expression ou le nom entre parenthèses d'un type. La taille est déterminée à partir du type de l'opérande. Le résultat est un entier. Si le type de l'opérande est un type de tableau de longueur variable, l'opérande est évalué; sinon, l'opérande n'est pas évalué et le résultat est une constante entière.

pmg
la source
51
"Si le type de l'opérande est un type de tableau de longueur variable, l'opérande est évalué" wow! Je ne m'en suis jamais rendu compte
Kos
6
qu'entendez-vous par type de tableau de longueur variable? Cela signifie que l'opérande est un tableau? Dans ce cas, le code n'est pas un tableau. Pouvez-vous clarifier les choses pour moi?
Neigyl R. Noval
37
Un tableau de longueur variable est un tableau déclaré dont la taille est une valeur inconnue lors de la compilation, par exemple si vous lisez Ndepuis stdin et make int array[N]. C'est l'une des fonctionnalités de C99, non disponible en C ++.
Kos
21
@LegendofCage, en particulier, cela signifierait que dans quelque chose comme sizeof(int[++x])(vraiment, vraiment une mauvaise idée, de toute façon) le ++pourrait être évalué.
Jens Gustedt
3
Wreschnig @ Joe: Il est évalué par gcc, clanget à ideone.com/Pf7iF
JFS
190

sizeofest un opérateur au moment de la compilation, donc au moment de la compilation sizeofet son opérande sont remplacés par la valeur du résultat. L' opérande n'est pas évalué (sauf lorsqu'il s'agit d'un tableau de longueur variable); seul le type de résultat compte.

short func(short x) {  // this function never gets called !!
   printf("%d", x);    // this print never happens
   return x;
}

int main() {
   printf("%d", sizeof(func(3))); // all that matters to sizeof is the 
                                  // return type of the function.
   return 0;
}

Production:

2

car shortoccupe 2 octets sur ma machine.

Modification du type de retour de la fonction en double:

double func(short x) {
// rest all same

donnera 8en sortie.

codaddict
la source
12
Seulement parfois - c'est le temps de compilation si possible.
Martin Beckett
10
-1, car cela est en contradiction avec la réponse acceptée (et correcte), et ne cite pas la norme.
sam hocevar
1
Il y a un avantage intéressant à la résolution au moment de la compilation de l'opérateur sizeof () lorsque vous travaillez avec des chaînes. Si vous avez une chaîne qui est initialisée en tant que chaîne entre guillemets, au lieu d'utiliser strlen (), où le tableau de caractères comprenant la chaîne doit être analysé pour le terminateur nul au moment de l'exécution, sizeof (quoted_string) est connu au moment de la compilation, et donc au moment de l'exécution. C'est une petite chose, mais si vous utilisez la chaîne citée dans une boucle des millions et des millions de fois, cela fait une différence significative dans les performances.
user2548100
Si vous l'utilisez vraiment des millions et des millions de fois dans une boucle, ne serait-il pas beaucoup plus judicieux de factoriser le calcul de la longueur hors de la boucle? J'espère sûrement que vous n'avez pas des millions et des millions de constantes codées en dur différentes dans votre code. : -o
Veky
47

sizeof(foo) essaie vraiment de découvrir la taille d'une expression au moment de la compilation:

6.5.3.4:

L'opérateur sizeof donne la taille (en octets) de son opérande, qui peut être une expression ou le nom entre parenthèses d'un type. La taille est déterminée à partir du type de l'opérande. Le résultat est un entier. Si le type de l'opérande est un type de tableau de longueur variable, l'opérande est évalué; sinon, l'opérande n'est pas évalué et le résultat est une constante entière.

En bref: tableaux de longueur variable, exécutés au moment de l'exécution. (Remarque: les tableaux de longueur variable sont une fonctionnalité spécifique - pas les tableaux alloués avec malloc(3).) Sinon, seul le type de l'expression est calculé, et cela au moment de la compilation.

sarnold
la source
33

sizeofest un opérateur intégré à la compilation et n'est pas une fonction. Cela devient très clair dans les cas où vous pouvez l'utiliser sans les parenthèses:

(sizeof x)  //this also works
hugomg
la source
1
Mais comment est-ce une réponse à la question?
Sebastian Mach
5
@phresnel: C'est juste pour préciser que sizeof est "bizarre" et n'est pas soumis aux règles des fonctions normales. J'ai quand même édité le message pour éliminer la confusion possible avec les opérateurs d'exécution normaux comme (+) et (-)
hugomg
L' sizeofopérateur n'est pas un opérateur à la compilation, il suffit de lui donner un VLA pour le comprendre.
paxdiablo
21

Remarque

Cette réponse a été fusionnée à partir d'un doublon, ce qui explique la date tardive.

Original

À l'exception des tableaux de longueur variable, sizeof n'évalue pas ses arguments. Nous pouvons le voir dans le projet de section standard C99 6.5.3.4 Le paragraphe 2 de l' opérateur sizeof qui dit:

L'opérateur sizeof donne la taille (en octets) de son opérande, qui peut être une expression ou le nom entre parenthèses d'un type. La taille est déterminée à partir du type de l'opérande. Le résultat est un entier. Si le type de l'opérande est un type de tableau de longueur variable, l'opérande est évalué; sinon, l'opérande n'est pas évalué et le résultat est une constante entière.

Un commentaire ( maintenant supprimé ) a demandé si quelque chose comme ça évaluerait au moment de l'exécution:

sizeof( char[x++]  ) ;

et en effet cela fonctionnerait, quelque chose comme ça fonctionnerait aussi (les voir en direct ):

sizeof( char[func()]  ) ;

car ils sont tous deux des tableaux de longueur variable. Cependant, je ne vois pas beaucoup d'utilisation pratique dans l'un ou l'autre.

Remarque, les tableaux de longueur variable sont traités dans le projet de section standard C99 6.7.5.2 Déclarateurs de tableau, paragraphe 4 :

[...] Si la taille est une expression constante entière et que le type d'élément a une taille constante connue, le type de tableau n'est pas un type de tableau de longueur variable; sinon, le type de tableau est un type de tableau de longueur variable.

Mise à jour

Dans C11, la réponse change pour le cas VLA, dans certains cas, il n'est pas spécifié si l'expression de taille est évaluée ou non. Extrait de la section 6.7.6.2 Déclarateurs de tableaux qui dit:

[...] Lorsqu'une expression de taille fait partie de l'opérande d'un opérateur sizeof et que la modification de la valeur de l'expression de taille n'affecterait pas le résultat de l'opérateur, il n'est pas précisé si l'expression de taille est évaluée ou non.

Par exemple dans un cas comme celui-ci ( voir en direct ):

sizeof( int (*)[x++] )
Shafik Yaghmour
la source
1
La chose importante à retenir ici est que la plupart du temps, sizeofc'est effectivement une macro - elle ne crée pas de code, mais pré-calcule la valeur attendue et la dépose directement dans le code. Notez que c'était le seul comportement jusqu'à C99, car les VBA n'existaient pas (je n'en avais jamais entendu parler avant cette réponse, croyez-le ou non!)
Corley Brigman
De quelle manière sizeof (char[x++]);utiliserait la valeur de xpour autre chose que la détermination de la valeur de l'expression x++et de la nouvelle valeur pour x, qui sont toutes deux normales avec cet opérateur?
supercat
@alk ... euh, oui, je voulais dire 'VLA' bien sûr :) Shafik - pourquoi ceux-ci évalueraient-ils au moment de l'exécution? comme je l'ai dit, je n'ai jamais vu de VLA, mais les types de ceux-ci sont connus au moment de la compilation, n'est-ce pas?
Corley Brigman
@CorleyBrigman la réponse rapide serait b / c, la norme le dit, mais la raison est b / c, nous ne connaissons pas la taille du tableau au moment de la compilation, nous devons donc évaluer l'expression au moment de l'exécution. Les VLA sont un sujet intéressant, voici deux articles que j'ai sur eux ici et ici .
Shafik Yaghmour
@Shafik - ahh, ok, je suppose que ma confusion était que je ne savais pas que c'était char[x++]un VLA. il ressemble effectivement à un char*à mes yeux inconnus.
Corley Brigman, le
11

Comme l'opérande d' sizeofopérateur n'est pas évalué, vous pouvez le faire:

int f(); //no definition, which means we cannot call it

int main(void) {
        printf("%d", sizeof(f()) );  //no linker error
        return 0;
}

Démo en ligne: http://ideone.com/S8e2Y

Autrement dit, vous n'avez pas besoin de définir la fonction fsi elle est utilisée sizeofuniquement. Cette technique est principalement utilisée dans la métaprogrammation de modèles C ++, car même en C ++, l'opérande de sizeofn'est pas évalué.

Pourquoi ça marche? Cela fonctionne parce que l' sizeofopérateur n'opère pas sur la valeur , mais opère sur le type de l'expression. Ainsi, lorsque vous écrivez sizeof(f()), il opère sur le type de l'expression f(), et qui n'est rien d'autre que le type de retour de la fonction f. Le type de retour est toujours le même, quelle que soit la valeur que la fonction retournerait si elle s'exécute réellement.

En C ++, vous pouvez même ceci:

struct A
{
  A(); //no definition, which means we cannot create instance!
  int f(); //no definition, which means we cannot call it
};

int main() {
        std::cout << sizeof(A().f())<< std::endl;
        return 0;
}

Pourtant, il semble que, dans sizeof, je crée d'abord une instance de A, en écrivant A(), puis j'appelle la fonction fsur l'instance, en écrivant A().f(), mais rien de tel ne se produit.

Démo: http://ideone.com/egPMi

Voici un autre sujet qui explique quelques autres propriétés intéressantes de sizeof:

Nawaz
la source
10

L'exécution ne peut pas se produire pendant la compilation. Donc ++i/ ça i++n'arrivera pas. N'exécutera sizeof(foo())pas non plus la fonction mais renverra le type correct.

rakesh
la source
2
" L'exécution ne peut pas se produire pendant la compilation. " Que voulez-vous dire?
curiousguy
1
La compilation ne créera que du code objet ... Le code objet ne sera exécuté que lorsque l'utilisateur exécutera le binaire. Comme sizeof se produit au moment de la compilation, supposer que i ++ incrémente est incorrect.
rakesh
" Comme sizeof se produit au moment de la compilation ", vous voulez dire: "tout comme sizeofune expression de constante de temps de compilation"?
curiousguy
Comme "#define" se produit pendant le prétraitement, de même, sizeof se produira au moment de la compilation. Pendant la compilation, toutes les informations de type sont disponibles, donc sizeof est évalué puis là pendant la compilation et la valeur est remplacée. Comme déjà mentionné par @pmg "From the C99 Standard" auparavant.
rakesh
1
" sizeof se produira au moment de la compilation " pour quelque chose qui n'est pas un tableau de longueur variable
curiousguy
0

sizeofs'exécute au moment de la compilation, mais x++ne peut être évalué qu'au moment de l'exécution. Pour résoudre ce problème, la norme C ++ stipule que l'opérande de sizeofn'est pas évalué. La norme C dit:

Si le type de l'opérande [of sizeof] est un type de tableau de longueur variable, l'opérande est évalué; sinon, l'opérande n'est pas évalué et le résultat est une constante entière.

Edward Karak
la source
Il n'y a pas de VLA en C ++.
LF
-2

sizeof() L'opérateur donne uniquement la taille du type de données, il n'évalue pas les éléments internes.

munna
la source
Ceci est faux, l' sizeof()opérateur agit de manière récursive et obtiendra la taille en octets de tous les éléments d'un conteneur, des membres d'une classe ou d'une structure, etc. Vous pouvez le prouver très facilement en créant une classe simple avec quelques membres et l'appel sizeof(). (Cependant, tout ce qui est un pointeur ne peut pas voir la taille de - juste la taille du pointeur.) Cela se produit au moment de la compilation, comme d'autres commentateurs l'ont déclaré: les expressions à l'intérieur du sizeof()ne sont pas évaluées.
Tyler Shellberg