views:

298

answers:

5

This question is about C++

I always thought that name of an array in C++ is only a pointer, so I thought that

int ar[10];
cout << sizeof(ar);

will give me the same as sizeof(int *). But it gives 40 - so it is real size of whole array. It also gives 40 when array size is given by variable:

int n = 10;
int ar[n]

I want to copy a class that contains an array. If it would be allocated with operator new then I should manualy copy this array within a copy constructor. But how about constant sized arrays? Does class contains only a pointer to an array, or does it contains whole array? Is simple memcpy(...) safe here?

EDIT: Another example:

int n;
cin >> n;
int ar[n];
cout << sizeof(ar);

and it prints n*4. I'm using g++ on linux.

I even tried this:

class Test {
public:
    int ar[4];
};

and

Test a, b, c;
a.ar[0] = 10;
b = a;
memcpy(&c, &a, sizeof(a));
a.ar[0] = 20;
cout << "A: " << a.ar[0] << endl;
cout << "B: " << b.ar[0] << endl;
cout << "C: " << c.ar[0] << endl;

and it gives:

A: 20
B: 10
C: 10

So array is stored as a part of a class and can be copied with memcpy. But is it safe?

+1  A: 

sizeof(ar) only works because the declaration of ar is visible to the compiler at that point.
Array sizes are not stored and never available in runtime, if you pass ar to a function and do sizeof(ar) there you'll get 4. So its basically that your compiler is smart enough to look a couple of lines above.

sizeof is a compiler keyword that will deliver a size in compile time. So the compiler must be able to know, or deduce, the size of the var you are looking at to give the proper size.

REEDIT: : sizeof is a compile-time constant [except for C99 variable-length arrays] where the compiler can add runtime computation. This however is a compiler extension to C++ as C99 is not part of the C++ standard.

Arkaitz Jimenez
I also tried: int n; cin >> n; int ar[n]; cout << sizeof(ar); and it also gives size of whole array, not a pointer.
klew
where do you initialize n?
Arkaitz Jimenez
When I declare size of an array in a runtime, compiler doesn't know anything about this size. But sizeof(ar) it still gives size of whole array. btw. I'm using g++
klew
So on my compiler sizeof isn't a compiler keyword, because it gives different results when I run the same program with different parameters given at runtime.
klew
I think after C99 dynamic arrays support runtime sizeof, but thats probably a compiler extension for C++ as I don't think the C++ standard supports runtime sizeof
Arkaitz Jimenez
@klew sizeof must be a compiler keyword for any compiler that claims to support C or C++.
anon
Interesting note about variable-length arrays. Thanks! @Neil: I said it only because sizeof appeared to work as a function on variable-leght arrays.
klew
+1  A: 

Comment : in standard C (C89) you should only be able to initialize an array with a literal or a const. So there is no confusion for the compiler.

Answer : If you initialize an array in the "on the stack way" -- i.e., without using new or malloc. The class would encapsulate the size of the array. The base of an array is a pointer because the compiler uses pointer arithmetic internally to resolve the access operator [].

If you use new or malloc, then you must use variable that you used to allocate memory as the definitive measure of the size of the memory you allocated. you may use memcpy() with the array base pointer, as irrespective of where it is allocated, it is still a pointer to the base memory location of the array.

answer 2: after question edits

Yes it is perfectly safe to do that with const-sized arrays of the same type and of the same size, but do not do it with dynamically allocated resources unless you are tracking their types and sizes. Just avoid the approach you are taking and go with a container class.

Note : I have evaded your point on the sizeof operator on heap allocated memory as I cannot say what the behaviour is. I am old-school and I would never use sizeof on dynamic resources as it depends on runtime machinery, and who knows what runtime machinery different compiler vendors include.

However, I know this, that when an array is alloced by new (on most compilers) in C++ it places a integer -- physically -- at the base of the array. This integer represents the size of the array that follows. This is why you must use the delete [] operator, this operator differs from standard delete because it causes the compiler to spit out a loop which itteratively calls the destructor on your items. If a C++ compiler vendor place an integer at the base of an array than they might use this at runtime to extract it for the sizeof operator. I have no idea what the standard says about it, and I doubt it works this way.

TEST: Visual Studio 2008

#include "stdafx.h"
#include <iostream>


int _tmain(int argc, _TCHAR* argv[])
{
    int x;
    int *y=new int[5];
    int z[5];
    std::cout << sizeof(x);
    std::cout << " " <<  sizeof(y);
    std::cout << " " << sizeof(z);
    return 0;
}

OUTPUT:

4 4 20

You can only determine the size of an array at runtime if its hard-coded -- i.e., like in the case of z. x is an int and y is a pointer both behave as such.

Hassan Syed
Hm, but when I use new then I don't have information about the size. sizeof returns size of a pointer.
klew
In Standard C, aka c99, you can create an array using a variable as the size. You cannot do this in C++.
anon
The standard doesn't mention how to store the size for `new[]` - thats an implementation detail.
Georg Fritzsche
Take a look on my "another example" and try it on your compiler. Your example on my compiler gives the same results as yours.
klew
Your "knowledge" regarding C++ implementation of dynamically allocated arrays only applies to your specific implementation. Other implementations may do things differently.
anon
Your first sentence: Comment: in standard C ....
anon
@Hassan: You say *"when an array is alloced by new in C++"* - not *"my compiler does [...]"*. Try to take it as constructive criticism.
Georg Fritzsche
+1  A: 

Have you looked into STL's vector. That behaves similar to an array, has adjustable size, and contains a function "size()" which returns the size of the array.

wheaties
I think it won't work with memcpy.
klew
But std::vector has its own copy methods, so instead of having the client perform the copy, std::vector performs the copy.
Thomas Matthews
In general you are right. But I wanted to use it in MPI library and this data will be transfered to other process with something similar to memcpy() - it simply copy byte by byte from specific address.
klew
wheaties
I think you are right, but I still wouldn't use it :). I want to copy byte per byte whole class - it require just one single function to do it without any additional work on it. If I'd use vector in that class then I should copy class data and all vectors separately.
klew
+8  A: 

An array is not a pointer. The name of an array "decays" to a pointer when/if you pass it as a parameter to a function -- but sizeof is an operator built into the language, not a function, so sizeof(array) yields the actual size of the array as long as it's applied to the actual array (as opposed to the name of the array being passed as a parameter, then using sizeof() on the pointer that it decayed to when it was passed to the function.

As far as copying a class that contains an array, if it's really an array, like:

class X { 
    int x[10];
};

Then you don't need to do anything for it to be copied -- the compiler can/will generate a copy constructor that copies the contents of the array. If (and only if) you actually have a pointer, and allocate the space yourself, do you need to write a copy ctor to do a "deep copy" (i.e. allocate space in the new object, and copy the data pointed TO by the pointer). Rather than doing this, however, you should normally use an std::vector, which does all that internally so you don't have to worry about it.

Jerry Coffin
The best way to realize that sizeof is not a function is when you see that the parentheses are optional; `sizeof(x)` is the same as `sizeof x`.
Mark Ransom
@Mark:Except that can be misleading, because the parens aren't always optional. For a value, you can omit the parens, but for a type, you can't. e.g. `sizeof(int)` will compile, but `sizeof int` will not.
Jerry Coffin
+4  A: 

Taking these one at a time:

I want to copy a class that contains an array.

Okay, so for example:

class Foo
{
    int arr[20];
};

If it would be allocated with operator new then I should manualy copy this array within a copy constructor.

Okay, now the confusion sets in. In the above example, the array is actually part of the object. sizeof(Foo) would give you 80 if int is 4 bytes.

An alternative would be to have a pointer to an array, which is useful if the array needs to change size:

class Bar
{
    int *arr;
};

In that case, sizeof(Bar) is the size of a pointer (usually 4 or 8 bytes), and copying the object copies the pointer. This is called a "shallow copy". If you wanted a "deep copy", i.e. a copy would duplicate the array's contents and not just the object, then you need a copy constructor.

The third alternative is to use vector, as wheaties recommends:

class Bob
{
    std::vector<int> arr;
};

This internally works the same as the Bar case, and the vector can be resized, but the vector template takes care of the deep copy for you, so you don't need a copy constructor.

I'd recommend the Foo case if you need a fixed-size array, where the size is known at compile time, and the Bob case otherwise. The Bar case is pretty much just reinventing the wheel.

Is simple memcpy(...) safe here?

Safe for Foo. Safe for Bar if you want a shallow copy. Unsafe for Bob.

The moral of the story is that storing a variable in an object works just like storing it in a function block or global: if you specify an array ([N] instead of *), the size had better be determined at compile time, and you get the storage placed right there.

Mike DeSimone
So constant sized arrays in classes are a part of a class. Class don't store a pointer to an array. Is this a standard or just an implementation?
klew
It's standard behavior. I'm sure someone else knows where it is in the C standard, but consider that, to C++, a `class` and a `struct` are the same thing, with one difference: the members of a `struct` are public by default, and the members of a `class` are private by default. For purposes of storage, copy semantics, etc., a `struct` and `class` work the same.
Mike DeSimone