sizeof taking two arguments

天涯浪子 提交于 2019-11-27 18:44:50

In C then the array is decaying to a pointer, because of the different specification of the comma operator with relation to rvalues and lvalues (not the only place such a difference can be found). In C++ then the array stays an array, yielding the correct result.

In C, comma operator doesn't produce an lvalue, consequently the array arr which is an lvalue decays into a pointer type which is a rvalue (in this case). So sizeof(0,arr) becomes equivalent to sizeof(char*), due to lvalue-to-rvalue conversion.

But in C++, comma operator produces an lvalue. There is no lvalue-to-rvalue conversion. So sizeof(0,arr) remains same, which is equivalent to sizeof(char[100]).

By the way, sizeof is not a function, it's an operator. So the following is completely valid C++ (and C, if you imagine printf instead of cout):

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

Demo : http://www.ideone.com/CtEhn

You might think that I've passed 4 operands to sizeof but that is wrong. sizeof operates on the result of the comma operators. And its because of the many comma operators you see many operands.

4 operands with 3 comma operators; just like in 1+2+3+4, there're 3 operators, 4 operands.

The above is equivalent to the following (valid in 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

Demo : http://www.ideone.com/07VNf

So it's the comma operator which makes you feel that there are many arguments. Here comma is an operator, but in function call, comma is NOT an operator, its simply argument separator.

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

So sizeof(a,b,c,d) operates on the type of the result of , operators, exactly in the same way, sizeof(1+2+3+4) operates on the type of the result of + operators.

Also note that you cannot write sizeof(int, char, short), precisely because comma operator cannot operate on types. It operates on value only. I think, sizeof is the only operator in C and C++, which can operate on types as well. In C++, there is one more operator which can operates on types. Its name is typeid.

It is a comma operator. And the difference you are talking about has absolutely nothing to do with sizeof. The difference is really in lvalue-to-rvalue, array-to-pointer and similar decay behaviors between C and C++ languages.

C language is rather trigger-happy in this regard: arrays decay to pointers practically immediately (with the exception of very few specific contexts), which is why the result of 0, arr expression has char * type. It is equivalent to 0, (char *) arr.

In C++ language arrays preserve they "arrayness" much longer. When used in the context of , operator arrays don't decay to pointers (and lvalues do not decay to rvalues), which is why in C++ the type of 0, arr expression is still char[100].

This is what explains the difference in sizeof behavior in that example. ?: operator is another example of an operator that demonstrates the similar difference in decay behavior, i.e. sizeof(0 ? arr : arr) will give you different results in C and C++. Basically, it all stems from the fact that C operators don't usually preserve the lvalueness of their operands. A lot of operators can be used to demonstrate this behavior.

This is not sizeof taking two arguments. sizeof is an operator, not a function.

Consider that (0, arr) is an expression using the comma operator, and everything else falls into place.

sizeof doesn't take two arguments. But it's not a function, either, so the (...) don't delimit function arguments, they're just an optional part of the syntax, and enforce grouping. When you write sizeof(0, arr), the argument to sizeof is the single expression 0, arr. A single expression with a comma operator, which evaluates the expression to the left of the comma, throws out its value (but not its side effects), then evaluates the expression to the right of the comma, and uses its value as the value of the complete expression.

I'm not sure about C, but this could be a difference between the langauges. In C++, the array-to-pointer conversion doesn't occur unless it is needed; in C, if I recall correctly, the standard says that it always takes place except in certain contexts. Including as the operator of sizeof. In this case, since the comma operator doesn't have an constraints with regards to the types of its operands, the array-to-pointer conversion doesn't take place in C++. In C, an operatand of the comma operator isn't listed in the exceptions, so the array-to-pointer conversion does take place. (In this case, the array is an operand of the comma operator, and not of sizeof.)

The best way to see what could be going on here is look at the grammar in the standard. If we look at the draft C99 standard section 6.5.3 Unary operators paragraph 1 we can see that the grammar for sizeof is:

sizeof unary-expression
sizeof ( type-name )

So the second one does not apply but how does the sizeof unary-expression apply in this case? If we look at section A.2.1 Expressions from the draft standard and work through the grammar like so:

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

we get the parenthesizes around an expression and now we just have to look at the grammar for comma operator from section 6.5.17 Comma operator and we see:

expression:
  assignment-expression
  expression , assignment-expression

So we have now have:

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

both expression and assignment-expression can take us to primary-expression which has the following grammar:

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

and 0 is a constant and arr is an identifier so we have:

 sizeof( constant , identifier )

So what does the comma operator do here? Section 6.5.17 paragraph 2 says:

The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation. Then the right operand is evaluated; the result has its type and value.97)

since the comma operator is not one of the exceptions where a array is not converted to a pointer it yields a pointer(this is covered in section 6.3.2.1 Lvalues, arrays, and function designators) which means we end up with:

sizeof( char * )

In C++ the grammar is pretty similar so we end in the same place but the comma operators works differently. The C++ draft standard section 5.18 Comma operator says:

[...]The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand[...]

so and array-to-pointer conversion is not not required and so we end up with:

sizeof( char[100] ) 

As several have already said, and I want to add only one thing, sizeof is an operator taking either an expression or a cast expression. For this reason I took the habit to write paranthesis to a sizeof only if it is a cast expression.

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

I will write

sizeof arr 
sizeof v

but

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

I do the same with return no parenthesis, as it is not a function call, and if I put parenthesis it's because the expression following needs them.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!