sizeof принимает два аргумента - PullRequest
34 голосов
/ 13 июня 2011

В C.1.3 C ++ IS (2003. Это тоже в C ++ 11 IS), стандарт указывает на разницу между ISO C и C ++;а именно, для

char arr[100];

sizeof(0, arr) возвращает sizeof(char*) в C, но 100 в C ++.

Я не могу найти документацию для sizeof с двумя аргументами.Очевидным отступлением является оператор запятой, но я так не думаю: sizeof(arr) в C равно 100;sizeof(0, arr) - это sizeof(char*)sizeof(0, arr), и sizeof(arr) - это 100 в C ++.

Возможно, я упускаю весь смысл IS в этом контексте.Кто-нибудь может помочь?Это похоже на вопрос, обсуждавшийся еще в '09, но никто не ссылался на IS, и я не думаю, что правильный ответ был дан.


Редактировать : На самом делеIS говорит об операторе запятой.По какой-то причине (0, arr) возвращает char* в C, но char[100] в C ++.Почему?

Ответы [ 7 ]

46 голосов
/ 13 июня 2011

В C оператор запятой не выдает lvalue, следовательно, массив arr, который является lvalue , распадается в тип указателя, который является rvalue (в данном случае). Таким образом, sizeof(0,arr) становится эквивалентным sizeof(char*) из-за преобразования lvalue в rvalue .

Но в C ++ оператор запятой выдает lvalue. Нет преобразования lvalue-to-rvalue . Таким образом, sizeof(0,arr) остается тем же, что эквивалентно sizeof(char[100]).

Кстати, sizeof это не функция, это оператор. Таким образом, следующее является полностью допустимым C ++ (и C, если вы представляете printf вместо cout):

int a[100], b[200], c[300], d[400];
cout << sizeof(a,b,c,d) << endl;

Демо: http://www.ideone.com/CtEhn

Вы можете подумать, что я передал 4 операнда sizeof, но это неправильно. sizeof работает с результатом операторов запятой. И из-за множества операторов запятых вы видите много операндов.

4 операнда с 3 запятыми; как и в 1+2+3+4, есть 3 оператора, 4 операнда.

Вышеуказанное эквивалентно следующему (действительно в C ++ 0x):

auto & result = (a,b,c,d); //first all comma operators operate on the operands.
cout << sizeof (result) << endl; //sizeof operates on the result

Демо: http://www.ideone.com/07VNf

Так что это оператор запятая , который заставляет вас чувствовать , что существует множество аргументов . Здесь запятая является оператором, но при вызове функции запятая НЕ является оператором, это просто разделитель аргументов.

function(a,b,c,d); //here comma acts a separator, not operator.

Итак, sizeof(a,b,c,d) работает с типом результата , операторов, точно так же, sizeof(1+2+3+4) работает с типом результата + операторов.

Также обратите внимание, что вы не можете написать sizeof(int, char, short), именно потому, что оператор запятая не может работать на типах . Работает только на значение . Я думаю, sizeof - единственный оператор в C и C ++, который также может работать с типами . В C ++ есть еще один оператор, который может работать с типами . Его зовут typeid.

24 голосов
/ 13 июня 2011

В C тогда массив распадается на указатель из-за различной спецификации оператора запятой по отношению к rvalues ​​и lvalues ​​(не единственное место, где можно найти такую ​​разницу).В C ++ массив остается массивом, что дает правильный результат.

6 голосов
/ 13 июня 2011

Это оператор запятой.И разница, о которой вы говорите, не имеет абсолютно никакого отношения к sizeof.Разница действительно в lvalue-to-rvalue, массиве-указателю и аналогичном поведении затухания между языками C и C ++.

Язык C в этом отношении весьма радует триггера: массивы распадаются на указатели практически сразу (за исключением очень небольшого числа конкретных контекстов), поэтому результат выражения 0, arr имеет тип char *.Это эквивалентно 0, (char *) arr.

В языке C ++ массивы сохраняют свою «массивность» гораздо дольше.При использовании в контексте , операторные массивы не распадаются на указатели (и lvalues ​​не распадаются на rvalues), поэтому в C ++ тип выражения 0, arr по-прежнему char[100].

Это то, что объясняет разницу в поведении sizeof в этом примере.Оператор ?: является еще одним примером оператора, который демонстрирует сходную разницу в поведении затухания, т.е. sizeof(0 ? arr : arr) даст вам разные результаты в C и C ++.По сути, все это связано с тем фактом, что операторы C обычно не сохраняют значение своих операндов.Для демонстрации такого поведения можно использовать множество операторов.

5 голосов
/ 13 июня 2011

Это не sizeof с двумя аргументами.sizeof это оператор, а не функция.

Учтите, что (0, arr) - это выражение, использующее оператор запятой, а все остальное становится на свои места.

3 голосов
/ 13 июня 2011

sizeof не принимает двух аргументов. Но это тоже не функция, поэтому (...) не разделяют аргументы функции, они просто необязательная часть синтаксиса и принудительное группирование. Когда ты пишешь sizeof(0, arr), аргумент sizeof является единственным выражением 0, arr. Одно выражение с оператором запятой, которое оценивает выражение слева от запятой, выбрасывает его значение (но не его побочные эффекты), затем оценивает выражение справа от запятой, и использует его значение в качестве значения полного выражения.

Я не уверен насчет C, но это может быть разница между . охват языков В C ++ преобразование массива в указатель не происходит, если необходимо; в С, если я правильно помню, стандарт говорит, что это всегда происходит, кроме как в определенных контекстах. В том числе как оператор sizeof. В этом случае, так как оператор запятой не имеют ограничения в отношении типов своих операндов, преобразование массива в указатель не происходит в C ++. В С Оператор и оператор запятой не указан в исключениях, поэтому преобразование массива в указатель имеет место. (В этом случае массив является операндом оператора запятой, а не sizeof.)

2 голосов
/ 26 февраля 2014

Лучший способ увидеть, что здесь происходит, - взглянуть на грамматику в стандарте.Если мы посмотрим на черновик стандартного раздела C99 6.5.3 Унарные операторы абзац 1 , то увидим, что грамматика для sizeof:

sizeof unary-expression
sizeof ( type-name )

Итак, второеодин не применяется, но как sizeof unary-expression применяется в этом случае?Если мы посмотрим на раздел A.2.1 Выражения из чернового стандарта и проработаем грамматику следующим образом:

unary-expression -> postfix-expression -> primary-expression -> ( expression )

мы получим скобки вокруг выражения итеперь нам просто нужно взглянуть на грамматику для оператора запятой из раздела 6.5.17 оператора запятой и мы увидим:

expression:
  assignment-expression
  expression , assignment-expression

Итак, у нас теперь есть:

sizeof( expression , assignment-expression )
                   ^
                   |
                   comma operator

оба выражения и выражения-присваивания могут привести нас к primary-expression , которое имеет следующую грамматику:

primary-expression:
  identifier
  constant
  string-literal
  ( expression )

и 0 - это константа , а arr - это идентификатор , поэтому мы имеем:

 sizeof( constant , identifier )

Так что же означает оператор запятой делать здесь?Раздел 6.5.17 абзац 2 гласит:

Левый операнд оператора запятой оценивается как выражение void;после его оценки есть точка последовательности.Затем вычисляется правый операнд;результат имеет свой тип и значение. 97)

, поскольку оператор запятой не является одним из исключений, когда массив не преобразуется в указатель, он дает указатель ( это описано в разделе 6.3.2.1 L-значения, массивы и обозначения функций ), что означает, что мы в итоге получим:

sizeof( char * )

In C ++ грамматика очень похожа, поэтому мы заканчиваем в одном и том же месте, но операторы запятых работают по-разному.В разделе стандарта черновика C ++ 5.18 Оператор запятой говорит:

[...] Тип и значение результата - это тип и значение правого операнда;результат имеет ту же категорию значений, что и его правый операнд [...]

, поэтому преобразование массива в указатель не требуется, и в итоге мы получаем:

sizeof( char[100] ) 
1 голос
/ 27 июня 2011

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

 char *arr;
 struct xxx { ... } v;

Я напишу

sizeof arr 
sizeof v

, но

sizeof (struct xxx)       /* Note the space after the sizeof, it's important */
sizeof (char *)

Я делаю то же самое с return без круглых скобок, так как это не вызов функции, и если я ставлю круглые скобки, это потому, что они нужны для выражения, следующего за ним.

...