Welcome to your next C programming lesson. In our last session, we explored storage classes, understanding how a variable's scope and lifetime are determined. A key takeaway was that every variable, whether on the stack or in a static data segment, resides at a specific memory address. Today, we delve into one of C's most powerful and fundamental features: pointers.
This lesson will teach you to directly work with those memory addresses. Our goal is to master the manipulation of 1D and 2D arrays and strings by demonstrating the intimate relationship between pointers and arrays. This is not just a theoretical exercise; the pointer-array duality is a cornerstone of C and a frequent source of challenging multiple-choice questions in the GATE exam, where understanding memory representation is key.
The Pointer-Array Connection in One Dimension
Let's begin with the simplest case: a one-dimensional array. When you declare an array like int arr[5];, the compiler allocates a contiguous block of memory large enough to hold five integers. The "magic" in C is how it treats the array's name, arr.
In most contexts, the name of an array decays into a pointer to its first element. This means arr behaves exactly like &arr[0].
To build a solid understanding of this principle, we will work through a clear, step-by-step video.
#24 C Pointers and Arrays | C Programming For Beginners
This video from Programiz provides an excellent introduction to the relationship between 1D arrays and pointers. It builds the concepts from the ground up.
First, watch the brief introduction to array access using indices. Next, see how memory addresses are printed and discover the crucial fact that the array name points to the first element's address. Watch from this segment. Pay close attention to the output showing numbers and &numbers[0] produce the same address. Now, learn about pointer arithmetic. Understand how numbers + i gives the address of the i-th element by watching from this part. The compiler is smart: + i doesn't add i bytes; it adds i * sizeof(element_type) bytes. Finally, see how to dereference these pointers to get the actual values. The expression *(numbers + i) is the pointer equivalent of numbers[i]. Watch this explained from this section.
From this, we establish two fundamental equivalences for a 1D array arr:
arr + iis equivalent to&arr[i](both yield the address of the i-th element).*(arr + i)is equivalent toarr[i](both yield the value of the i-th element).
This interchangeability is what we call the pointer-array duality. You can even use this to modify array elements, as shown in the video. For example, *(arr + 2) = 10; is the same as arr[2] = 10;.
Strings: Arrays of Characters
In C, a string is simply a one-dimensional array of characters, terminated by a special null character \0. For example, char name[] = "GATE"; creates an array of 5 characters: 'G', 'A', 'T', 'E', '\0'.
Because strings are arrays, everything we just learned applies directly.
nameis a pointer to the first character, 'G'.name + 1is a pointer to the second character, 'A'.*(name + 2)gives the value of the third character, 'T'.
One common GATE trap involves string literals. If you declare char *p = "GATE";, p is a pointer to a string literal. These literals are often stored in a read-only part of memory. Attempting to modify it, like *(p + 1) = 'X';, results in undefined behavior, which can crash your program.
The Nuances of 2D Arrays and Pointers
Now, let's extend this to two-dimensional arrays. This is where the concepts become more subtle and where GATE questions often test for deep understanding. A 2D array, like int A[3][3];, is best thought of as an array of arrays. In this case, A is an array of three elements, where each element is itself an array of three integers.
This changes the meaning of the array name A.
Ais not a pointer to anint.Ais a pointer to a 1D array of 3 integers.
This distinction is critical. Let's dissect it with a detailed video.
1.6 Pointers and 2-D Arrays | Two dimensional Array | Data Structures & Algorithm Tutorials
Jenny's Lectures CS IT offers a fantastic deep dive into the complexities of 2D arrays and pointers, covering many common points of confusion.
First, understand the memory layout and the "array of arrays" concept. Watch from the beginning to see how a 2D array is stored linearly and how A[0], A[1], etc., are names for the individual 1D arrays (rows). Next, see why assigning the 2D array name to a simple integer pointer (int *p = A;) causes a type mismatch. A points to a whole array, not a single integer. This is explained from this segment. Now, explore the various ways to get the base address and initialize a pointer correctly (e.g., p = A[0] or p = &A[0][0]). The video breaks down the meaning of expressions like A, *A, and A[0] from this section. This is a critical part for MCQs. The most important part is understanding pointer arithmetic. Observe the difference between A + 1 (moves to the next row) and *A + 1 (moves to the next element in the first row). This is masterfully explained from this segment. Finally, see the generalized formula for accessing an element A[i][j] using pointers: *(*(A + i) + j). This derivation, shown from this part, is a must-know for GATE.
Let's summarize the key pointer arithmetic rules for a 2D array A[R][C]:
A: A pointer to the first row (A[0]), which is a 1D array ofCintegers. Its type isint (*)[C].A + i: A pointer to the i-th row (A[i]). Adding 1 toAadvances the address byC * sizeof(int)bytes.*(A + i): This is equivalent toA[i]. It dereferences the row pointer to give you the i-th row itself. The result is a pointer to the first element of that row, i.e.,&A[i][0]. Its type isint *.*(A + i) + j: This isA[i] + j. It's a pointer to the j-th element in the i-th row (&A[i][j]).*(*(A + i) + j): This is*(A[i] + j), which is equivalent toA[i][j]. It gives the value at that element.
A Final Tricky Point: A vs &A
You might notice that printing A and &A gives the same address value (the base address of the array). However, they are of different types, which becomes evident when you perform arithmetic.
Ais a pointer to the first row (an array ofCints).&Ais a pointer to the entire 2D array (an array ofRarrays ofCints).
Therefore:
A + 1points to the next row. The address increases byC * sizeof(int).&A + 1points past the entire 2D array. The address increases byR * C * sizeof(int).
This distinction is explained beautifully in the final part of the video you just watched.
1.6 Pointers and 2-D Arrays | Two dimensional Array | Data Structures & Algorithm Tutorials
To solidify this final, advanced concept, revisit the video. The explanation uses a 1D array as an analogy to make the point clear.
Watch the section comparing a+1 with &a+1 for a 1D array, and then see how this logic extends to the 2D array case. This is covered from this crucial segment.
Conclusion
Today, we have unraveled the deep and often intricate relationship between pointers and arrays in C. Mastering these concepts is non-negotiable for excelling in GATE, as they form the basis for many questions on program output and behavior.
Key Takeaways:
- An array's name typically decays into a pointer to its first element.
- For a 1D array
arr,arr[i]is syntactically equivalent to*(arr + i). - A 2D array
A[R][C]is an array of 1D arrays.Ais a pointer to the first row, not the first element. - The expression to access
A[i][j]using pointers is*(*(A + i) + j). - Pointer arithmetic depends on the type of the pointer.
A + 1advances by the size of a row, while&A + 1advances by the size of the entire array. - Strings are null-terminated character arrays and follow the same rules as 1D arrays.
Having mastered how to access existing memory locations using pointers, our next logical step is to learn how to request memory from the system at runtime. In the next lesson, we will explore dynamic memory management, using functions like malloc(), calloc(), and free() to build more flexible and powerful programs.