Array of pointers - arrays

I am trying to construct an m-way tree and I am having trouble visualizing an array of pointers pointing to different instances of the B_tree node class (this basically creates the array type nodes and includes all functions associated with the tree such as count, insert etc)
Are there any tips/tricks to visualizing an array of pointers for this case? Are there any good links/resources for explanation of array of pointers? (I did not find the common search results on google that helpful)...

Here is a picture of an array of pointers, they aren't pointing to anything, but this is a visualization of an array of pointers. Here is a link explaining arrays of pointers http://ee.hawaii.edu/~tep/EE160/Book/chap9/section2.1.4.html. Enjoy.

Array with pointers is just like a usual array with a maximum number of size. Each position of the array it does not hold an integer or float or char or a struct. It holds a pointer.
What a pointer is ?
Imagine the computer's memory like a huge array which hold different kind of values. The variable which holds the pointer what it actually holds is the address of a block of memory. It does not hold the value of an integer but if you have int *a; , it means that the variable a which is store in a memory address hold the address of something which is integer.
The pointer always holds 4 bytes. Therefore an array with pointers means that each positions shows the memory address of something. If you have an integer array with pointers of size 10, means that each position shows the address of a memory block( this block stores an integer). So the array holds 10 pointers and each one of them shows to an integer.

Related

Is there a way to use zero-length arrays with 2d arrays?

I just discovered zero-length arrays and I'd like to use it for a 2d array, the advantage of these arrays is to avoid pointers inside a structure so we can just free the structure instead of having to free it's data, so it can better be used inside a container structure like a linked list for example without having to pass a destructor function, but the problem is I can't figure out how to use it for 2d arrays. I have a structure looking like this:
struct s_arg
{
int argc;
char argv[0][0];
};
But the problem is: how to keep track of each member size without another array containing sizes? Is this possible to do this with no malloc for struct members ?
No.
The only reason an array can be used with no size known is because you do not need to know the size in order to access elements. Given an array a, a[0] is at the start of the array, a[1] is one element beyond that, and so on. The location of any element a[i] can be computed without knowing the size of the array.
Naturally, for an array to exist, somebody has to allocate memory for it, and so they must know the size. So the creator of an array must know its size, but the user does not need to.
GCC allows zero-length arrays as an extension so that a structure can have an array at its end, where the memory is allocated by the creator of the structure, who knows its size. Except to support old software, this extension became unnecessary once the C standard supported arrays of unknown size (declared with no size, [], called flexible array members).
For two-dimensional arrays, the ability to use an array without knowing its size does not apply. Given a two-dimensional array of char named a, a[i][j] is located j elements after the start of a[i]. Each of those elements is a char, so calculating j char beyond a[i] is easy. And a[i] starts i elements after the start of a. But the elements of a are arrays of char. To know how big i elements is, you must know the size of the element; you must know the size of the array of char.
So a two-dimensional array cannot be used unless the size of the second dimension is known.
There are ways to use a two-dimensional array whose second-dimension size is known at run-time, including:
Use a one-dimensional array and calculating indices into it manually, as with a[i*size + j].
Use a one-dimensional array and convert its address. For example, from some structure s with member m, (char (*)[size]) s.m, which can then be used as ((char (*)[size]) s.m)[i][j]. (See other Stack Overflow questions and answers for language-lawyer issues about treating one-dimensional arrays as two-dimensional arrays.)
Also, your member name argv suggests you might want this structure to store command-line arguments passed as a parameter of main. If so, you should be mindful that the argv second parameter of main is a pointer to pointers, not a pointer to an array. The data in those strings is generally not arranged in memory for use as a two-dimensional array of char. You could copy the strings into a two-dimensional array, but that would generally be wasteful.

Changing the array's base-address

Why can't I modify the base address of an array? Is it because the allocated memory would be lost? in that case, I can make an array using a pointer and change what the pointer points to and the allocated memory would be lost too, then what is the difference?
Arrays are objects all on their own, and not pointers. Consider a simpler object:
int a = 0;
Would you expect to be able to change its address? Of course not. An object is a region of storage with a type. The region of storage is identified by its address, so you won't expect to change it. And arrays are objects too. When you declare
int b[8] = {0};
you declare an object, the size of eight integers, that will occupy some storage. You can't change its address any more than you can change the address of any single int.
You have probably been told that arrays are pointers. But they are not! They may be converted, even implicitly, to a pointer more often than not, but they are still object types. Pointers often stand in for arrays because the address of the first element is enough to reach any other element with pointer arithmetic, but the pointer is not the array object itself. The difference becomes apparent when you inspect their object properties. For instance:
sizeof(b) != sizeof(int*)
The object b is not the size of a pointer, indeed it is the size of 8 integers, likely larger than a pointer.

Confusion between "int array[int]" and "int *array"

int array[100];
int *array;
I am confused about the differences between int array[100] and int *array.
Essentially, when I do int array[100] (100 it's just an example of an int), I just reserved space in memory for 100 ints, but I can do int * array and I didn't specify any type of size for this array, but I can still do array[9999] = 30 and that will still make sense.
So what's the difference between these two?
A pointer is a pointer, it points somewhere else (like the first element of an array). The compiler doesn't have any information about where it might point or the size of the data it might point to.
An array is, well, an array of a number of consecutive elements of the same type. The compiler knows its size, since it's always specified (although sometimes the size is only implicitly specified).
An array can be initialized, but not assigned to. Arrays also often decay to pointers to their first element.
Array decay example:
int array[10];
int *pointer = array; // Here the symbol array decays to the expression &array[0]
// Now the variable pointer is pointing to the first element of array
Arrays can't naturally be passed to function. When you declare a function argument like int arr[], the compiler will be translating it as int *arr.
All of this information, and more, should be in any good book, tutorial or class.
A non-technical explanation:
A pointer's contents refer to an address (which may or may not be valid). An array has an address (which must be valid for the array to exist).
You can think of a pointer as being like an envelope - you can put any address you want on it, but if you want it sent to somewhere in particular, that address has to be correct.
An array is like your house - it exists somewhere, so it has an address. Things properly addressed get sent there.
In short:
A pointer holds an address.
An array has an address.
So
int *array;
creates a pointer of indeterminate value (it can point anywhere!).
When you then have
array[9999] = 30;
you're trying to set the 9999th int value from where array points to the value of 30. But you don't know where array points because you didn't give it an actual value.
And that's undefined behavior.
The difference is when you do int array[100], a memory block of 100 * sizeof(int) is allocated on the stack, but when you do int *array, you need to dynamically allocate memory (with malloc function for example) to use the array variable. Dynamically allocated memory is on the heap, not stack.
int array[100] means a variable array which will be able to hold 100 int values this memory will be allocated from the stack. The variablearray will be having the base address of the array and memory will be allocated for the same.
But in the case of int *array since you are declaring this as a local variable, pointer variable array will be having a garbage address. So if you do array[9999] it could cause a segmentation violation since you are trying to access garbage memory location outside your program.
Some points that you can find useful to know:
Via int arr[N] you specify an array of type int which can store N
integers. To get information about how much memory array is taking you can use sizeof operator. Just multiply the number of items in an array by the size of type: N*sizeof(int).
Name of the array points to the first element in an array, e.g. *arr is the same as arr[0], also you may wonder why a[5] == 5[a].
An uninitialized array of non-static storage duration is filled with indeterminate values.
The size of an array may be known at runtime, if you write int arr[] = {1, 2} the size is calculated by a compiler.
Accessing an unexisting element can cause undefined behaivor, which means that anything could happen, and in most cases you'll get garbage values.
Via int *array you specify a pointer array of type int
Unless a value is assigned, a pointer will point to some garbage address by default.
If you don't allocate memory at all or not fully allocate it or access unexisting element but try to use a pointer as an array, you'll get undefined behavior as expected.
After allocating memory (when the pointer is no longer needed) memory should be freed.
int array[100]; defines an array of int.
int *array; defines a pointer to an int. This pointer may point to an int variable or to an element of an array of int, or to nothing at all (NULL), or even to an arbitrary, valid or invalid address in memory, which is the case when it is an uninitialized local variable. It is a tad misleading to call this pointer array, but commonly used when naming a function argument that indeed points to an actual array. The compiler cannot determine the size of the array, if any, from the pointer value.
Here is a topographic metaphor:
Think of an array as a street with buildings. It has GPS coordinates (memory address) a name (but not always) and a fixed number of buildings (at a given time, hard to change). The street name together with the building number specifies a precise building. If you specify a number larger than the last number, it is an invalid address.
A pointer is a very different thing: think of it as a an address label. It is a small piece of paper that can be used to identify a building. If it is blank (a null pointer), it is useless and if you stick it to a letter and send that, the letter will get lost and discarded (undefined behavior, but it is easy to tell that it is invalid). If you write an invalid address on it, the effect is similar, but might cost much more before failing delivery (undefined behavior and difficult to test for).
If a street is razed (if memory was freed), previously written address labels are not modified, but they no longer point the anything useful (undefined behavior if you send the letter, the difficult kind). If a new street is later named with the name on the label, the letter might get delivered, but probably not as intended (undefined behavior again, memory was freed and some other allocated object happens to be at the same memory address).
If you pass a building to a function, you would usually not unearth it and truck it, but merely pass its street address (a pointer to the n-th building of the street, &array[n]). If you don't specify a building and just name the street, it means go to the beginning of the street. Similarly, when passing an array to a function is C, the function receives a pointer to the beginning of the array, we say that arrays decays as pointers.
Without specifying size in int * array, array[9999] = 30 can cause segmentation fault as it may lead to accessing of inaccessible memory
Basically int * array points to a random location. For accessing the 9999th element the array must point to a location having that much sufficient space. But the statement int * array doesn't explicitly creates any space for that.

How do arrays work inside a struct?

If I have for example
typedef struct node
{
int numbers[5];
} node;
Whenever I create an instance of such a struct there's gonna be allocation of memory in the stack for the array itself, (in our case 20 bytes for 5 ints(considering ints as 32 bits)), and numbers is gonna be a pointer to the first byte of that buffer. So, I thought that since inside an instance of node, there's gonna be a 20 bytes buffer(for the 5 ints) and a 4 bytes pointer(numbers), sizeof(node) should be 24 bytes. But when I actually print it out is says 20 bytes. Why is this happening? Why is the pointer to the array not taken into account?
I shall be very grateful for any response.
Arrays are not pointers:
int arr[10]:
Amount of memory used is sizeof(int)*10 bytes
The values of arr and &arr are necessarily identical
arr points to a valid memory address, but cannot be set to point to another memory address
int* ptr = malloc(sizeof(int)*10):
Amount of memory used is sizeof(int*) + sizeof(int)*10 bytes
The values of ptr and &ptr are not necessarily identical (in fact, they are mostly different)
ptr can be set to point to both valid and invalid memory addresses, as many times as you will
There is no pointer, just an array. Therefore the struct is of size sizeof( int[5] ) ( plus possible padding ).
The struct node and its member numbersshare the address. If you have a variable of type node or a pointer to that variable, you can access its member.
When you have a variable such as int x; space is set aside for the value. Whenever the identifier x is used, the compiler generates code to access the data in that space in the appropriate manner... there's no need to store a pointer to it to do this (and if there were, wouldn't you need a pointer to that pointer? And a pointer to that? etc.).
When you have an array like int arr[5];, space is set aside the same way, but for 5 ints. When the identifier arr is used, the compiler generates code to access either the relevant array element or give the address of the array (depending on how it's used). The array is not a pointer, and doesn't contain one... but the compiler may use its address instead of its contents in some situations.
An array is said to decay to a pointer to its first element in many situations, but that just means that in those situations the identifier will give its address instead of its contents, much like when you use the address-of operator with a non-array variable. The fact that you can get the address of the int x with &x doesn't mean x contains the address of an int... just that the compiler knows how to figure it out.
Arrays don't work like that. They only allocate space for their elements, but not for a pointer. The "pointer" you are talking about (numbers) is just a placeholder for the address of the array's first element; think of it as a literal, instead of a variable. Therefore, you can not assign a value to it.
int myint;
numbers = &myint;
This won't work, since there is no memory where you could store &myint. numbers will just be converted to an address at compile time.
Size of structure is always defined by the size of its members.
So its really doesn't matter whether members are simply int, char, float or arrary or even structure itself.

Multidimensional arrays: don't the pointers point to their own addresses?

I'm a student learning C, trying to wrap my head around something.
Let's say you have some multidimensional array int multi[2][2]. The value of multi will be a pointer to the address of multi[0]. For simplicity, let's say that address is 1000. Dereferencing multi[0] extracts another address, this address the address of multi[0][0]. Dereferencing that, in turn, gets us the value (or returns the address for assignment if on the left side.)
Now, multi + 1 returns the address of the second pointer. Let's say that we have 4 byte ints (of which there are two per nested array), so the address returned by multi + 1 will be 1008. This address will contain the address of multi[1][0], which will be 1008.
So here I see a pattern: doesn't this mean that the address in multi[0], multi[1], and so forth, contains a pointer to its own address?
Arrays and pointers are different. This topic is often presented poorly in books.
In the case of int multi[2][2], this is a block of4 contiguous ints. There are no pointers involved. C's type system divides this block up into two sub-arrays, each containing 2 ints.
These expressions: multi, multi[0] denote arrays. When you use an expression that denotes an array, in a context other than sizeof or & , then a conversion is performed, and the result of that conversion is a pointer to the first element of the array.
This pointer is an rvalue, i.e. it has the same sort of status as x + y : it's a value you can work with, but it doesn't consume any storage (at least, it's not stored as part of the array); and you can't use the & operator on it.
The value of multi will be a pointer to the address of multi[0]
If multi, which is an array, is used in an expression, it is converted to a pointer to multi[0], the value of which is the address of multi[0]. An array, when used in an expression, is always converted to a pointer to its first element ... thus arrays in C aren't first class objects and cannot be passed to functions (only their addresses can) or assigned (memcpy is used to copy arrays).
Dereferencing multi[0] extracts another address, this address the address of multi[0][0]
There's no "extraction". dereferencing multi[0] yields an array, which is converted to a pointer to its first element. The value of this pointer is the address of multi[0][0]. All of these addresses have the same value, because they point to exactly the same place in memory ... the first of four ints.
Now, multi + 1 returns the address of the second pointer.
Again, there is no pointer in memory. multi + 1 refers to an array ... the second of two arrays of two ints. Using that array in an expression converts it to a pointer to its first element, multi[1][0].
Let's say that we have 4 byte ints (of which there are three per nested array), so the address returned by multi + 1 will be 1012.
Er, you declared int multi[2][2], which is two arrays each of which contains two arrays. So multi + 1 is 1008.
This address will contain the address of multi[1][0], which will be 1012.
It makes no sense to talk about addresses containing addresses. pointers contain addresses, but we have no pointers in memory here, we only have ints. multi[1][0] is an int and its address is 1008. multi[1] is an array of ints and its address is also 1008 ... of course, because its address is the same as the address of its first element.
So here I see a pattern: doesn't this mean that the address in multi[0], multi[1], and so forth, contains a pointer to its own address?
There are no addresses in multi[0] and multi[1], there are arrays (of ints). arrays have addresses, but in this case they don't contain addresses. The way to "see a pattern" is to think in terms of what's in memory. Here, you simply have a block of 4 ints -- two arrays of two ints each; arrays have no overhead or other extraneous content in C, they just have the data ... what you declare is what you get. The array is an abstraction, a grouping of items determined by its type and size, which exist only at compile-time and in the mind of the programmer. Thus, multi[0] contains an array, in the abstract model of a C program, but all there is in memory is four ints, the first two of which comprise that array.
The situation would be different with
int* multi[2];
There you have an array of two pointers to int; each pointer will point to an array of ints (once you set it to the address of an array of ints, usually obtained from malloc). Once you allocate memory for the subarrays and assign the two pointers, you can use this multi much the way you use the multi above, but the type, size, and memory layout of multi[0] and multi[1] are quite different in the two cases.

Resources