0

I'm currently in programming 2 at my college. We have a test coming up and just wanted to ask for some confirmation on a few things I put together on pointers. Consider the following code:

//CASE 1

int Awesome = 10;        //Line 1
int *P = &Awesome;       //Line 2

cout << Awesome << endl; //Line 3
cout << *P << endl;      //Line 4

//CASE 2

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

cout << X[2] << endl;    //Line 3
cout << A[2] << endl;    //Line 4

Here's where my questions come in, but I think I sort of "stumbled" onto the answers of my questions, but I want to make sure its correct.

In the first line 2, I declare pointer P and give it Awesome's address by using the reference operator. In the second line 2, I declare pointer A and give it the address of X. I never understood why we didn't use the & operator in the case of giving an array address to a pointer. After thinking about it, I recall hearing that the array name is just a constant pointer containing the address the first subscript of the array. If thats correct, then it would make sense why we wouldn't need to use the & to obtain the address of the array, because the array name itself already contains the address. Is this correct?

My second question. In the first line 4, I'm cout-ing the contents of what pointer P is pointing at by using the dereference operator, but in the second 4th line, I don't need to use the * operator to obtain the value. In my head, I wasn't sure why it differed, but I think the concept in the paragraph above made it more clear. The array name itself and a pointer are basically the same thing (except the array name is a constant pointer I think). An array name is a constant pointer that holds the address and the pointer is getting this address. Therefore, we use the new pointer holding the new address the same way. Its not the same as the first instance at all. Since an array name and a pointer are basically the same, we don't need to use the special * operator, just use it like a normal array with array notation. Is this correct?

I've been struggling to understand why the two concepts above differed, why in one instance (int *P = &Address) we needed to use the & operator to give an address and why in another instance (int *A=X) we didn't. And I was struggling with why we use the * in one instance (cout << *P;) and not in another (cout << A[2];). The differences had me very confused, but if I'm anywhere close to being correct with my logic, it makes total sense now. Can anyone confirm this or correct it if its entirely wrong?

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
JosephTLyons
  • 2,075
  • 16
  • 39

3 Answers3

3

There is one important difference between arrays and pointers in C/C++.

Given:

int a[10];
int *p = a;

sizeof(a) will return 10*sizeof(int) (Probably == 40 or 80). sizeof(p) will return sizeof(int*) (Probably == 4 or 8).

Array derefrencing can also work on pointers in C/C++. So you could write p[1] or *(p + 1).

If it helps you can also write:

int *p = &a[0];

This is particular helpful in C++ for writing template code that works for std::vector, std::array, normal C/C++ arrays or any other container that stores its elements in a continuous block of memory and provides an array interface.

Edit: I would also add that due to aliasing compilers should be better able to optimize array accesses than pointer derefrencing.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
Jarra McIntyre
  • 1,265
  • 8
  • 13
  • Upvoted for some good points about templates, despite of the hurtful term "C/C++". – Christian Hackl Feb 20 '16 at 09:16
  • Thanks for the upvote. There are of course very important differences between the two languages. In this case I was meaning C-style arrays in C++ :) as opposed to std::array. – Jarra McIntyre Feb 20 '16 at 09:30
2

It is not formally correct that arrays and pointers are the same type.

Here's proof, using an overloaded function. The first overload takes an array by reference, the second takes a pointer by reference. It shows that int[8] and int* are distinct types.

#include <iostream>

void f(int (&x) [8]) {
    std::cout << "array type\n";
}

void f(int*& y) {
    std::cout << "pointer type\n";
}

int main()
{
    int x[8];
    int* y;

    f(x);
    f(y);
}

What's true is that arrays can easily be converted to a pointer to their first element. This often happens when beginners try to pass an array by value to a function. There must be a question on why this does not work every 2 or 3 days here at Stackoverflow. This conversion means that, for example, functions void f(int x[8]) and void f(int* x) would be identical to each other (and the compiler would complain about a redefinition). Above all, this means of course that the size information ([8]), which is part of the array type, is lost.

The conversion is informally called "decay", and it also happens at assignment or initialisation, like here:

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

This does not mean that int[3] and int* are identical, much like int and double are not identical just because you can write my_double = my_int;.

If thats correct, then it would make sense why we wouldn't need to use the & to obtain the address of the array, because the array name itself already contains the address. Is this correct?

No. You do need to use & to obtain the address of the array. You do not need it to obtain the value contained in its first element.

In your example:

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

std::cout << &X; would give you the address of the local array object.

Since an array name and a pointer are basically the same, we don't need to use the special * operator, just use it like a normal array with array notation. Is this correct?

Well, it depends on what you really mean with the weasel word "basically".

After all, the subscript operator is just syntactical sugar for pointer arithmetics [*]. t[n] is by definition equal to *(t + n) (which is, ironically, identical to *(n + t), which is consequently identical to n[t]).

int X[3]={0,0,5};        //Line 1
int *A = X;              //Line 2

cout << X[2] << endl;    //Line 3
cout << A[2] << endl;    //Line 4

In line 3, you do *(X + 2). That's fine. X "decays" to a pointer to its first element, and then 2 is added to that pointer. The result of the pointer addition is dereferenced.

In Line 4, you do *(A + 2). That's also fine. The only difference is that no "decay" takes place; A is already of a pointer type.


By the way... In your question and comments, you keep mentioning that an "array name itself is just holding an address". This means less than you seem to think it does. After all, it just states the very simple fact that the name of a variable denotes an object which lives in memory and therefore must have an address. You could say that about a simple int variable. For example, in int x = 123;, one could say that "the int name itself is just holding an address". This is somewhat true, but I don't think it's a useful piece of the puzzle for someone trying to understand arrays and pointers.


[*] Which proves of course that syntactical sugar can be very important to write maintainable code! :)

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • Being totally honest, some of this is a bit above my head. But I do understand that the array notation would be more useful as it tells the user (and the compiler?) how much information it is supposed to remember. X[3] tells the compiler that there are 3 elements to remember, but by passing that array address to the pointer, the compiler doesn't know the length. Is this what you are referring to by the "decay?" I apologize, I'm only into my second semester of programming, so some of it is well above my head. – JosephTLyons Feb 20 '16 at 09:46
  • @joe_04_04: It's above your head because it's awfully complicated and you just started learning. As for myself, it took years and a lot of practical experience for me to truly understand arrays. You know, C++ inherited the array concept from the ancient days of old C. It's not a really clean concept, and IMO it doesn't fit completely into the whole type system of C++. There's no real good reason for why you shouldn't be able to pass an array by value to a function, but that's the way it is. Of course, in real (good) C++ code you just use `std::array` or `std::vector` anyway. – Christian Hackl Feb 20 '16 at 09:53
  • @joe_04_04: "decay" means that an array's size (number of elements) information is lost. For example, if you have a local `int X[3]`, then the number of elements is `sizeof(X) / sizeof(int)`. The number of elements allows you to iterate the array, for example. If you just have `int*`, then there is no way to get this information. The devious thing is that you can write `int X[3]` into a function signature and the compiler will not complain or warn you. – Christian Hackl Feb 20 '16 at 09:56
  • Yes, out of all my years of formal subjects, programming is by far, the hardest subject I've ever attempted to study. My mind is quite literally melting now as we discuss topics in programming 201. My professor does all of his tests on paper (not sure how other schools/professors do it), so we really have to understand the concepts and can't rely on the compiler to help us out. I appreciate this, but its easily the most challenging subject I've ever attempted to study. I've gained a whole new appreciation for developers these 6 months. I'm understanding your comment more as I reread it. – JosephTLyons Feb 20 '16 at 09:58
  • @joe_04_04: Computer science should always start on paper. That's a good thing in my books! :) It also gives you a much nicer feeling to see theory put to life by the machine later on. – Christian Hackl Feb 20 '16 at 09:59
  • I agree with you, while its very difficult to read the output of lengthy convoluted code on paper, its helping us understand it much more. Anyway, thanks for the awesome help Christian! – JosephTLyons Feb 20 '16 at 10:03
  • @joe_04_04: By the way... one last thing. A helpful analogy (IMO) is to picture an array as almost the same thing as a bunch of individual variables, e.g. `int x[3] = { 1, 2, 3};` is almost the same as `int x1 = 1; int x2 = 2; int x3 = 3;`. This analogy is useful because it will always remind you how an array's element count is set in stone at run-time. – Christian Hackl Feb 20 '16 at 10:05
1

Your insights about arrays are correct. An array variable can be treated as a pointer. When you declare

int x[8];
int* y;

Then both x and y are compatible with the type int*. The main difference is that x points to a range of addresses on the stack, with enough capacity to store 8 integers, while y is not initialized.

Also, *x and x[0] are two identical ways of dereferencing.

*x == x[0]
*(x+n) == x[n]
Adi Levin
  • 5,165
  • 1
  • 17
  • 26
  • I'm glad you included that last bit of code! Dereferencing the information using array notation (X[0]) also came to me after remembering that the array name itself is just holding an address. I used cout << X; to get the address, then I realized that all along, that by using the array notation (X[0]), I had been dereferencing, but didn't know it. Thanks for clarifying that. – JosephTLyons Feb 20 '16 at 08:15
  • 3
    Array is not _"just a ponter"_ . Arrays decay to pointers easily, but they have own distinct type which sometimes is preserved and important (ranged-for with array type variable, multidimensional arrays) – Revolver_Ocelot Feb 20 '16 at 08:38
  • To my knowledge, this applies to C++ as well: http://stackoverflow.com/questions/1641957/is-an-array-name-a-pointer-in-c – user4581301 Feb 20 '16 at 08:39
  • That edit is equally incorrect. An array has data storage. A pointer has no more storage than the space for an address of something else. An array can be treated like a pointer, but it is not. – user4581301 Feb 20 '16 at 08:41
  • I accept your comments. Made another edit to the post. I'm trying to keep it simple, and not stray too much from the purpose of the question. – Adi Levin Feb 20 '16 at 08:44
  • *"An array variable can be treated as a pointer"* is still incorrect when you consider references. For example, if a function takes `int[3]` by reference, then you cannot pass `int*` to it. For example, `void f(int(&)[3]) {} int main() { int* p; f(p); }` does not compile. Likewise, `void f(int*&) {} int main() { int arr[1] = { 123 }; f(arr); }` does not compile, either. – Christian Hackl Feb 20 '16 at 09:13