Array-pointer issue

Hello, everyone. I need your help.

Consider the following fragment of code:

1
2
3
4
5
6
7
8
9
10
11
    int *nobody;

    nobody = new int(420);

    int **p;

    p = &nobody; // works

    cout << nobody << endl;
    cout << **p << endl;
    


This works fine, nothing wrong about that!
But, as per as I know, an array-name without index is a pointer to the first element of that array. So, consider the following code:

1
2
3
4
int a[3] = { 11, 12, 13 };

    int **q;
    q = &a; // compile-time error 



Here I think a and nobody are both pointers to integer.
And then I try to assign its address to a pointer to pointer- q, the same thing as I did in the previous code.
But, this results in a compile-time error saying- "cannot convert to something from something...".

Now, can someone please give me a clear idea about the difference between these two criteria?
Last edited on
But, as per as I know, an array-name without index is a pointer to the first element of that array.

No, it is not. In some situations it may behave as one, but the type of an array is fundamentally different from a pointer type. The type of &a is pointer-to-array-of-3-int, which obviously doesn't match the type of q which is pointer-to-pointer-to-int.
Then how is the following code possible:


1
2
3
4
5
int *p;

int a[3] = { 11, 12, 13 };

p = a; // this is okay 


Please explain...
It looks to me like an array is inplicitly convertible to a pointer BUT! an array of arrays, or a 2D-array, is NOT implicitly convertible to a pointer to pointer because the pointers wouldn't know the size of each dimension and so it wouldn't make any sense...
(I'm not sure about how exactly it works though)

You could hack a little bit and cast the 2D-array to int*.
Note that the pointer still does NOT know the dimensions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Example program
#include <iostream>

int main()
{
    int arr1[5] = {1,2,3,4,5};
    int arr2[2][5] = {{1,2,3,4,5}, {6,7,8,9,10}};
    
    int* p1 = arr1;
    //int* p2 = (int*)arr2;
    int* p2 = reinterpret_cast<int*>(arr2);
    
    for(int i = 0; i < 5; ++i) {
        std::cout << p1[i] << " ";
    } std::cout << std::endl;
    
    for(int i = 0; i < 2*5; ++i) {
        std::cout << p2[i] << " ";
    } std::cout << std::endl;
}
Last edited on
Then how is the following code possible:

What's going on when you assign a one dimensional array to a pointer of the same time, and also when you pass one to a function which takes a pointer of the same type, is referred to as array decay.

what is array decaying?
http://stackoverflow.com/questions/1461432/what-is-array-decaying

Notes

- The decay mechanism only works with the first index.
- when used in a function declaration, [] means pointer! e.g. void message(char text[]); This is apparently hang over from BCPL, the language that C was derived from.

(This bit of random knowledge comes from this article by Dennis Ritchie:
The Development of the C Language
http://people.fas.harvard.edu/~lib113/reference/c/c_history.html
I found while hunting for the reason that * binds to the right, i.e. why writing
int* a = 0, b = 0; is dangerous.)

You could hack a little bit and cast the 2D-array with a reinterpret_cast to int*.

You don't have to be quite so brutal: either use a single [0] to get a row and rely on it to decay to a pointer, or two [0]'s to acquire the first element and take its address. No coercion required.

int* p2 = arr2[0];

int* p2 = &arr2[0][0];

Andy

PS When you wrote

int* nobody = new int(420);

you did realise you were new-ing a single int with value of 420 rather than an array of 420 ints? (Just checking as the following discussion has been about arrays?)
Last edited on
PS When you wrote

int* nobody = new int(420);

you did realise you were new-ing a single int with value of 420 rather than an array of 420 ints? (Just checking as the following discussion has been about arrays?)



Yes, understand that.

But I learnt that array is a contiguous memory, and its name is a pointer to the first element of that array. So the name of an integer-array points to an integer I guess. So, I compared with it another pointer to integer.

No, it is not. In some situations it may behave as one,


So, when these things differ? When do I need to care for 'array decay' kind of stuffs?
Last edited on
So, when these things differ? When do I need to care for 'array decay' kind of stuffs?
Whenever you loose the number of dimensions of the array, i guess, that's whenever you have more than 1 dimension
I heard about it for the first time today so you shouldn't have any problems

Note that we wrote some examples how you can convert an array to a pointer
When do I need to care for 'array decay' kind of stuffs?

Well, suppose you have some working code...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int main()
{
    int data[4] = {1, 2, 3, 4};

    // use usual way of getting array size (size of
    // whole array divided by size of one element.)
    int max = sizeof(data)/sizeof(data[0]);

    cout << "data :";
    for(int i = 0; i < max; ++i)
        cout << " " << data[i];
    cout << "\n\n";

    return 0;
}


data : 1 2 3 4


And you decide it's best to refactor your code into a function before adding new functionality...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;

// print my 4 digit array
void dump(int data[4]) {
    // use usual way of getting array size (size of
    // whole array divided by size of one element.)
    int max = sizeof(data)/sizeof(data[0]);

    cout << "data :";
    for(int i = 0; i < max; ++i)
        cout << " " << data[i];
    cout << "\n\n";
}

int main()
{
    int data[4] = {1, 2, 3, 4};

    dump(data);

    return 0;
}


data : 1


The problem is that due to array decay the 4 is ignored; dump's 'data' param is just a pointer, which means the sizeof calculation goes wrong.

Andy
Last edited on
If you want to find out what type the compiler thinks a variable is, you can use the typeinfo mechanism
http://www.cplusplus.com/reference/typeinfo/

The code below gets a little bit involved as GCC returns a 'mangled' name which need to be converted into a friendly name.

Andy

GCC (MinGW)

q -> 11 12 13

type of a : int [3]
type of &a : int (*) [3]
type of q : int (*) [3]
type of q_take1 : int**


Visual Studio 10 C++ compiler

q -> 11 12 13

type of a : int const [3]
type of &a : int (*)[3]
type of q : int (*)[3]
type of q_take1 : int * *


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <string>
#include <typeinfo>
#include <memory>
#include <cstdlib>
using namespace std;

#ifdef __GNUG__
#include <cxxabi.h>
#endif

// GCC returns 'mangled' names from std::typeid::name so need to convert them
// to a meaningful name.
//
// "Borrowed" from:
// Unmangling the result of std::type_info::name
// http://stackoverflow.com/questions/281818/unmangling-the-result-of-stdtype-infoname
string demangle(const char* name);
template <class T>
string type(const T& t) {
    return demangle(typeid(t).name());
}

int main() {
    int a[3] = { 11, 12, 13 };

    // normal pointer to pointer to int
    int **q_take1 = NULL;
    // error: cannot convert 'int (*)[3]' to 'int**' in assignment

    // pointer to array of three ints
    int (* q)[3] = NULL;

    q = &a;

    cout << "q ->";
    for(int i = 0; 3 > i; ++i)
        cout << " " << (*q)[i]; // note dereference
    cout << "\n\n";

    cout << "type of a : " << type(a) << "\n";
    cout << "type of &a : " << type(&a) << "\n";
    cout << "type of q : " << type(q) << "\n";
    cout << "type of q_take1 : " << type(q_take1) << "\n";
 
    return 0;
}

#ifdef __GNUG__
struct handle {
    char* p;
    handle(char* ptr) : p(ptr) { }
    ~handle() { free(p); }
};

string demangle(const char* name) {
    int status = -4; // some arbitrary value to eliminate the compiler warning
    handle result( abi::__cxa_demangle(name, NULL, NULL, &status) );
    return (status == 0) ? result.p : name ;
}
#else
// does nothing if not g++
string demangle(const char* name) {
    return name;
}
#endif 
Last edited on
Thanks to everybody.
The more I learn, the more I realize how little I know!
Last edited on
Topic archived. No new replies allowed.