Arrays and C++ Integration
Pointer Array Duality
The Secret Life of C Arrays
In the world of C programming, arrays and pointers share a uniquely intimate relationship. Understanding this connection is the key to unlocking C's power for low-level memory manipulation. To put it simply, an array name is essentially a constant pointer to the first element in that array.
Let's consider a simple integer array. When you declare int arr[5];, the compiler allocates a contiguous block of memory large enough to hold five integers. The name arr itself behaves like a pointer to the very first element, arr[0]. This means you can use the name arr in most contexts where a pointer is expected.
Pointers and arrays are intimately linked in C programming.
You can see this relationship in action. The expressions arr and &arr[0] will yield the same memory address. This is the base address of the array, the starting point from which all other elements are located.
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
printf("Value of arr: %p\n", arr);
printf("Address of arr[0]: %p\n", &arr[0]);
return 0;
}
/* Output will show the same memory address for both lines */
Navigating Memory with Pointer Arithmetic
Because the array name acts as a pointer, you can access its elements using pointer arithmetic. When you add an integer to a pointer, the compiler doesn't just add the number; it adds that number multiplied by the size of the data type the pointer points to. This allows you to 'step' through memory in units of your data type.
For an array arr, the memory address of the element at index i is calculated as:
address of arr[i] = base_address + i * sizeof(element_type)
This means that arr[i] is equivalent to *(arr + i). The first expression is array subscript notation, which is more readable. The second is pointer notation, using the dereference operator (*) to access the value at the calculated memory address. This equivalence is at the heart of C's flexibility.
Let's make this concrete. If an integer takes up 4 bytes and arr is at address 0x1000, then arr + 1 doesn't point to 0x1001. It points to 0x1004, the address of the next integer, arr[1]. The dereference operator (*) is then used to retrieve the value stored at that new address.
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
// Accessing elements using pointer arithmetic
printf("Value at index 2 (arr[2]): %d\n", arr[2]);
printf("Value at index 2 (*(arr + 2)): %d\n", *(arr + 2));
// Using a separate pointer
int *p = arr; // p now points to the first element
printf("Value pointed to by p: %d\n", *p); // Dereferences p to get 10
p++; // Increment the pointer to the next element
printf("Value after p++: %d\n", *p); // Now prints 20
return 0;
}
The Mystery of Array Decay
An important concept related to arrays and pointers is "array decay." When an array is passed to a function, what's actually passed is not the entire array, but a pointer to its first element. The array essentially "decays" into a pointer.
This has a significant implication: inside the function, the sizeof operator will return the size of a pointer, not the size of the entire array. This is a common source of bugs for new C programmers. You lose the array's size information once it's passed as a parameter.
#include <stdio.h>
// The function receives a pointer, even if it looks like an array
void print_size(int arr[]) {
// This will print the size of a pointer, not the array's size!
printf("Size inside function: %zu bytes\n", sizeof(arr));
}
int main() {
int my_array[10];
printf("Size in main: %zu bytes\n", sizeof(my_array));
print_size(my_array);
return 0;
}
/*
Typical Output:
Size in main: 40 bytes
Size inside function: 8 bytes (on a 64-bit system)
*/
Because of array decay, it's standard practice in C to pass the size of the array as a separate argument to any function that needs to operate on it. Without that extra piece of information, the function has no way of knowing where the array ends.
Arrays vs. Pointers: The Key Differences
While they are closely related, arrays and pointers are not identical. The most crucial difference is that an array name is a constant pointer. It refers to a fixed memory location—the beginning of the array. You cannot change where it points.
int arr[10];
int* p;
p = arr; // VALID: p now points to the first element of arr.
arr = p; // INVALID: You cannot change where arr 'points'.
arr++; // INVALID: You cannot increment the array's base address.
p++; // VALID: The pointer p now points to the next element.
Another difference is how memory is allocated. Declaring an array like int arr[10]; allocates memory on the stack (for local variables) or in the static data segment (for global/static variables). This memory is managed automatically. Pointers, on the other hand, often point to memory that has been dynamically allocated on the heap using functions like malloc. This type of memory management must be handled manually.
In C, what is the primary relationship between an array's name and a pointer?
Given the C code int arr[] = {10, 20, 30, 40};, which expression is equivalent to arr[2]?