how do arrays work internally in c/c++ - c++

I was wondering how do arrays work in c. I end up with an hypothesis and I'd like to know if I am right or not.
We know arrays are a sequence of adjacent memory cases(boxes), where each box has the size of the type it stocks (i.e if INTs one box has a size = sizeof(int) and an array of 3 INTs takes in memory adjacent places of 3 sizeof(int) )
Now we also know that we can dynamically allocate memory for an array of a certain type (malloc in C, new in C++).
what makes me wonder is the fact that an array has for origin the the address of the first box of the array and the first value (the value in the later box) when calling it with the bracket [0] is array[0] == *(array+0) == *array (whether array was declared "type * array" or "type array[]" or "type array[size]") and "array" called that way whether define as a pointer or an array ("type * array" or "type array[]" or "type array[size]") is the address of the first box.
I end up thinking and I'd like a confirmation on this: arrays when even declared with the square brackets ([]) are actually in memory a sequence of n pointers each containing (having as a value not as an address) the address of a memory box Bi containing the actual value + those memory boxes (B0,...,Bn each containing the actual values). such that in the and when one declares "int array[5]" the program actually allocate 5 adjacent boxes of int pointers P0,P1,..,P4 and 5 int sized memory places scattered all over the computer memory B0,B1,...,B4 where the value of Pi is the address of Bi
Am I right or wrong!!?? Thank you!

arrays when even declared with the square brackets ([]) are actually in memory a sequence of n pointers each containing [...] the address of a memory box Bi containing the actual value + those memory boxes
Nope.
It sounds like you're puzzled how array[0] == *(array+0) == *array could be true both for an array declared as int array[10]; and int *array = ...;. A perfectly reasonable question; We're told that for a pointer ptr the expression *ptr gets the value the pointer is pointing at, so when we use the same syntax with an array where are the addresses that we're dereferencing?
Here's the secret: The array index operator ([]) does not work on arrays in C and C++. When you apply it to an array the language implicitly converts the array into a pointer to the array's first element. Thus adding to an array or dereferencing an array appears to behave the same as adding or dereferencing a pointer.
int array[10];
// These lines do exactly the same thing:
int *ptr1 = &array[0]; // explicitly get address of first element
int *ptr2 = array; // implicitly get address of first element
So arrays really are a contiguous set of elements in memory where each element really is the value, not a pointer to another location containing the value. It's just that the way arrays are defined means that they often convert to a pointer implicitly and so it seems like there are pointers when really there's just an implicit conversion.

Arrays are stored contiguously in the virtual memory. However, the physical memory addresses that they map to may or may not be contiguous.
And the array elements do not store a pointer to point to the next element. Only the value is stored.

Think of it as this:
array[n] is simply a syntactic sugar for *(array + n).
And no, there are no pointers, the array actually contains the values in a continuos memory range.

The array does not consist of any pointers. The elements of an array are stored on the heap whereas the reference to those elements is stored on the stack. If you declare an array called values of type int and it consists of 5 elements.
The variable values is a pointer to the first value values[0] stored on the heap, it is also referred to as the base address which is the address of the first element of the array. You might wonder that how would the code find the other elements of the array. values[1] can be dereferenced by *(values+1) or at a low level it is like this *(&values + n*sizeof(values[0])) where n is the index of the element you want to dereference.
So you get the other elements of an array by just adding the size of the element to the memory. This is because the elements of an array are stored side-by-side in memory or technically stated, the blocks of memory share the same border.
An array does not have any pointers, an array like data-structure that contains pointers is called a linked list.
You can learn about the internal working of arrays here

Related

Clarification on the relation of arrays and pointers

I wanted some further understanding, and possibly clarification, on a few things that confuse me about arrays and pointers in c++. One of the main things that confuse me is how when you refer to the name of an array, it could be referring to the array, or as a pointer to the first element, as well as a few other things. in order to better display where my trouble understanding is coming from, I'll display a few lines of code, as well as the conclusion that I've made from each one.
let's say that I have
int vals[] = {1,50,3,28,32,500};
int* valptr = vals;
Because vals is a pointer to the first value of the array, then that means that valptr, should equal vals as a whole, since I'm just setting a pointer equal to a pointer, like 1=1.
cout<<vals<<endl;
cout<<&vals<<endl;
cout<<*(&vals)<<endl;
cout<<valptr<<endl;
the code above all prints out the same value, leading me to conclude two things, one that valptr and vals are equal, and can be treated in the same way, as well as the fact that, for some reason, adding & and * to vals doesn't seem to refer to anything different, meaning that using them in this way, is useless, since they all refer to the same value.
cout<<valptr[1]<<endl;//outputs 50
cout<<vals[1]<<endl;//outputs 50
cout<<*vals<<endl;//outputs 1
cout<<*valptr<<endl;//outputs 1
the code above furthers the mindset to me that valptr and vals are the same, seeing as whenever I do something to vals, and do the same thing to valptr, they both yield the same result
cout<<*(&valptr +1)-valptr<<endl;
cout<<endl;
cout<< *(&vals + 1) -vals<<endl;
Now that we have established what I know, or the misconceptions that I may have, now we move on to the two main problems that I have, which we'll go over now.
The first confusion I have is with cout<< *(&vals + 1) -vals<<endl; I know that this outputs the size of the array, and the general concept on how it works, but I'm confused on several parts.
As shown earlier, if
cout<<vals<<endl;
cout<<&vals<<endl;
cout<<*(&vals)<<endl;
all print out the same value, the why do I need the * and & in cout<< *(&vals + 1) -vals<<endl; I know that if I do vals+1 it just refers to the address of the next element on the array, meaning that *(vals+1)returns 50.
This brings me to my first question: Why does the & in *(&vals+1) refer to the next address out the array? why is it not the same output as (vals+1)in the way that *(&vals)and (vals)have the same output?
Now to my second question. We know thatcout<< *(&vals + 1) -vals<<endl; is a valid statement, successfully printing the size of the array. However, as I stated earlier, in every other instance, valptr could be substituted for vals interchangeably. However, in the instance of writingcout<<*(&valptr +1)-valptr<<endl; I get 0 returned, instead of the expected value of 6. How can something that was proven to be interchangeable before, no longer be interchangeable in this instance?
I appreciate any help that anyone reading this can give me
Arrays and pointers are two different types in C++ that have enough similarities between them to create confusion if they are not understood properly. The fact that pointers are one of the most difficult concepts for beginners to grasp doesn't help either.
So I fell a quick crash course is needed.
Crash course
Arrays, easy
int a[3] = {2, 3, 4};
This creates an array named a that contains 3 elements.
Arrays have defined the array subscript operator:
a[i]
evaluates to the i'th element of the array.
Pointers, easy
int val = 24;
int* p = &val;
p is a pointer pointing to the address of the object val.
Pointers have the indirection (dereference) operator defined:
*p
evaluates to the value of the object pointed by p.
Pointers, acting like arrays
Pointers give you the address of an object in memory. It can be a "standalone object" like in the example above, or it can be an object that is part of an array. Neither the pointer type nor the pointer value can tell you which one it is. Just the programmer. That's why
Pointers also have the array subscript operator defined:
p[i]
evaluates to the ith element to the "right" of the object pointed by p. This assumes that the object pointer by p is part of an array (except in p[0] where it doesn't need to be part of an array).
Note that p[0] is equivalent to (the exact same as) *p.
Arrays, acting like pointers
In most contexts array names decay to a pointer to the first element in the array. That is why many beginners think that arrays and pointers are the same thing. In fact they are not. They are different types.
Back to your questions
Because vals is a pointer to the first value of the array
No, it's not.
... then that means that valptr, should equal vals as a whole, since I'm just setting a pointer equal to a pointer, like 1=1.
Because the premise is false the rest of the sentence is also false.
valptr and vals are equal
No they are not.
can be treated in the same way
Most of the times yes, because of the array to pointer decay. However that is not always the case. E.g. as an expression for sizeof and operand of & (address of) operator.
Why does the & in *(&vals+1) refer to the next address out the array?
&vals this is one of the few situation when vals doesn't decay to a pointer. &vals is the address of the array and is of type int (*)[6] (pointer to array of 6 ints). That's why &vals + 1 is the address of an hypothetical another array just to the right of the array vals.
How can something that was proven to be interchangeable before, no longer be interchangeable in this instance?
Simply that's the language. In most cases the name of the array decays to a pointer to the first element of the array. Except in a few situations that I've mentioned.
More crash course
A pointer to the array is not the same thing as a pointer to the first element of the array. Taking the address of the array is one of the few instances where the array name doesn't decay to a pointer to its first element.
So &a is a pointer to the array, not a pointer to the first element of the array. Both the array and the first element of the array start at the same address so the values of the two (&a and &a[0]) are the same, but their types are different and that matters when you apply the dereference or array subscript operator to them:
Expression
Expression type
Dereference / array subscript expresison
Dereference / array subscript type
a
int[3]
*a / a[i]
int
&a
int (*) [3] (pointer to array)
*&a / &a[i]
int[3]
&a[0]
int *
*&a[0] / (&a[0])[i]
int

Array and pointers in c++ [duplicate]

This question already has answers here:
C/C++ int[] vs int* (pointers vs. array notation). What is the difference?
(5 answers)
Why am I being told that an array is a pointer? What is the relationship between arrays and pointers in C++?
(6 answers)
Closed 6 years ago.
I often hear that the name of an array is constant pointer to a block of memory therefore statement like
int a[10];
and
int * const p= a;
must be equal in a sense that p is pointer that points to the same block of memory as array a[] and also it may not be changed to point to another location in memory.
However if you try to print sizeof these two pointers you get different results:
cout<< sizeof(a); // outputs size of 10 integer elements
While
cout<< sizeof(p); // outputs sizeof pointer to int
So, why do compilers treat these two differently? What's the true relation between arrays and pointers from compiler point of view?
I often hear that the name of an array is constant pointer to a block of memory
You've often been mislead - or you've simply misunderstood. An array is not a constant pointer to a block of memory. Array is an object that contains a sequence of sub-objects. All objects are a block of memory. A pointer is an object that contains an address of an object i.e. it points to the object.
So in the following quote, a is an array, p points to the first sub-object within a.
int a[10];
and
int * const p= a;
must be equal in a sense that p is pointer that points to the same block of memory as array a[] and also it may not be changed to point to another location in memory.
If that is your definition of equal, then that holds for non-array objects as well:
char c;
int * const p = &c;
Here p "points to the same memory as c" and may not be changed to point to another location in memory. Does that mean that char objects are "equal" to pointers? No. And arrays aren't either.
But isn't a (the name of the array), a constant pointer that points to the same element of the array?
No, the name of the array isn't a constant pointer. Just like name of the char isn't a constant pointer.
the name of an array holds the address of the first element in the array, right?
Let's be more general, this is not specific to arrays. The name of a variable "holds the address" of the object that the variable names. The address is not "held" in the memory at run time. It's "held" by the compiler at compile time. When you operate on a variable, the compiler makes sure that operations are done to the object at the correct address.
The address of the array is always the same address as where the first element (sub-object) of the array is. Therefore, the name indeed does - at least conceptually - hold the same address.
And if i use *(a+1), this is the same as a[1], right? [typo fixed]
Right. I'll elaborate: One is just another way of writing another in the case of pointers. Oh, but a isn't a pointer! Here is the catch: The array operand is implicitly converted to a pointer to first element. This implicit conversion is called decaying. This is special feature of array types - and it is the special feature which probably makes understanding the difference between pointers and arrays difficult the most.
So, even though the name of the array isn't a pointer, it can decay into a pointer. The name doesn't always decay into a pointer, just in certain contexts. It decays when you use operator[], and it decays when you use operator+. It decays when you pass the array to a function that accepts a pointer to the type of the sub-object. It doesn't decay when you use sizeof and it doesn't decay when you pass it to a function that accepts an array by reference.

Why say that a 2-dimensional array is a "pointer to a pointer?"

Suppose we have that the following 2-dimensional array:
int multi[3][3];
As I understand it, we can then say that multi is a "pointer to a pointer", or -- at the very least -- we can do something like the followiong:
int **p_p_int = multi;
Question: Why don't we say that multi is a "pointer to a pointer to a pointer to pointer", since multi points towards the address of its first element, which itself points to a pointer, which points towards the address of multi[0][0], which itself points to the value of multi[0][0].
This seems to be 4 objects that point (here I'm counting addresses as pointers).
Now you might say "but addresses aren't pointers, even though pointers can equal addresses", in which case it also seems weird to say that a 2-dimensional array is a "pointer to a pointer", since it's actually a pointer to an address to a pointer to an address (to a value).
I feel like I have mananaged to confuse myself quite a bit here :)
A 1d array converts to a pointer rather than being one — e.g. you can reassign a pointer, you cannot reassign an array.
A literal 2d array isn't stored in the way described below.
However a 2d array can be achieved by a pointer to a pointer if:
int **array2d is a pointer. It points to an array. That array contains pointers.
Because array2d points to an array of pointers, array2d[n] is also a pointer. But it's a pointer to an array of integers.
So that's two pointers, total.
In pseudo code, the steps to look up the item at (m, n) are:
add m to array2d to index that array. Read pointer, p from calculated address;
add n to p to index that array. Read integer from calculated address.
The job of a pointer is to point towards something. Just because something is both a pointer and points to something else doesn't make it a pointer to a pointer, unless that something else is a pointer.
So let's break this down:
multi points towards the address of its first element,
That makes it a pointer
which itself points to a pointer,
That makes it a pointer to a pointer.
which points towards the address of multi[0][0],
No. It contains the address of multi[0][0], it doesn't point to it.
which itself points to the value of multi[0][0]
Well, of course. It's a pointer, pointers point to values, that's their job. That doesn't make them pointers to pointers, it makes them pointers to values.
This seems to be 4 objects that point (here I'm counting addresses as pointers).
Sure, but two of those pointings are the very same pointing just counted twice. You say X is a pointer that contains a value that points to something as if that was two separate pointings. The job of a pointer is to have a value that points to something, that's what makes it a pointer in the first place. It's two ways of saying the same thing, "X is a pointer" = "X contains a value (of a type) that points to something".
Saying that multi is a pointer to a pointer is just wrong. It is a 2D array.
multi can not be converted to a ** - only to *
A pointer to pointer would obviously point to a pointer. multi is not doing that. You'll find an integer at that location - not a pointer - simply because multi is not an array of pointers. multi[n] may also be converted to a * but the converted value is not taken from a place where it was stored - it is just calculated from multi.
Don't think of a 2D array like:
int a[3];
int b[3];
int c[3];
int* x[3] = {a, b, c};
cause that is simply not how 2D arrays work.
All pointer values you get from a 2D array are calculated values - not stored values.
I think that there are two unrelated stuff here that you are mixing up:
the name of any array of the type T[][][]...n...[] decayes into T*[][][]...n-1...[] for example:
int arr [7] decayes into int* arr
std::fstream arr[2][2] decays into std::fstream* arr[2]
by decaying, we mean that a function which can get T can also accept T' which T' is the decayed type of T
you can dynamically create psuedo 2 dimentional arrays by using pointer to pointer and dynamic allcoation
int** psuedeo2DimArray = new int*[10];
for (auto i=0U;i<10;i++){
psuedeo2DimArray[i] = new int[10];
}
In all cases, the type of multi is int[2][2] and nothing else.

When I create a int pointer and instantiate an array, what is happening in memory with the array?

Lets say I do something like this
int *array;
array = new int[10];
How is memory set up for this array?
What type is array[0]? (a pointer? an int?)
This code first allocates room for a pointer called array. This memory is allocated on the stack.
Next, it allocates a block of memory from the heap to hold 10 integers and assigns the address to array.
array[0] will refer to the first integer in the block of 10. The subscript makes it a value instead of a pointer.
And for God's sakes, start accepting some answers that people are giving you!
In this case, array[0] has type int.
The memory is pretty simply a pointer pointing to memory to hold 10 ints that's allocated somewhere on the free store (which translates mostly to: "we honestly don't care about its address, we just care that it's our memory and we can use it 'til we delete it").
Note that in this case, you're depending on an equivalence that was originally defined in C: that x[y] is equivalent to *(x+y). In this case, your x is a pointer, and addition to the pointer happens in increments of the size of the type that it points to, so when we use array[N], we get the Nth item in the memory pointed to by array.
One minor detail: you shouldn't do this -- probably ever. There's almost never a good reason to use new type[size] in C++. At one time we did because better alternatives (e.g., std::vector) weren't available yet -- but nowadays it's a lousy idea.
array[0], as an element of the array, is an int.
array has already been declared a pointer to an int.
In memory, the 'new' call allocates a single block of space to hold ten integers. 'array' is a pointer to the start of that block. array[0] is the first element. array[1] is the second, and so on.
How is memory set up for this array?
new int[10] will dynamically allocate enough memory from the free store to contain ten values of type int. It gives you a pointer to that memory, which you store in your array variable.
array itself is an automatic variable of pointer type; it's typically stored in a region of the stack that was allocated automatically for the current function call, and will be deallocated when the function returns.
Once you've finished with the memory, you must return it to the free store using delete [] array. Since it's easy to get that wrong, it's usually better to use RAII types such as smart pointers or containers to manage dynamic resources. In this case, std::vector<int> array(10) will give you an array of the same size, and will automatically delete it for you when it goes out of scope.
What type is array[0]?
array[0] is the first element of an array of int, so its type is int.

a couple of simple questions about basic pointer use in c++ and the c++ memory model

I've been studying along with the Stanford courses on iTunes U and have hit pointers in C++. I think I understand how pointers work, but I just want to check how to do some simple stuff. Let's say I want to create a dynamic array:
double *array;
At this point there's a variable called "array" in the stack and nothing in the heap. First question - what's stored in "array" at this point? A pointer to some nonsense piece of memory?
I then allocate memory using "new":
array = new double[10];
Second question - at this point, what's stored in "array"? A pointer to some contiguous piece of memory big enough to hold ten doubles? (Sorry for the simple questions, but I really want to make sure I understand)
I assign the double 2.0 to each element in the array:
for(int i=0; i<array.length(); i++) array[i]=2.0;
Third question - is this different from using the dereference operator to assign? (i.e., *array[i]=2.0). I then pass the array to some other function:
myFunc(double array[]){
for(int i=1; i<array.length(); i++){
array[i]=array[i]*array[i-1];
}
}
Fourth question - on the pass to myFunc, since array is an array of pointers to doubles, and not an array of doubles, it passes by reference without "&", right? That means the operations in my loop are affecting the actual data stored in "array". What if I wanted to pass by value, so that I wouldn't be touching the data in "array"? Would I use
myFunc(double *array[]){...}?
Last question - what if I wanted to manipulate the memory addresses for the contents of "array" for some reason? Could i use
someVar = &array[5];
to assign the the hex address of array[5] to someVar?
I've read the section on pointers in the reader and watched the Binky video a dozen times and it still doesn't make sense. Any help would be greatly appreciated.
EDIT: Thanks a lot to everyone who answered so far. If you wouldn't mind I just have one more question. In the declaration double *array;, "array" is declared as a pointer to a double, but once I use "new" to assign it, "array" ceases being a pointer to a double, and becomes an array of doubles, right?
array contains junk data - whatever was in that memory location before array existed is still there. If you try to play with it you're going to shoot yourself in the foot, which is why you need to assign it to a valid memory location, (hence the ensuing call to new[]).
Yes, array now contains a pointer (memory address) to some contiguous piece of memory big enough to hold ten doubles.
*array[i]=2.0 won't actually compile. array[i] results in a double, and you can't use the dereference operator on a double.
What you're passing is that address to the first element in the array. So you are passing the pointer by value, and the array by reference (as the pointer is a reference to the array.) To pass the array itself by value you'd have to have one parameter for each entry. You could also copy the array and send in the copy, but the copy itself would be passed by reference, too.
double* someVar = &array[5]; will return to you a pointer to the 6th element of the array. array[5] gives you the double, and taking the address of it (with &) will give you the memory address (pointer) of that double.
Yep, that's what's happening
Most definitely. More specifically, a pointer to the beginning of a contiguous piece of memory.
Not in this case; * (for dereference) is a unary operator, and yet you have passed it two arguments. You can be sure it is multiplication that is performed (or an overloaded version of it) - also, what could array[i](*array[i-1]) mean? you can't dereference something that isn't a pointer (or doesn't have the unary * operator overloaded)
You're only passing the pointer by value and not the data. If you want to pass the data by value (make it unchanged outside the function), you'd have to copy it first, and pass that (or just use a vector)
Yes, you're just getting the address of a part of contiguous memory, and you can store the address and modify the dereferenced value elsewhere, the array will be modified also.
Also, be weary that when you allocate on the heap, you have to delete the memory afterwards. In this case, you would use delete[] array;
After declaration, the array variable contains an arbitary value. You're not allowed to do anything with that value. After new, it contains a pointer to a contiguous range of memory large enough to hold 10 doubles. *array[i]=2.0 is an error (that would imply that array is an array of pointers to double). Indexing operator [] is just a syntactic sugar for *(array+i)=2.0.
Forth question: SAY WHAT?? You don't have an array of pointers to doubles anywhere in that code. In functions, void f(double *x) and void f(double x[]) are THE SAME THING: a pointer to double. If you pass to f an array, x will receive the address of the first element (which is the VALUE of an array).
You can't pass arrays by value. Alternatively, they are always passed by value (as everything else in C), but note that the VALUE of an array is the address of its first element.
Your last question: I have no idea what you're trying to achieve, but the question clearly shows that you're confused. An address is an address, there's no such thing as "hex address".