Почему sizeof (x ++) не увеличивает x?

439

Вот код, скомпилированный в dev С++:

#include <stdio.h>

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

Я ожидаю, что x будет 6 после выполнения note 1. Однако выход:

4 and 5

Может ли кто-нибудь объяснить, почему x не увеличивается после note 1?

  • 37
    Я хотел бы отметить, что DevC ++ использует очень старый устаревший компилятор, вы можете обновить его до более новой IDE, например, Codeblocks Eclipse или Visual Studio
  • 4
    ++ x дает тот же результат, что и x ++, cygwin и gcc 3.4.4.
Показать ещё 5 комментариев
Теги:
sizeof

8 ответов

472
Лучший ответ

Из C99 Standard (акцент мой)

6.5.3.4/2

Оператор sizeof дает размер (в байтах) своего операнда, который может быть выражением или заключенным в скобки именем типа. Размер определяется по типу операнда. Результат - целое число. Если тип операнда - тип массива переменной длины, то операнд оценивается; в противном случае операнд не оценивается, а результат представляет собой целочисленную константу.

  • 50
    "Если тип операнда является типом массива переменной длины, операнд оценивается" вау! Я так и не поняла
  • 5
    что вы подразумеваете под типом массива переменной длины? Это значит, что операнд является массивом? Код в этом случае не является массивом. Вы можете прояснить ситуацию для меня?
Показать ещё 15 комментариев
161

sizeof является оператором компиляции, поэтому во время компиляции sizeof и его операнд заменяется значением результата. Операнд не оценивается (за исключением случаев, когда он является массивом переменной длины); используется только тип.

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;
}

Вывод:

2

как short занимает 2 байта на моей машине.

Изменение возвращаемого типа функции на double:

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

даст 8 в качестве вывода.

  • 12
    Только иногда - это время компиляции, если это возможно.
  • 9
    -1, так как это противоречит принятому (и правильному) ответу и не цитирует стандарт.
Показать ещё 1 комментарий
48

sizeof(foo) очень сложно обнаружить размер выражения во время компиляции:

6.5.3.4:

Оператор sizeof дает размер (в байтах) своего операнда, который может быть выражение или имя в скобках типа. Размер определяется по типу операнд. Результат - целое число. Если тип операнда представляет собой массив переменной длины тип, операнд оценивается; в противном случае операнд не оценивается, и результат целочисленная константа.

Короче: массивы переменной длины, выполняемые во время выполнения. (Примечание: Variable Length Arrays - это определенная функция - не массивы, выделенные с помощью malloc(3).) В противном случае вычисляется только тип выражения, а при компиляции время.

  • 1
    Как сильно это старается? :П
33

sizeof является встроенным оператором компиляции и не является функцией. Это становится очень ясным в тех случаях, когда вы можете использовать его без круглых скобок:

(sizeof x)  //this also works
  • 0
    будет работать sizeof ((i ++))?
  • 4
    @Gaotter: да, лишние скобки игнорируются: codepad.org/fhdjYhzV
Показать ещё 2 комментария
17

Примечание

Этот ответ был объединен из дубликата, что объясняет позднюю дату.

Оригинал

За исключением массивов переменной длины sizeof не оценивает свои аргументы. Мы видим это из проекта стандартного раздела C99 6.5.3.4 Оператор sizeof оператора, который гласит:

Оператор sizeof дает размер (в байтах) своего операнда, который может быть выражение или имя в скобках типа. Размер определяется по типу операнд. Результат - целое число. Если тип операнда представляет собой массив переменной длины тип, операнд оценивается; в противном случае операнд не оценивается, и результат целочисленная константа.

Комментарий (теперь удаленный) спросил, будет ли что-то подобное оцениваться во время выполнения:

sizeof( char[x++]  ) ;

и действительно, это будет работать так же (Посмотрите на них как в прямом эфире):

sizeof( char[func()]  ) ;

так как они являются массивами переменной длины. Хотя, я не вижу много практического использования в любом из них.

Примечание. Массивы переменной длины описаны в черновик проекта C99 раздел 6.7.5.2 Объявление объявления массива, пункт 4:

[...] Если размер представляет собой целочисленное константное выражение, а тип элемента имеет известный постоянный размер, тип массива не является массивом переменной длины; в противном случае тип массива - это тип массива переменной длины.

Обновить

В C11 ответ изменяется для случая VLA, в некоторых случаях он не определен, оценивается ли выражение размера или нет. Из раздела 6.7.6.2 Объявление манифеста, в котором говорится:

[...] Если выражение размера является частью операнда размера оператора и изменение значения выражения размера не будет влияют на результат оператора, не определено, независимо от того, выражение размера оценивается.

Например, в таком случае (см. его в прямом эфире):

sizeof( int (*)[x++] )
  • 1
    Важно помнить, что в большинстве случаев sizeof фактически является макросом - он не создает код, а предварительно вычисляет ожидаемое значение и помещает его прямо в код. Обратите внимание, что это было единственное поведение до C99, поскольку VBA не существовало (я никогда не слышал о них до этого ответа, верьте этому или нет!)
  • 0
    sizeof (char[x++]); образом будет sizeof (char[x++]); использовать значение x для чего-либо другого, кроме определения значения выражения x++ и нового значения для x , оба из которых являются нормальными для этого оператора?
Показать ещё 7 комментариев
10

Поскольку операнд оператора sizeof не оценивается, вы можете сделать это:

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

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

Демо-версия онлайн: http://ideone.com/S8e2Y

То есть вам не нужно определять функцию f, если она используется только в sizeof. Этот метод в основном используется в метапрограммировании шаблонов С++, так как даже в С++ операнд sizeof не оценивается.

Почему это работает? Он работает, потому что оператор sizeof не работает по значению, а работает по типу выражения. Поэтому, когда вы пишете sizeof(f()), он работает с типом выражения f(), и это не что иное, как возвращаемый тип функции f. Тип возврата всегда один и тот же, независимо от того, какое значение возвращает функция, если она действительно выполняется.

В С++ вы можете даже это:

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;
}

Но похоже, что в sizeof я сначала создаю экземпляр A, написав A(), а затем вызывая функцию f в экземпляре, написав A().f(), но такого не бывает.

Демо: http://ideone.com/egPMi

Вот еще одна тема, которая объясняет некоторые другие интересные свойства sizeof:

9

Выполнение не может произойти во время компиляции. Поэтому ++i/i++ не будет. Также sizeof(foo()) не будет выполнять функцию, но вернет правильный тип.

  • 2
    « Выполнение не может произойти во время компиляции». Что вы имеете в виду?
  • 1
    Компиляция создаст только объектный код ... Объектный код будет выполняться только тогда, когда пользователь выполняет двоичный файл. Поскольку sizeof происходит во время компиляции, предполагая, что i ++ будет увеличивать, что неправильно.
Показать ещё 3 комментария
0
Оператор

sizeof() дает размер только типа данных, он не оценивает внутренние элементы.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню