Чому sizeof (x ++) не збільшується x?


505

Ось код, складений у вікнах 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;
}

Я очікую x6 після виконання примітки 1 . Однак вихід:

4 and 5

Чи може хтось пояснити, чому xне збільшується після примітки 1 ?


37
Зауважу, що DevC ++ використовує дуже старий застарілий компілятор, можливо, ви захочете оновити до новішої IDE, наприклад, Codeblocks Eclipse або Visual Studio
Tom J Nowell

4
++ x дає такий самий результат, як x ++, cygwin та gcc 3.4.4.
yehnan

6
@Tim Pletzcker Тому що перше значення - це розмір змінної, а не сама змінна.
Surfbutler

Моя відповідь була додана через злиття з цим моєю відповіддю насправді показує, як можна отримати оцінку часу виконання, якщо VLAsніхто з інших цього не робить.
Шафік Ягмур

2
подібного письма слід уникати, оскільки це може спричинити небажані побічні ефекти. Ваше питання вже є одним із них.
користувач666412

Відповіді:


537

Від стандарту C99 (акцент - мій)

6.5.3.4/2

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


51
"Якщо тип операнду є масивом змінної довжини, операнд оцінюється" о! Я ніколи цього не усвідомлював
Кос

6
що ви маєте на увазі під типом масиву змінної довжини? Це означає, що операнд є масивом? Код у цьому випадку не є масивом. Чи можете ви мені прояснити речі?
Neigyl R. Noval

37
Масив змінної довжини - це масив, оголошений тим, що розмір є значенням, невідомим під час компіляції, наприклад, якщо ви читаєте Nз stdin та make int array[N]. Це одна з функцій C99, недоступна в C ++.
Кос

21
@LegendofCage, зокрема, це означало б, що в чомусь подібному sizeof(int[++x])(насправді, дуже поганій ідеї, як ++би то не було ) це можна оцінити.
Єнс Гуведт

3
@Joe Wreschnig: Оцінюється gcc, clangа на ideone.com/Pf7iF
jfs

190

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
Лише іноді - це час компіляції, якщо це можливо.
Мартін Бекетт

10
-1, оскільки це суперечить прийнятій (і правильній) відповіді, і не цитує стандарт.
sam hocevar

1
Приємна користь у тому, що час складання роздільної здатності оператора sizeof () під час роботи з рядками. Якщо у вас є рядок, ініціалізований як рядок, що цитується, замість того, щоб використовувати strlen (), де масив символів, що включає рядок, повинен бути сканований на нульовий термінатор під час виконання, розмір (quoted_string) відомий під час компіляції, і тому під час виконання. Це невелика річ, але якщо ви використовуєте цитований рядок у циклі мільйони та мільйони разів, це суттєво відрізняється у продуктивності.
користувач2548100

Якщо ви дійсно використовуєте його мільйони і мільйони разів у циклі, чи не було б набагато розумнішим вирахувати обчислення довжини з циклу? Я, звичайно, сподіваюся, що у вашому коді немає мільйонів і мільйонів різних твердо кодованих констант. : -o
Veky

47

sizeof(foo) намагається дуже важко виявити розмір виразу під час компіляції:

6.5.3.4:

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

Якщо коротко: масиви змінної довжини, запускаються під час виконання. (Примітка. Масиви змінної довжини є специфічною особливістю - не масиви, виділені за допомогою malloc(3).) В іншому випадку обчислюється лише тип виразу, і це під час компіляції.


33

sizeofє вбудованим оператором часу компіляції і не є функцією. Це стає дуже зрозумілим у випадках, коли ви можете використовувати його без дужок:

(sizeof x)  //this also works

1
Але як це відповідь на питання?
Себастьян Мах

5
@phresnel: Це лише для того, щоб зрозуміти, що розмірofof "дивний" і не підпорядковується правилам нормальних функцій. Я все-таки відредагував публікацію, щоб усунути можливу плутанину з нормальними операторами виконання, такими як (+) та (-)
hugomg

sizeofОператор НЕ оператор часу компіляції, вам потрібно тільки дати йому VLA , щоб зрозуміти це.
paxdiablo

21

Примітка

Цю відповідь було об'єднано з дубліката, що пояснює пізню дату.

Оригінал

За винятком масивів змінної довжини sizeof не оцінює його аргументи. Це ми бачимо з проекту стандартного розділу C99 6.5.3.4 Розмір оператора, абзац 2, який говорить:

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

У коментарі ( зараз видалено ) запитували, чи може щось подібне оцінювати під час виконання:

sizeof( char[x++]  ) ;

і справді так, щось подібне також спрацювало ( Дивіться їх обох наживо ):

sizeof( char[func()]  ) ;

оскільки вони обидва масиви змінної довжини. Хоча я не бачу багато практичного використання ні в одному.

Зверніть увагу, масиви змінної довжини розглядається в проекті C99 стандартного розділ 6.7.5.2 масиву declarators пункту 4 :

[...] Якщо розмір є цілим постійним виразом і тип елемента має відомий постійний розмір, тип масиву не є масивом змінної довжини; в іншому випадку тип масиву - це тип масиву змінної довжини.

Оновлення

У C11 відповідь змінюється для випадку VLA, у деяких випадках не визначено, чи оцінюється вираження розміру чи ні. З розділу 6.7.6.2 Декларатори масиву, який говорить:

[...] Якщо вираз розміру є частиною операнда оператора sizeof і зміна значення виразу size не вплине на результат оператора, не визначено, чи оцінюється вираження розміру чи ні.

Наприклад, у такому випадку ( див. Його наживо ):

sizeof( int (*)[x++] )

1
Тут важливо пам'ятати, що більшість часу, sizeof це фактично макрос - він не створює код, а попередньо обчислює очікуване значення і депонує його прямо в код. Зауважте, що це було єдиною поведінкою до C99, оскільки VBA не існувало (я ніколи насправді не чув про них, поки не відповів, повірте чи ні!)
Корлі Брігман

Яким чином sizeof (char[x++]);використовуватиме значення xдля нічого, крім визначення значення виразуx++ та нового значення для x, обидва вони нормальні для цього оператора?
supercat

@alk ... Помилка, так, я мав на увазі "VLA" звичайно :) Шафік - чому б вони оцінювали під час виконання? як я сказав, я ніколи не бачив VLA, але типи цих обох відомі під час компіляції, чи не так?
Корлі Бригман

@CorleyBrigman швидка відповідь буде b / c стандарт говорить так, але причина - b / c, ми не знаємо розмір масиву під час компіляції, тому нам доведеться оцінити вираз під час виконання. VLA - цікава тема, ось два посади, які я маю на них тут і тут .
Шафік Ягмур

@Shafik - ах, гаразд, я думаю, моє плутанина була, я не зрозумів char[x++], що це VLA. це ефективно виглядає як char*на мої непомічені очі.
Корлі Бригман

11

Оскільки операнд 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лише. Цей прийом здебільшого застосовується в метапрограмуванні шаблонів на C ++, як навіть у C ++, операндіsizeof не оцінюється.

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

У програмі C ++ ви навіть можете це:

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:


10

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


2
" Виконання не може відбутися під час компіляції ". Що ви маєте на увазі?
curiousguy

1
Компіляція створить лише об'єктний код ... Код об'єкта буде виконуватися лише тоді, коли користувач виконує двійковий код. Оскільки sizeof відбувається під час компіляції, припускаючи, що приріст i ++ буде неправильним.
граблі

" Оскільки sizeof трапляється під час компіляції ", ви маєте на увазі: "як sizeofпостійний вираз часу компіляції"?
допитливий хлопець

Як і "#define" відбувається під час попередньої обробки, аналогічний розмір буде відбуватися під час компіляції. Під час компіляції доступна вся інформація про тип, тому оцінюється розмірofof, і там під час компіляції і значення замінюється. Як вже згадувалося в @pmg "Від стандарту C99" раніше.
rakesh

1
" sizeof відбудеться під час компіляції " для чогось, що не є масивом змінної довжини
curiousguy

0

sizeofзапускається під час компіляції, але x++може бути оцінена лише під час виконання. Щоб вирішити це, стандарт C ++ диктує, що операнд sizeofне оцінюється. Стандарт C говорить:

Якщо тип операнда [of sizeof] - це тип масиву змінної довжини, операнд оцінюється; в іншому випадку операнд не оцінюється, а результат є цілою постійною.


В С ++ немає VLA.
LF

-2

sizeof() Оператор надає розмір лише типу даних, він не оцінює внутрішні елементи.


Це помилково, sizeof()оператор діє рекурсивно і набере розмір у байтах усіх елементів контейнера, членів класу чи структури тощо. Ви можете це довести дуже легко, створивши простий клас з кількома членами та закликаючи sizeof()його. (Однак усе, що є вказівником, не може побачити розмір - просто розмір вказівника.) Це відбувається все під час компіляції, як заявили інші коментатори: вирази всередині sizeof()не оцінюються.
Тайлер Шеллберг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.