sizeof taking two arguments
Asked Answered
P

7

37

In C.1.3 of the C++ IS (2003. It's in the C++11 IS, too), the standard points out a difference between ISO C and C++; namely, for

char arr[100];

sizeof(0, arr) returns sizeof(char*) in C, but 100 in C++.

I can find no documentation for sizeof taking two arguments. The obvious fallback is the comma operator, but I don't think so: sizeof(arr) in C is 100; sizeof(0, arr) is sizeof(char*). Both sizeof(0, arr) and sizeof(arr) are 100 in C++.

I may be missing the whole point of the IS in this context. Can anyone help? This is similar to a question discussed back in '09, but no one referred to the IS, and I don't think the correct answer was given.


Edit: Actually, the IS is talking about the comma operator. So, for some reason (0, arr) returns a char* in C, but a char[100] in C++. Why?

Pagination answered 13/6, 2011 at 14:11 Comment(4)
Actually, the IS is talking about the comma operator, but I still don't understand what is going on.Pagination
Funny. I've never seen sizeof with 2 params. It's defined as a unary operator with optional use of (), which would sort of rule out multiple params.Gardant
@PeterRowell: Exactly. The expression being passed to sizeof here is using the comma operator. i.e. sizeof x where x is the expression (0, arr).Lowman
I took a somewhat different approach then the existing answers, that I think may be a bit more interesting.Carlenacarlene
Z
25

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.

Zoller answered 13/6, 2011 at 14:16 Comment(0)
A
49

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.

Aquatic answered 13/6, 2011 at 14:19 Comment(1)
Regarding the last paragraph: The new operator in C++ also operates on types.Age
Z
25

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.

Zoller answered 13/6, 2011 at 14:16 Comment(0)
B
6

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.

Bookplate answered 13/6, 2011 at 14:25 Comment(1)
+1 for not saying that the behavior is because , would yield a non-lvalue and for correctly saying that this is because of the trigger-happiness of C. And for not saying that the lvalue to rvalue conversion would be responsible for this.Bergquist
L
5

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.

Lowman answered 13/6, 2011 at 14:18 Comment(3)
The question said "The obvious fallback is the comma operator, but I don't think so", and you got 4 upvotes for "Consider ... the comma operator, and everything else falls into place"?Tupiguarani
@Michael: At the very least, I'm saying that the OP thinks wrong.Lowman
+1 for mentioning that sizeof is an operator. It takes an expression OR a cast expression.Dutyfree
C
5

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] ) 
Carlenacarlene answered 26/2, 2014 at 4:45 Comment(0)
K
3

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.)

Keniakenilworth answered 13/6, 2011 at 14:44 Comment(0)
D
1

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.

Dorthea answered 27/6, 2011 at 10:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.