Showing posts with label dynamic arrays. Show all posts
Showing posts with label dynamic arrays. Show all posts

Wednesday, September 1, 2010

Dynamic allocation of arrays in cpp

The problems with fixed size arrays

Declaring an array with a fixed size like
int a[100000];
has two typical problems:
  • Exceeding maximum. Choosing a real maximum is often impossible because the programmer has no control over the size of the data sets the user is interested in. Erroneous assumptions that a maximum will never be exceeded are the source of many programming bugs. Declaring very large arrays can be extremely wasteful of memory, and if there are many such arrays, may prevent the program from running in some systems.
  • No expansion. Using a small size may be more efficient for the typical data set, but prevents the program from running with larger data sets. If array limits are not checked, large data sets will run over the end of an array with disastrous consequences. Fixed size arrays can not expand as needed.
These problems can be avoided by dynamically allocating an array of the right size, or reallocating an array when it needs to expand. Both of these are done by declaring an array as a pointer and using the new operator to allocate memory, and delete to free memory that is no longer needed.
This is exactly what is vector does, but let's see how it's done with an array.

Declare array as a pointer, allocate with new

To create a variable that will point to a dynamically allocated array, declare it as a pointer to the element type. For example,
int* a = NULL;  // pointer to an int, intiallly to nothing.
A dynamically allocated array is declared as a pointer, and must not use the fixed array size declaration. The above declaration creates a pointer, but doesn't yet allocate any memory to it.

Allocate an array with code>new

When the desired size of an array is known, allocate memory for it with the new operator and save the address of that memory in the pointer. Remember: Pointers may be subscripted just as arrays are. The example below reads in a number and allocates that size array.
int* a = NULL;   // Pointer to int, initialize to nothing.
int n; // Size needed for array
cin >> n; // Read in the size
a = new int[n]; // Allocate n ints and save ptr in a.
for (int i=0; i
a[i] = 0; // Initialize all elements to zero.
}
. . . // Use a as a normal array
delete [] a; // When done, free memory pointed to by a.
a = NULL; // Clear a to prevent using invalid memory reference.

Freeing memory with delete

When you are finished with dynamically allocated memory, free it with the delete operator. After memory is freed, it can be reused by later new requests. Memory that your program didn't free will be freed when the program terminates. Never free memory that wasn't dynamically allocated - the results are unpredictable.
delete [] a;  // Free memory allocated for the a array.
a = NULL; // Be sure the deallocated memory isn't used.

Use [] when deleting arrays

You must specify "[]" when deleting an array, but not for a single value. It isn't possible to delete only part of an array.

Do you have to reset a pointer after delete?

Following the delete in these examples, I reset the pointer to NULL. This isn't strictly necessary, but it's very good practice so that any use of the pointer will produce an error. Attempts to use memory location 0, which is the normal default value of NULL, will be blocked by the way most operating systems allocate memory.
Why doesn't delete reset the pointer? It does in some systems, but the language specification does not require it, so not all systems do it.

2d array and pointers in details

Expand|Select|Wrap|Line Numbers
  1. int array[5][10];
  2.  
Here array is the address of array[0]. Since array[0] is an array of 10 int, array is the address of an array of 10 int. You can assign the name array to a pointer to an array of 10 int:
Expand|Select|Wrap|Line Numbers
  1. int array[5][10];
  2.  
  3. int (*ptr)[10] = array;
  4.  
Fourth, when the number of elements is not known at compile time, you create the array dynamically:

Expand|Select|Wrap|Line Numbers
  1. int* array = new int[value];
  2. int (*ptr)[10] = new int[value][10];
  3. int (*ptr)[10][15] = new int[value][10][15];
  4.  
In each case value is the number of elements. Any other brackets only describe the elements.

Using an int** for an array of arrays is incorrect and produces wrong answers using pointer arithmetic. The compiler knows this so it won't compile this code:

Expand|Select|Wrap|Line Numbers
  1. int** ptr = new int[value][10];    //ERROR
  2.  
new returns the address of an array of 10 int and that isn't the same as an int**.

Likewise:
Expand|Select|Wrap|Line Numbers
  1. int*** ptr = new int[value][10][15];    //ERROR
  2.  
new returns the address of an array of 10 elements where each element is an array of 15 int and that isn't the same as an int***.

Tuesday, August 31, 2010

How to bound check arrays in cpp / c

Bound checking in cpp /c is headache....
char *strcpy(char *dest, const char *src)
{
char *save = dest;
while(*dest++ = *src++);
return save;
}

//main func
char *src = "hello to c programming language";
char dest[12];

strcpy(dest,src); //calling function

Here we have no bound check on dest size or src size. When we pass it to function it is perfectly alright but
problem is dest is array which is just 12 bytes long...but src is larger string...

So if programmer is lucky , he gets Error - "Segmentation fault"
else in worse case, he gets his core dumped...that is his memory may have changed the effect of it can be seen after few days.

What's the solution?
We cant change this library function to check bound check, like sending size to it with both src and dest...because many programs might be using it...and this change may hamper these million of programs. So it is the responsibility of programmer to check whether he has provided enough space or not?
Note: There is no way right now to check bounds by [] operator.

Vectors
A vector will do bounds checking if you use the at() function, for example:
std::vector v(5);
v
.at(3) = 10;
v
.at(5) = 20; // throws an exception, std::out_of_range
However, if you use operator[], there is no bounds checking. (And accessing non-existent elements leads to undefined behavior.)