Pointers

Hello! I am new at computer programming and I am currently tying to understand pointers and multi-dimensional arrays. I have spent about 5 hours everyday for the last three days trying to understand it. I've watched youtube vieos and read several articles and two books. However, I just don't get understand them. I know what pointers are and I know what arrays are. But when I put them together, my understanding of them goes down the drain. Any suggestions?

Last edited on
That is normal ..
This concept needs too many practices to understand...

Can you write specific questions about your problem ?

Also try to read this :
http://www.cplusplus.com/doc/tutorial/pointers/
I know what pointers are

Could you explain them to us? That way we'll learn what you know. (Besides, when attempting to explain something one has good chance to learn self too.)
Pointers are values that hold the address of some variable. Pointers are useful when used in functions when you actually want to modify the memory itself to a different value rather than the function just using a copy.

As I was writing this explanation, a question came to mind.

Why can we write "int a[][4]" but not "int a[4][]"?

Also, what is the difference between

int p_p_test;
p_p_test = new int*[3];

rather than

int p_p_test;
p_p_test = int*[3];

What is special about the new?
One more question: Sorry I am asking so many questions...

1
2
3
4
5
6
	int **p_p_test;
	p_p_test = new int*[3]; 

	cout << &p_p_test[0] << endl;
	cout << &p_p_test[1] << endl;
	cout << &p_p_test[2] << endl;


When I run this code, the output is 0x600010480, 0x600010488, and 0x600010490. What doesn't make sense is that an integer is 8 bites and so it should go from 0x600010488 to 0x600010496 not 0x600010490. Why is it doing this?

When I write in int*[3], this implies that I am allocating three memories each with 8 bites correct?

New allocates memory in a location on the heap that is chosen by the OS, and how you used it causes a memory leak as you are not storing any reference to where it is (you're also trying to assign an int a pointer, it's usually the other way round. If you are going to use new, allocate a pointer using new, or even better use a smart pointer.
int *test; test = new int (0);
or better
std::unique_ptr test (new int (0));
Addresses are coded in Hexadecimal... not decimal ..
It is different than numbers that are used every day..

http://www.cplusplus.com/doc/hex/

You should read it ..
Addresses are coded in Hexadecimal... not decimal ..

Addresses are numbers. Addresses (or any number for that matter) can be represented in hex, decimal, octal, binary or whatever base you like. There is no requirement that addresses be represented as hex, although hex representation is common on 32 bit and larger machines. Octal was common on some 16 bit machines.
Last edited on
Yes I know ..

That's why I have said (coded)..

Most time we see it in Hex...

And in the OP last question .. It seems that he don't know about hex representation and assuming that the Addresses in his code
are represented in decimal ..

Arrays
An array is a contiguous sequence of same-type values in memory. For example,

int five_primes = { 2, 3, 5, 7, 123457 };
appears in (32-bit, little-endian) memory as:

[ 02 00 00 00 ][ 03 00 00 00 ][ 05 00 00 00 ][ 07 00 00 00 ][ 41 E1 01 00 ]


An array has three pieces of information associated with it:
- the address of the first element
- the number of elements in the array
- the type of the elements in the array (which includes the size in bytes of each element in the array)

An array will degrade to a pointer at the earliest opportunity. That's why you can use an array's name like a pointer.

1
2
3
4
5
6
7
#include <cstring>

int main()
{
  char s[] = "Hello world!";      // s is an array
  int length = std::strlen( s );  // but strlen() takes a pointer as argument
  ...

You can even make a direct assignment:

1
2
  char s[] = "Hola mundo";
  char* p = s;  // s automatically converts to a pointer, so this assignment works okay!!! 


Pointers
In C and C++, pointer arithmetic is important, and there are a lot of ways to write it. Specifically, the [] operator takes a pointer and an integer as arguments. It then calculates a new pointer. The following are equivalent ways to access the fourth element of an array:

         a[3]                   *(a+3)                   3[a]

(That last one there is an oddity with C and C++. Only weirdos doing weird stuff ever use it. So you can safely ignore it.)

At this point you should be able to see that you can access any element of an array if all you have is a pointer to the first element in the array.

This helps us answer your last question:


Arguments
Function arguments may be pointers, and a pointer is always to a specific type of thing. In the case of what looks to be an array:

int foo( int a[20] )

it turns out that C and C++ don't actually care how many elements are in the array. You could have just written:

int foo( int a[] )

The reason is because that is not an array you are passing as argument to the function, but a pointer. That's right, you could have just written:

int foo( int* a )

The argument a is a pointer to an integer. And, using regular pointer arithmetic, you can access any element of the array. This is why, BTW, it is so important to pass the length of your array to a function that takes one -- because the function has no idea how many elements are in your array. The actual number of elements in the argument array could be different each time:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

void myfn( int* a, int n )
{
  for (int i = 0; i < n; i++)
    std::cout << i << ": " << a[i] << "\n";
}

int main()
{
  int xs[] = { 1, 2, 3 };
  int ys[] = { 9, 8, 7, 6, 5 };

  myfn( xs, 3 );
  myfn( ys, 5 );
}


Multidimensional
An array with multiple dimensions is really an array of arrays. For example:

int two_dims[3][4];

declares an array of three elements. Each element is itself an array of four elements. Let's initialize the array to get a better view:

1
2
3
4
5
6
int two_dims[3][4] =
{
  { 11, 12, 13, 14 },
  { 21, 22, 23, 24 },
  { 31, 32, 33, 34 }
};

Three elements. Each element is an array of four integers. We can rewrite it this way as well:

1
2
3
typedef int element[4];  // an element is an array of four integers

element two_dims[3];  // two_dims is an array of three elements 


Now, when you wish to pass such a thing as an argument, remember that you get a pointer to the first element of the array. The type of the entire first element is important, but not the number of elements in the array.

Hence, you could write your function as:

void foo( element* two_dims )
or
void foo( element two_dims[] )
or
void foo( element two_dims[3] )
or even
void foo( element two_dims[3000] )
...
It all winds up meaning the same thing. And if you don't have a typedef handy for the type of the elements in the array, you must write it out as well:

void foo( int two_dims[][4] )

See how the type (int[4]) is added to the argument (two_dims[])? Does that make sense?


Static vs Dynamic Memory
When you declare an array (just like in the examples above), that is over memory that is part of your program. It is accessible in the same place before and during the entire time your program runs. This is important, because the compiler knows exactly where that memory will be when it builds your program.

But there is nothing saying that all the memory your program accesses must be like that.

The new and delete operators (they're really like functions) ask for additional memory to be given to or taken from your program. Neither you nor the compiler know where that memory is going to be, so you need a pointer to keep track of it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  int* a;

  a = new int[20];  // get me the address of the first integer in an array of 20 integers.

  a[3] = 12;  // do stuff with the array

  delete [] a;  // forget all about the array


  a = new int;  // now just get me the address of a single integer

  *a = 92;  // do stuff with that integer
  a[0] += 1;

  delete a;  // forget all about that integer 

Hope this helps.

Edit: fixed typo
Last edited on
Thank you guys all for answering my questions! I really appreciate it!

Shadowmouse, I'm not sure what you mean by memory leak and smart pointer...

Duoas, what does typedef do?

I'm not sure how you went from

1
2
3
typedef int element[4];  // an element is an array of four integers

element two_dims[3];


to using element and two_dims in a function. What does it mean when you are putting them together, element two_dims, like that in the function.

And for void foo( int two_dims[][4] ), why can you just write [][4]? Why don't you have to put a number in the first []?
Duoas wrote:
An array has three pieces of information associated with it:
- the address of the first element
- the number of elements in the array
- the type of the elements in the array (which includes the size in bytes of each element in the array)

Likewise, a pointer has two pieces of information:
- the address of an element
- the type of the element (which includes the size in bytes of the element)

The value that a pointer holds, the address, can be either valid (the address contains element of expected type) or invalid. The nullptr is a safe invalid address, because by convention it is known to be invalid. There is no way to know whether some other address is valid or invalid. The code must ensure that.

There is no way to know, whether a memory address contiguous to address that does have a valid element does contain a valid element of the same type. In other words a pointer may point to either a single element, or to a single element that is a member of an array.

And for void foo( int two_dims[][4] ), why can you just write [][4]? Why don't you have to put a number in the first []?


Duoas did state that these are indistinguishable:
1
2
void foo( int matrix[][4] );
void bar( int * matrix[4] );

In both cases the matrix is a pointer that refers to element that is a four integer array.

If you do dereference the matrix like this: matrix[k], you will get a pointer (X) that contains an address:
matrix + k * 4 * sizeof(int). The 4 * sizeof(int) is the size of one element in bytes and the k determines which element in the (presumed) array.

The pointer X has type too: pointer to integer. Dereferencing the X gives us access to an integer. Obviously, you will not write:
1
2
auto X = matrix[k];
int x = X[n];

but rather: int x = matrix[k][n];

Why can we write "int a[][4]" but not "int a[4][]"?

The int a[][7] (as function parameter) decays to int * a [7] and we still know the size of one element: 7*sizeof(int).
The int a[3][] (as function parameter) would decay to int * * a but we would have no idea, how many bytes to add to the address, if one writes a[1]. That is an error.
Thank you keskiverto and duoas for taking the time to answer my question. I sincerely appreciate it so very much. It clarifies and confuses things all at the same time. But regardless, thank you so so so much!!!
It'll all make perfect sense eventually. It just takes a little time to wrap your brain around new concepts.
Topic archived. No new replies allowed.