C++ sizeof

J

Jess

Hello, if I have the following code that has an array of int*:

#include<iostream>
#include<string>
#include<cstring>
#include<cstddef>

using namespace std;

int main(){
int x = 1;
int y = 2;
int z = 3;

int* a[] = {&x,&y,&z};
int* c[] = {&x,&y};

cout << sizeof(a) << endl;
cout << sizeof(c) << endl;

return 0;
}

The output is 12 and 8. "sizeof" is supposed to return the number of
bytes its argument occupies according to its argument type, i.e.
without actually evaluating its argument. Since both "a" and "c" have
type "int**", why does "sizeof" return different values?

Thanks.
 
V

Victor Bazarov

Jess said:
Hello, if I have the following code that has an array of int*:

#include<iostream>
#include<string>
#include<cstring>
#include<cstddef>

using namespace std;

int main(){
int x = 1;
int y = 2;
int z = 3;

int* a[] = {&x,&y,&z};
int* c[] = {&x,&y};

cout << sizeof(a) << endl;
cout << sizeof(c) << endl;

return 0;
}

The output is 12 and 8. "sizeof" is supposed to return the number of
bytes its argument occupies according to its argument type, i.e.
without actually evaluating its argument. Since both "a" and "c" have
type "int**", why does "sizeof" return different values?

No, 'a' has the type 'int*[3]', and 'c' has the type 'int*[2]'. They
are _arrays_, not pointers. If your compiler supports decent typeid
information, you could output the type yourself

cout << typeid(a).name() << endl;

V
 
D

dragoncoder

Jess said:
Hello, if I have the following code that has an array of int*:

using namespace std;
int main(){
int x = 1;
int y = 2;
int z = 3;
int* a[] = {&x,&y,&z};
int* c[] = {&x,&y};
cout << sizeof(a) << endl;
cout << sizeof(c) << endl;
return 0;
}
The output is 12 and 8. "sizeof" is supposed to return the number of
bytes its argument occupies according to its argument type, i.e.
without actually evaluating its argument. Since both "a" and "c" have
type "int**", why does "sizeof" return different values?

No, 'a' has the type 'int*[3]', and 'c' has the type 'int*[2]'. They
are _arrays_, not pointers. If your compiler supports decent typeid
information, you could output the type yourself
I thought sizeof when applied to an array gives the number of elements
in the array. Shouldn't the output be 3 and 2 ?
 
V

Victor Bazarov

dragoncoder said:
Jess said:
Hello, if I have the following code that has an array of int*:

using namespace std;
int main(){
int x = 1;
int y = 2;
int z = 3;
int* a[] = {&x,&y,&z};
int* c[] = {&x,&y};
cout << sizeof(a) << endl;
cout << sizeof(c) << endl;
return 0;
}
The output is 12 and 8. "sizeof" is supposed to return the number of
bytes its argument occupies according to its argument type, i.e.
without actually evaluating its argument. Since both "a" and "c"
have type "int**", why does "sizeof" return different values?

No, 'a' has the type 'int*[3]', and 'c' has the type 'int*[2]'. They
are _arrays_, not pointers. If your compiler supports decent typeid
information, you could output the type yourself
I thought sizeof when applied to an array gives the number of elements
in the array.

Why would you think that?
Shouldn't the output be 3 and 2 ?

Yes, but only if sizeof(int) == 1.

V
 
O

Owen Ransen

I thought sizeof when applied to an array gives the number of elements
in the array. Shouldn't the output be 3 and 2 ?

sizeof, in general, gives the size of the object in bytes.

It is useful for when you do memset or memcmp or memcpy
which requires sizes in bytes...


Easy to use graphics effects:
http://www.ransen.com/
 
V

Victor Bazarov

Owen said:
sizeof, in general, gives the size of the object in bytes.

It is useful for when you do memset or memcmp or memcpy
which requires sizes in bytes...

It's useful in many other places, like 'write' or 'read' or 'malloc'...
They all operate in bytes, of course.

Generally speaking there is no sense to make 'sizeof' behave differently
depending on the argument's being an array versus a scalar object. It's
more important for 'sizeof' to be consistent. Since returning 1 or 0
for non-array object is useless, 'sizeof' was made to return byte count
and for consistency reasons it does the same for any type of argument.

If one needs to know the size of the array, one can always get it like
this:

sizeof(somearray) / sizeof(somearray[0])

V
 
B

blangela

Also remember that the sizof operator does it's thing at compile
time. If the compiler cannot correctly compute the size of the
arguement passed to the sizeof operator, it will not return the result
you may have been expecting. For example if you pass an array as an
arguement to a function, the sizeof operator will not return the size
of the array (when used in the function on the passed array), but
instead will return the size of the pointer which points to the array.
 
V

Victor Bazarov

blangela said:
Also remember that the sizof operator does it's thing at compile
time. If the compiler cannot correctly compute the size of the
arguement passed to the sizeof operator, it will not return the result
you may have been expecting. For example if you pass an array as an
arguement to a function, the sizeof operator will not return the size
of the array (when used in the function on the passed array), but
instead will return the size of the pointer which points to the array.

That all depends on how you pass it. The thing is, 'sizeof' always
calculates the size of the object (based on the static type), or of the
type, or the expression (with all promotions/conversions applied), and
it is only unable to do that in an illegal program, from, say incomplete
type. If the program compiles, 'sizeof' did what is asked of it. And
since references are not objects, if you pass the reference to 'sizeof',
you will get the size of the referred object; which brings us to

#include <cstddef>
template<class T>
size_t getsize(T &t) // passing by reference
{
return sizeof t;
}

int main()
{
int a[10];
size_t s = getsize(a); // s will be 10*sizeof(int)
}


V
 
J

Jess

No, 'a' has the type 'int*[3]', and 'c' has the type 'int*[2]'. They
are _arrays_, not pointers. If your compiler supports decent typeid
information, you could output the type yourself

cout << typeid(a).name() << endl;

Although "a" and "c" are arrays, when we use their names as a values,
they are effectively pointers, aren't they? so "a" is the pointer
pointing to the first element of the array. since each element in "a"
is a "int*", "a" should be "int**", where have I done wrong?
moreover, if we do: "*a", then it's "&x", isn't it? Thanks.
 
J

Jess

Also remember that the sizof operator does it's thing at compile
time. If the compiler cannot correctly compute the size of the
arguement passed to the sizeof operator, it will not return the result
you may have been expecting. For example if you pass an array as an
arguement to a function, the sizeof operator will not return the size
of the array (when used in the function on the passed array), but
instead will return the size of the pointer which points to the array.

do you mean in my problem:

#include<iostream>
#include<string>
#include<cstring>
#include<cstddef>

using namespace std;

int main(){
int x = 1;
int y = 2;
int z = 3;

int* a[] = {&x,&y,&z};
cout << sizeof(a) << endl;

return 0;

}

since I pass "a" that is an array to sizeof, it will return the size
of the "int*"? This is what I expected but the result was 12, instead
of 1 or something else.
 
J

Jess

That all depends on how you pass it. The thing is, 'sizeof' always
calculates the size of the object (based on the static type),

what is "static type"?
or of the type, or the expression (with all promotions/conversions applied),

what kind of promotions/conversions?
and it is only unable to do that in an illegal program, from, say incomplete
type.

When could an incomplete type occur?
If the program compiles, 'sizeof' did what is asked of it. And
since references are not objects, if you pass the reference to 'sizeof',
you will get the size of the referred object;

if I pass a pointer (instead of a reference), then do I get the size
of the pointer? similarly, if I pass an object (pass-by-value), then
do I get the size of the object?

Thanks.
 
V

Victor Bazarov

Jess said:
No, 'a' has the type 'int*[3]', and 'c' has the type 'int*[2]'. They
are _arrays_, not pointers. If your compiler supports decent typeid
information, you could output the type yourself

cout << typeid(a).name() << endl;

Although "a" and "c" are arrays, when we use their names as a values,
they are effectively pointers, aren't they?

If they are used in an expression other than in 'sizeof' or 'typeid',
or as a template argument, or as an initialiser of a reference of the
array type.
so "a" is the pointer
pointing to the first element of the array. since each element in "a"
is a "int*", "a" should be "int**", where have I done wrong?

I don't understand the "where have I done wrong" question.
moreover, if we do: "*a", then it's "&x", isn't it? Thanks.

Again, you lost me here. What's 'x'?

V
 
V

Victor Bazarov

Jess said:
Also remember that the sizof operator does it's thing at compile
time. If the compiler cannot correctly compute the size of the
arguement passed to the sizeof operator, it will not return the
result you may have been expecting. For example if you pass an
array as an arguement to a function, the sizeof operator will not
return the size of the array (when used in the function on the
passed array), but instead will return the size of the pointer which
points to the array.

do you mean in my problem:

#include<iostream>
#include<string>
#include<cstring>
#include<cstddef>

using namespace std;

int main(){
int x = 1;
int y = 2;
int z = 3;

int* a[] = {&x,&y,&z};
cout << sizeof(a) << endl;

return 0;

}

since I pass "a" that is an array to sizeof, it will return the size
of the "int*"? This is what I expected but the result was 12, instead
of 1 or something else.

You got the result you were supposed to get: sizeof(int*) * 3
because 'a' is an array.

V
 
V

Victor Bazarov

Jess said:
what is "static type"?

The type of the expression at compile time.
what kind of promotions/conversions?

Standard promotions/conversions, for example when you say a+b, and
'a' is of type 'char' and 'b' is of type 'long', the expression
will have the type 'long'.
When could an incomplete type occur?

When you only declare it (like this

struct A;

) and don't define it.
if I pass a pointer (instead of a reference), then do I get the size
of the pointer?
Sure.

similarly, if I pass an object (pass-by-value), then
do I get the size of the object?

Yes.

V
 
J

Jess

If they are used in an expression other than in 'sizeof' or 'typeid',
or as a template argument, or as an initialiser of a reference of the
array type.

By "template argument" do you mean something like this?
template<class X>
void foo(X* a);

where "a" is an array, each of its elements has type "X"?

Can you please elaborate a bit on "as an initialiser of a reference of
the array type"? :)
I don't understand the "where have I done wrong" question.

I meant my deduction tells me "a" should have type "int**", but
instead, it's an array of int*.
Again, you lost me here. What's 'x'?

I defined "a" in this program:

#include<iostream>
#include<string>
#include<cstring>
#include<cstddef>

using namespace std;

int main(){
int x = 1;
int y = 2;
int z = 3;

int* a[] = {&x,&y,&z};
int* c[] = {&x,&y};

cout << sizeof(a) << endl;
cout << sizeof(c) << endl;

return 0;

}

Thanks.
 
J

Jess

The type of the expression at compile time.



Standard promotions/conversions, for example when you say a+b, and
'a' is of type 'char' and 'b' is of type 'long', the expression
will have the type 'long'.



When you only declare it (like this

struct A;

) and don't define it.



Yes.

Thanks a lot. :)
 
V

Victor Bazarov

Jess said:
By "template argument" do you mean something like this?
template<class X>
void foo(X* a);

where "a" is an array, each of its elements has type "X"?

No. In this case, if 'foo' is called like 'foo(a)' and 'a' is, in
fact an array, the X will be deduced as the element type because 'a'
will decay. However, if you drop the asterisk, and then call 'foo'
with a true array, 'X' is likely to be deduced as "an array of N of
blah".
Can you please elaborate a bit on "as an initialiser of a reference of
the array type"? :)

int (&ra)[10] = a;

here 'a' does not decay.
I meant my deduction tells me "a" should have type "int**", but
instead, it's an array of int*.

What deduction? You only provide the conclusion, not the path that
you took to reach it. How could I point out the mistake somewhere
on that path when you only indicate the destination?
Again, you lost me here. What's 'x'?

I defined "a" in this program:

#include<iostream>
#include<string>
#include<cstring>
#include<cstddef>

using namespace std;

int main(){
int x = 1;
int y = 2;
int z = 3;

int* a[] = {&x,&y,&z};
int* c[] = {&x,&y};

cout << sizeof(a) << endl;
cout << sizeof(c) << endl;

return 0;

}

Thanks.

Alright, even though I've lost your trail of thought, the
answer is 'yes', *a equals &x.

V
 
J

Jess

No. In this case, if 'foo' is called like 'foo(a)' and 'a' is, in
fact an array, the X will be deduced as the element type because 'a'
will decay. However, if you drop the asterisk, and then call 'foo'
with a true array, 'X' is likely to be deduced as "an array of N of
blah".

If foo's signature is

void foo(X* a);

Then when I call foo(a), then the compiler knows foo is expecting a
pointer-type argument and hence array "a" decays to a pointer. Is this
what happens? If the signature of foo becomes

void foo(X a);

Then when I pass an array "a" to foo like "foo(a)", then does the
compiler always forbid "a" to decay? Why does a compiler do that? Is
array the only data structure with this behaviour?

This reminds me of another question. What I've learned before is that
if we have a function whose argument is not a pointer type or
reference type, then it's always pass-by-value. Following this
reasoning, it seems if we do "foo(a)", then the compiler will copy "a"
to "foo". Now the problem arises. What does it mean to copy an
array? Do we copy its contents or the address of its first element?

Can you please elaborate a bit on "as an initialiser of a reference of
the array type"? :)

int (&ra)[10] = a;

here 'a' does not decay.

Why does compiler do that? The different treatments to arrays are
very complicated, why would C++ compilers make things so hard? I
imagine there must be some reason behind it. more likely to do with
the way compilers work. Can you please tell me the reason behind all
of these complications, as it helps me to understand the strange
behaviour?

What deduction? You only provide the conclusion, not the path that
you took to reach it. How could I point out the mistake somewhere
on that path when you only indicate the destination?

I skipped my deduction path, sorry. :) But I understand what you mean
now. :)
Thanks,
Jess
 
J

James Kanze

If one needs to know the size of the array, one can always get it like
this:
sizeof(somearray) / sizeof(somearray[0])

Note that this is very error prone, as it will silently give
wrong results when applied to a pointer. In C++, a much better
solution is:

template< typename T, size_t N >
inline size_t
size( T (&array)[ N ] )
{
return N ;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top