Programming Language & Its Applications — Introduction to C Programming, NEC licence examination syllabus (Nepal Engineering Council).
Contiguous memory, zero-based indexing, and the row-major layout that makes address arithmetic work.
An array is a block of contiguous memory holding elements of one type. That single fact explains everything else: why indexing is O(1), why the size is fixed at compile time, why a[i] is really *(a + i), and why C cannot check your bounds. Arrays are also the foundation of every data structure in the DSA paper.
#include <stdio.h>
int main(void) {
int a[5] = {10, 20, 30, 40, 50};
int n = sizeof a / sizeof a[0];
for (int i = 0; i < n; i++)
printf("a[%d]=%-3d at %p\n", i, a[i], (void*)&a[i]);
/* these four are all the same element */
printf("%d %d %d %d\n", a[2], *(a+2), *(2+a), 2[a]);
return 0;
}
Output:
a[0]=10 at 0x7ffd1c2a4400
a[1]=20 at 0x7ffd1c2a4404 <-- +4 bytes each
a[2]=30 at 0x7ffd1c2a4408
a[3]=40 at 0x7ffd1c2a440c
a[4]=50 at 0x7ffd1c2a4410
30 30 30 30
That 2[a] is legal C — because a[i] is defined as *(a+i), and addition commutes. It is a curiosity, not a style to imitate, but it proves that array subscripting is pointer arithmetic.
#include <stdio.h>
#define R1 2
#define C1 3
#define C2 2
int main(void) {
int A[R1][C1] = {{1,2,3},{4,5,6}};
int B[C1][C2] = {{7,8},{9,10},{11,12}};
int C[R1][C2] = {0};
/* C[i][j] = sum over k of A[i][k] * B[k][j] */
for (int i = 0; i < R1; i++)
for (int j = 0; j < C2; j++)
for (int k = 0; k < C1; k++)
C[i][j] += A[i][k] * B[k][j];
for (int i = 0; i < R1; i++) {
for (int j = 0; j < C2; j++) printf("%4d", C[i][j]);
printf("\n");
}
return 0;
}
Output:
58 64
139 154
#include <stdio.h>
/* the array DECAYS to a pointer - these are identical */
void f1(int a[]) { printf("f1 sizeof=%zu\n", sizeof a); }
void f2(int *a) { printf("f2 sizeof=%zu\n", sizeof a); }
void modify(int a[], int n) {
for (int i = 0; i < n; i++) a[i] *= 2; /* AFFECTS caller */
}
/* 2-D: all dimensions except the first must be given */
void show2d(int m[][3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) printf("%3d", m[i][j]);
printf("\n");
}
}
int main(void) {
int a[5] = {1,2,3,4,5};
printf("main sizeof=%zu (5 ints)\n", sizeof a);
f1(a); f2(a);
modify(a, 5);
for (int i = 0; i < 5; i++) printf("%d ", a[i]);
printf("\n");
int m[2][3] = {{1,2,3},{4,5,6}};
show2d(m, 2);
return 0;
}
Output:
main sizeof=20 (5 ints)
f1 sizeof=8 <-- a POINTER, not the array!
f2 sizeof=8
2 4 6 8 10 <-- modify() changed the original
1 2 3
4 5 6
The sizeof result is the proof: 20 bytes in main, 8 bytes inside the function. Once an array is passed, its size information is gone — which is exactly why every C function taking an array also takes a length parameter. Forgetting that is the root of countless buffer overflows.
#include <stdio.h>
int main(void) {
int a[3] = {1, 2, 3};
/* C does NOT check this. It compiles and runs. */
printf("a[5] = %d\n", a[5]); /* UNDEFINED BEHAVIOUR */
/* Writing out of bounds corrupts adjacent memory or
crashes with a segmentation fault. */
/* a[100] = 42; <-- may silently destroy other data */
return 0;
}
Output (varies, machine-dependent):
a[5] = 32764 <-- whatever happened to be there
sizeof changes inside a function (decay), and that C performs no bounds checking. Row-major vs column-major is a favourite one-mark question.Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loading…