65 votes

En C, l’opérateur sizeof renvoie 8 octets lorsqu’il passe 2,5 m mais 4 octets lorsqu’il passe 1,25 m * 2

Je ne comprends pas pourquoi l' sizeof de l'opérateur est en train de produire les résultats suivants:

sizeof( 2500000000 ) // => 8 (8 bytes).

... il retourne 8, et quand je ne les suivants:

sizeof( 1250000000 * 2 ) // => 4 (4 bytes).

... il renvoie 4 au lieu de 8 (qui est ce que j'attendais). Quelqu'un peut-il préciser comment sizeof détermine la taille d'une expression (ou de type de données) et pourquoi dans mon cas précis cela se produit?

Ma meilleure supposition est que l' sizeof de l'opérateur est au moment de la compilation de l'opérateur.

Bounty Question: Est-il temps d'exécution de l'opérateur qui permet d'évaluer ces expressions et de produire mon attendus de sortie (sans bouger)?

123voto

Luchian Grigore Points 136646

2500000000 ne rentre pas dans un int, de sorte que le compilateur correctement l'interprète comme un long (ou long long, ou d'un type où il se situe). 1250000000 t, 2. Le paramètre sizeof n'est pas évalué, de sorte que le compilateur ne peut pas savoir que la multiplication ne rentre pas dans un int, et donc renvoie la taille d'un int.

Aussi, même si le paramètre a été évalué, vous obtiendrez probablement un dépassement de capacité (et un comportement indéfini), mais probablement encore résultant en 4.

Ici:

#include <iostream>
int main()
{
    long long x = 1250000000 * 2;
    std::cout << x;
}

pouvez-vous deviner la sortie? Si vous pensez que c'est 2500000000, vous auriez tort. Le type de l'expression 1250000000 * 2 est int, parce que les opérandes sont int et int et la multiplication n'est pas automatiquement promu à un plus grand type de données si elle ne rentre pas.

http://ideone.com/4Adf97

Donc, ici, gcc, dit-il -1794967296, mais c'est un comportement indéfini, ce qui pourrait être n'importe quel nombre. Ce numéro s'inscrit dans une int.

En outre, si vous lancez un des opérandes dans le type attendu (un peu comme la fonte des entiers lors de la division si vous êtes à la recherche pour un non-résultat sous forme d'entier), vous verrez ce travail:

#include <iostream>
int main()
{
    long long x = (long long)1250000000 * 2;
    std::cout << x;
}

les rendements corrects 2500000000.

8voto

torek Points 25463

[Edit: je n'ai pas remarqué, au départ, que c'était posté à la fois comme le C et le C++. Je suis en répondant seulement à l'égard de C.]

Pour répondre à votre suivi de la question, "Est-il de toute façon à déterminer la quantité de mémoire allouée à une expression ou une variable au moment de l'exécution?": eh bien, pas exactement. Le problème, c'est que ce n'est pas très bien formé question.

"Expressions", C-à-la-langue (par opposition à certains aspects spécifiques de la mise en œuvre), n'utilisent pas toute la mémoire. (Des implémentations spécifiques au besoin du code et/ou des données de mémoire pour contenir les calculs, en fonction du nombre de résultats de l'ajustement dans les registres du CPU et ainsi de suite.) Si un résultat de l'expression n'est pas planqué dans une variable, il suffit simplement de s'évanouit (et le compilateur peut souvent omettre le code d'exécution pour calculer le jamais-résultat enregistré). La langue ne vous donne pas une façon de demander quelque chose qu'il ne veut pas assumer existe, c'est à dire, l'espace de stockage pour les expressions.

Variables, d'autre part, occupent de stockage (mémoire). La déclaration d'une variable indique au compilateur de combien de stockage à mettre de côté. Sauf pour le C99 est Variable Tableaux de Longueur, bien que, l'espace de stockage requis est déterminé uniquement à compiler temps, pas au moment de l'exécution. C'est pourquoi, sizeof x est généralement une constante de l'expression: le compilateur peut (et doit) déterminer la valeur de sizeof x au moment de la compilation.

C99 est VLAs sont une exception particulière à la règle:

void f(int n) {
    char buf[n];
    ...
}

L'espace de stockage requis pour buf n'est pas (en général) quelque chose, le compilateur peut trouver au moment de la compilation, sizeof buf n'est pas une constante de compilation. Dans ce cas, buf fait est attribué au moment de l'exécution et sa taille est seulement déterminé ensuite. Donc, sizeof buf est un runtime-expression calculée.

Pour la plupart des cas, cependant, tout ce qui est de la taille à l'avant, au moment de la compilation, et si une expression déborde au moment de l'exécution, le comportement est indéfini, la mise en œuvre définies, ou bien définis selon le type. Signé dépassement d'entier, comme dans de 2,5 milliards de dollars multiplié par 2, lors de l' INT_MAX est juste un peu plus de 2,7 milliards de dollars, les résultats dans "un comportement non défini". Des entiers non signés ne l'arithmétique modulaire et vous permettre ainsi de calculer dans GF(2k).

Si vous voulez faire certain que le calcul ne peut pas déborder, c'est quelque chose que vous devez calculer vous-même, au moment de l'exécution. C'est une grande partie de ce qui rend multiprecision bibliothèques (comme bpf) difficile d'écrire en C-il est généralement beaucoup plus facile, ainsi que plus rapide, le code de grandes parties de l'assemblage et de profiter des propriétés connues de la CPU (comme dépassement de drapeaux, ou le double du résultat étendu-registre-des paires).

6voto

KingsIndian Points 26855

Luchian répondu déjà. Juste pour compléter..

C11 Norme C++ (C ++ standard a les mêmes lignes) que le type d'un littéral entier sans suffixe désignant le type est dertermined comme suit:

De 6.4.4 Constantes (C11 projet):

La sémantique

4 La valeur d'un nombre décimal constante est calculée en base 10; que d'un octal constante, base 8; celle d'une constante hexadécimale, la base 16. L' lexicalement premier chiffre est le plus important.

5 Le type d'une constante entière est la première de l'correspondant liste dans laquelle sa valeur peut être représentée.

Et la table est comme suit:

Décimal Constante

int
int long int 
long long int

Octal ou Hexadécimal Constante

int
unsigned int
long int
unsigned long int
long long int
unsigned long long int

Pour Octal et Hexadécimal constantes, même non signé types sont possibles. Donc, en fonction de votre plate-forme selon la valeur dans la liste ci-dessus (int ou long int ou long long int) correspond à la première (dans l'ordre) sera le type de littéral entier.

2voto

kriss Points 10450

Une autre façon de mettre la réponse est-à-dire que ce qui est pertinent à l' sizeof n'est pas la valeur de l'expression mais c'est le type. sizeof renvoie la taille de la mémoire pour un type qui peut être fournie soit explicitement comme un type ou une expression. Dans ce cas, le compilateur va calculer ce type au moment de la compilation , sans réellement le calcul de l'expression (en suivant les règles connues, par exemple si vous appelez une fonction, le type est le type de la valeur retournée).

Comme d'autres l'affiche a déclaré il y a une exception pour la variable length array (dont la taille n'est connue qu'au moment de l'exécution).

En d'autres mots, vous l'habitude d'écrire des choses comme sizeof(type) ou sizeof expression où l'expression est une L-Value. L'Expression n'est presque jamais un complexe de calcul (comme le stupide exemple d'appel de fonction ci-dessus) : il serait inutile de toute façon comme il n'est pas évalué.

#include <stdio.h>

int main(){
    struct Stype {
            int a;
    } svar;
    printf("size=%d\n", sizeof(struct Stype));
    printf("size=%d\n", sizeof svar);
    printf("size=%d\n", sizeof svar.a);
    printf("size=%d\n", sizeof(int));

}

Notez également que sizeof est un langage de mots clés, pas une fonction entre parenthèses ne sont pas nécessaires avant la fin de l'expression (nous avons le même genre de règle pour le mot-clé return).

2voto

Paul Griffiths Points 7894

Pour votre question suivante, il n'y a pas "d'opérateur", et il n'y a pas de différence entre la taille "de compilation" d'une expression et la taille "d'exécution".

Si vous voulez savoir si un type donné peut contenir le résultat que vous recherchez, vous pouvez toujours essayer quelque chose comme ceci:

 #include <stdio.h>
#include <limits.h>

int main(void) {
    int a = 1250000000;
    int b = 2;

    if ( (INT_MAX / (double) b) > a ) {
        printf("int is big enough for %d * %d\n", a, b);
    } else {
        printf("int is not big enough for %d * %d\n", a, b);
    }

    if ( (LONG_MAX / (double) b) > a ) {
        printf("long is big enough for %d * %d\n", a, b);
    } else {
        printf("long is not big enough for %d * %d\n", a, b);
    }

    return 0;
}
 

et une solution (légèrement) plus générale, juste pour les alouettes:

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

/* 'gssim' is 'get size of signed integral multiplication */

size_t gssim(long long a, long long b);
int same_sign(long long a, long long b);

int main(void) {
    printf("size required for 127 * 1 is %zu\n", gssim(127, 1));
    printf("size required for 128 * 1 is %zu\n", gssim(128, 1));
    printf("size required for 129 * 1 is %zu\n", gssim(129, 1));
    printf("size required for 127 * -1 is %zu\n", gssim(127, -1));
    printf("size required for 128 * -1 is %zu\n", gssim(128, -1));
    printf("size required for 129 * -1 is %zu\n", gssim(129, -1));
    printf("size required for 32766 * 1 is %zu\n", gssim(32766, 1));
    printf("size required for 32767 * 1 is %zu\n", gssim(32767, 1));
    printf("size required for 32768 * 1 is %zu\n", gssim(32768, 1));
    printf("size required for -32767 * 1 is %zu\n", gssim(-32767, 1));
    printf("size required for -32768 * 1 is %zu\n", gssim(-32768, 1));
    printf("size required for -32769 * 1 is %zu\n", gssim(-32769, 1));
    printf("size required for 1000000000 * 2 is %zu\n", gssim(1000000000, 2));
    printf("size required for 1250000000 * 2 is %zu\n", gssim(1250000000, 2));

    return 0;
}

size_t gssim(long long a, long long b) {
    size_t ret_size;
    if ( same_sign(a, b) ) {
        if ( (CHAR_MAX / (long double) b) >= a ) {
            ret_size = 1;
        } else if ( (SHRT_MAX / (long double) b) >= a ) {
            ret_size = sizeof(short);
        } else if ( (INT_MAX / (long double) b) >= a ) {
            ret_size = sizeof(int);
        } else if ( (LONG_MAX / (long double) b) >= a ) {
            ret_size = sizeof(long);
        } else if ( (LLONG_MAX / (long double) b) >= a ) {
            ret_size = sizeof(long long);
        } else {
            ret_size = 0;
        }
    } else {
        if ( (SCHAR_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = 1;
        } else if ( (SHRT_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(short);
        } else if ( (INT_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(int);
        } else if ( (LONG_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(long);
        } else if ( (LLONG_MIN / (long double) llabs(b)) <= -llabs(a) ) {
            ret_size = sizeof(long long);
        } else {
            ret_size = 0;
        }
    }
    return ret_size;
}

int same_sign(long long a, long long b) {
    if ( (a >= 0 && b >= 0) || (a <= 0 && b <= 0) ) {
        return 1;
    } else {
        return 0;
    }
}
 

qui, sur mon système, génère:

 size required for 127 * 1 is 1
size required for 128 * 1 is 2
size required for 129 * 1 is 2
size required for 127 * -1 is 1
size required for 128 * -1 is 1
size required for 129 * -1 is 2
size required for 32766 * 1 is 2
size required for 32767 * 1 is 2
size required for 32768 * 1 is 4
size required for -32767 * 1 is 2
size required for -32768 * 1 is 2
size required for -32769 * 1 is 4
size required for 1000000000 * 2 is 4
size required for 1250000000 * 2 is 8
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X