IAE Academy

GATE CS

Pointers, arrays and strings in C

Pointers, arrays and strings are where C's memory model shows through its syntax. The topic covers how an array name becomes a pointer to its first element, how an address is computed from a base address and an index, and how the same idea extends to 2-D and 3-D arrays, pointer-to-pointer chains, function pointers, dynamically allocated memory, and structures and unions. Predicting the output of a C program usually means computing an address correctly or following a chain of dereferences step by step. That makes this topic the working foundation for reading C code, not only for questions that mention pointers by name.

11 previous-year questions · 11 solved · 7 practice questions

1. Arrays, pointers, and array-to-pointer decay

EXAM TIP

Conventions used throughout these notes, as GATE assumes them unless a question says otherwise: sizeof(int) = 4 bytes and sizeof(char) = 1 byte (1 by definition in C). Memory is byte-addressable, characters are ASCII, and arrays are stored row-major. The size of a pointer depends on the machine (commonly 4 bytes on 32-bit systems and 8 on 64-bit systems), and all object pointer types are assumed to have the same size. A question that needs a pointer's size will state it.

In an expression, an array name decays to a pointer to its first element: for T a[N], a behaves like &a[0], of type T *. There are exactly three exceptions, where no decay happens:

1. when the array is the operand of sizeof, so sizeof(a) is the size of the whole array;
2. when the array is the operand of unary &, so &a has type T (*)[N], a pointer to the whole array;
3. when a string literal is used to initialise a char array, as in char t[] = "GATE";.

a and &a hold the same numeric address but have different types. So a + 1 moves forward by one element (sizeof(T) bytes), while &a + 1 moves past the whole array (N * sizeof(T) bytes). An array name is not a modifiable lvalue: a++ and a = p are compile-time errors, whereas p++ on a pointer variable p is legal.

1-D ARRAY INDEXING / POINTER ARITHMETIC

For any array a with element type T. The same value is the address a + i. C arrays always start at index 0, and memory is byte-addressable.

Pointer arithmetic is always scaled by the size of the type pointed to. p + k moves forward k * sizeof(*p) bytes, not k bytes. So p++ on an int * moves 4 bytes (with sizeof(int) = 4), while p++ on a char * moves 1 byte.

Subtracting two pointers, or comparing them with <, >, <= or >=, is defined only when both point into the same array (or one past its last element). == and != may compare any two pointers of compatible type. The difference p2 - p1 has type ptrdiff_t and counts elements between the two pointers, not bytes.

sizeof and decay: the rules to apply

  1. When the operand is the array itself, as in int a[10]; then sizeof(a), the result is the size of the WHOLE array: 10 × sizeof(int) = 40 bytes.

  2. Once the array has become a pointer, sizeof gives sizeof(T *), never the array's size. This happens when the array is passed to a function (the parameter is a pointer) or assigned to a pointer variable.

  3. a[i] is defined as *(a + i). So a[i] and i[a] are both legal C and mean the same thing, since both expand to *(a + i).

  4. A 2-D array parameter written int arr[][C] is adjusted to int (*arr)[C], a pointer to an array of C ints. Only the first dimension may be left out. Every other dimension must be given, because the row length is needed for indexing.

Combining * and ++ (postfix ++ binds tighter than unary *; prefix ++ and unary * group right to left)

  1. *p++ means *(p++): the value used is the element p pointed at, and p then moves forward one element.

  2. *++p means *(++p): p moves forward first, then the new element is read.

  3. ++*p means ++(*p): the element pointed at is incremented, and p does not move.

  4. (*p)++: the element pointed at is incremented after its old value is used, and p does not move.

WORKED EXAMPLE

Given int a[6] = {10, 20, 30, 40, 50, 60}; and int *p = a + 2;, what is the value of *(p + 3) - *p, and what does p - a equal?

Solution
  1. p = a + 2 points at a[2], which holds 30.

  2. *(p + 3) is *(a + 5), which is a[5] = 60.

  3. *p is a[2] = 30.

  4. So *(p + 3) - *p = 60 − 30 = 30.

  5. p - a subtracts two pointers into the same array. It counts elements, giving 2, not the byte distance 8.

Answer: *(p + 3) - *p = 30; p - a = 2

EXAM TIP

An array parameter is a pointer inside the function. So when a question passes an array to a function and asks for sizeof of that parameter inside the function, the answer is sizeof of a pointer, whatever length is written in the parameter declaration.

2. Multi-dimensional arrays and address calculation

C always stores multi-dimensional arrays in row-major order: every element of row 0, then every element of row 1, and so on. A 2-D array T a[R][C] is one contiguous block of R × C elements, viewed as R rows of C elements each. Column-major storage appears only in language-neutral questions that state it explicitly. C itself never stores arrays column-major.

For int a[R][C], sizeof(a) = R × C × 4, sizeof(a[i]) = C × 4 (one row), and sizeof(a[i][j]) = 4.

2-D ROW-MAJOR ADDRESS (C ARRAYS)

The array is declared T a[R][C]. C is the number of COLUMNS (the row length), i is the row index and j is the column index, both starting at 0. The number of rows R does not appear.

3-D ROW-MAJOR ADDRESS

The array is declared T a[X][Y][Z], with all indices starting at 0. The first dimension X does not appear.

GENERAL ROW-MAJOR ADDRESS WITH ARBITRARY INDEX BOUNDS

Use this for a language-neutral array A[l1..u1][l2..u2] with element size w bytes, where Base is the address of A[l1][l2]. For column-major storage, swap the roles of the two indices: Base + ((j − l2)(u1 − l1 + 1) + (i − l1)) × w. For C arrays, l1 = l2 = 0 and this reduces to the formula above.

COLUMN-MAJOR VARIANT, 0-BASED (ONLY WHEN A QUESTION STATES COLUMN-MAJOR STORAGE)

R is the number of rows. C never stores arrays this way. Use this formula only when a question says the array is stored column-major.

The name a of a 2-D array int a[R][C] decays to int (*)[C], a pointer to a whole row, not to int *. So a + 1 skips one entire row (C × sizeof(int) bytes), while *a + 1 or a[0] + 1 skips one element. a[i][j] is *(*(a + i) + j).

a, a[0], &a and &a[0][0] all hold the same numeric address but have different types, so adding 1 to each moves by a different amount.

Expression

Type

What it points to / equals

a

int (*)[C]

address of row 0 (same as &a[0])

a + i

int (*)[C]

address of row i (same as &a[i])

*(a + i) or a[i]

int * (after decay)

address of a[i][0]

*(a + i) + j

int *

address of a[i][j]

*(*(a + i) + j) or a[i][j]

int

the value stored at a[i][j]

&a

int (*)[R][C]

address of the whole array (same number as a)

&a + 1

int (*)[R][C]

address just past the whole array: Base + R × C × sizeof(int)

EXAM TIP

For any multi-dimensional address question, first find the TYPE of the pointer being added to. A row pointer int (*)[C] moves C elements per +1. An element pointer int * moves one element per +1. A whole-array pointer int (*)[R][C] moves R × C elements per +1.

3. Pointers to pointers, pointer parameters and function pointers

A pointer-to-pointer T **p stores the address of a variable that itself stores the address of a T. Its common uses are:

  • an array of row pointers that simulates a 2-D array;
  • letting a function change the caller's pointer: pass T ** when the function must reassign it, for example to store newly allocated memory;
  • argv in int main(int argc, char **argv).

Each * removes one level of indirection. If pp is int **, then pp is an address, *pp is an int *, and **pp is an int.

C passes every argument by value, pointers included. A function that receives int *p gets its own copy of the address. It can change the object *p refers to, but reassigning p inside the function does not change the caller's pointer. To change the caller's pointer, the function must receive its address, as a T **.

WORKED EXAMPLE

What does the following program print?

#include <stdio.h>

int i = 0, j = 1;

void f(int *p, int *q) {
    p = q;
    *p = 2;
}

int main() {
    f(&i, &j);
    printf("%d %d", i, j);
    return 0;
}
Solution
  1. The call f(&i, &j) copies the address of i into p and the address of j into q.

  2. p = q; changes only the local copy p, which now points at j. i is not affected.

  3. *p = 2; therefore stores 2 into j.

  4. Back in main, i is still 0 and j is 2, so the program prints 0 2.

Answer: 0 2

A function pointer stores the address of a function. You can call through it, pass it as an argument (callbacks, such as the comparison function given to qsort), or store several in an array to build a dispatch table. Its declared parameter types and return type must match the function's.

Function pointer declaration and use

  1. Declare it as return_type (*name)(parameter_types);. The parentheses around *name are required: int *f(int); declares a function that returns int *, which is something else entirely.

  2. Assign with name = func; or name = &func;. Both mean the same thing, because a function name also decays to its address.

  3. Call with name(args) or (*name)(args). Both mean the same thing.

  4. The comparison function passed to the standard library's qsort has type int (*)(const void *, const void *).

WORKED EXAMPLE

What does the following program print?

#include <stdio.h>

int add(int x, int y) { return x + y; }
int sub(int x, int y) { return x - y; }

int main() {
    int (*ops[2])(int, int) = {add, sub};
    int result = ops[1](10, 4);
    printf("%d", result);
    return 0;
}
Solution
  1. ops is an array of 2 pointers to functions that take (int, int) and return int.

  2. After initialisation, ops[0] points to add and ops[1] points to sub.

  3. ops[1](10, 4) calls sub(10, 4), which returns 10 − 4 = 6.

  4. The program prints 6.

Answer: 6

EXAM TIP

Read a complicated declaration with the right-left rule. Start at the identifier. Read to the right ([] means array of, () means function returning) until you reach a closing parenthesis or the end. Then read to the left (* means pointer to). Step out one level of parentheses and repeat. For int (*ops[2])(int, int): ops is an array of 2, of pointers, to functions taking (int, int), returning int.

4. Strings, string literals, and standard library functions

C has no separate string type. A string is a sequence of chars ending in a null character '\0', stored in a char array or reached through a char *. The string functions in <string.h> find the end of a string by searching for this terminator. If a function is given a character array with no terminator, or writes past the end of the destination array, it reads or writes memory outside the array, which is undefined behaviour. sizeof(char) is 1 by definition, and GATE assumes ASCII codes (for example 'A' = 65, 'a' = 97, '0' = 48).

STORAGE FOR `CHAR T[] = "...";` (SIZE LEFT OUT)

n is the number of characters in the literal, which contains no embedded '\0'. The +1 is the terminator. So char t[] = "GATE"; gives sizeof(t) = 5 while strlen(t) = 4. If a size is written, the array has that size: char t[10] = "GATE"; has sizeof(t) = 10, with the unused bytes set to '\0'. char t[4] = "GATE"; is legal C but stores NO terminator.

Function

Behaviour

Trap

strlen(s)

Counts the characters before the first '\0', not counting the terminator

For char t[] = "...", sizeof(t) and strlen(t) differ by exactly 1

strcpy(dst, src)

Copies src into dst, terminator included; no bounds check

dst needs room for at least strlen(src) + 1 bytes, or it overflows silently

strncpy(dst, src, n)

Copies at most n characters; if src is shorter, pads the rest of the n bytes with '\0'

If strlen(src) >= n, dst is NOT null-terminated

strcat(dst, src)

Writes src over dst's terminator, then adds a new terminator

dst needs a total of strlen(dst) + strlen(src) + 1 bytes

strcmp(a, b)

Compares byte by byte (as unsigned char) until the strings differ; returns 0 if equal, a negative value if a < b, a positive value if a > b

Only the SIGN of the result is guaranteed, not −1, 0 or 1

strchr(s, c) / strstr(s, sub)

Returns a pointer to the first occurrence, or NULL if there is none

Check for NULL before dereferencing the result

char *s = "GATE"; makes s a pointer to a string literal. The literal may be stored in read-only memory, and modifying it through s is undefined behaviour. char t[] = "GATE"; copies the characters into a new, writable array t (on the stack if t is a local variable), so writing to t[0] is well defined.

The two declarations differ in three more ways:

  • s can be pointed elsewhere (s = "IAE"; is legal), but t is an array name and cannot be assigned (t = "IAE"; is a compile-time error);
  • sizeof(s) is the size of a pointer;
  • sizeof(t) is 5.

WORKED EXAMPLE

Trace char buf[6]; then strcpy(buf, "GATE"); then strcat(buf, "!");. Is this safe, and what does buf hold afterwards?

Solution
  1. "GATE" needs 5 bytes, 4 characters plus the terminator. buf has 6, so strcpy fits and fills bytes 0 to 4. Byte 5 keeps an indeterminate value.

  2. strcat finds the terminator at index 4, overwrites it with '!', and writes a new terminator at index 5.

  3. The result "GATE!" needs 6 bytes (5 characters plus the terminator). buf has exactly 6, so the code is safe with no spare room. One more appended character would overflow.

  4. Final contents: 'G', 'A', 'T', 'E', '!', '\0'.

Answer: Safe (it fits exactly); buf holds "GATE!"

WORKED EXAMPLE

What does the following fragment print (ASCII codes)?

char *p = "GATE2027";
printf("%s", p + (p[3] - p[1]));
Solution
  1. p[3] is 'E' (ASCII 69) and p[1] is 'A' (ASCII 65).

  2. p[3] - p[1] = 69 − 65 = 4, an ordinary int.

  3. p + 4 points at the 5th character, '2'.

  4. printf("%s", ...) prints from there up to the terminator: 2027.

  5. Note the parentheses. Without them, p + p[3] - p[1] is evaluated as (p + 69) - 65, and the intermediate pointer p + 69 lies outside the 9-byte literal, which is undefined behaviour even though the final pointer would be back inside it.

Answer: 2027

EXAM TIP

When code uses strncpy, check whether dst is later used as a string. If strlen(src) >= n, dst has no terminator, so a later strlen(dst) or printf("%s", dst) reads past the array. That is undefined behaviour, not a neat truncation.

5. const with pointers, dangling and NULL pointers

Where const binds: read right to left from the identifier

  1. const int *p; (or int const *p;): p is a pointer to a const int. *p cannot be modified through p, but p itself can be pointed somewhere else.

  2. int * const p;: p is a const pointer to int. p cannot be reassigned once initialised, but *p can be modified.

  3. const int * const p;: neither the pointer nor the value it points to can be changed.

  4. The address of a plain int can be stored in a const int *. Storing the address of a const int in a plain int * without a cast is a constraint violation: the compiler must issue a diagnostic, and many compilers warn rather than refuse to compile. Modifying a const object through such a pointer is undefined behaviour.

A dangling pointer still holds an address, but the object at that address no longer exists. The two usual causes are memory that has been freed and the address of a local (automatic) variable used after its function has returned. Dereferencing a dangling pointer is undefined behaviour. It may appear to work, print garbage, or crash, and the result can differ from run to run.

How a pointer becomes dangling

  1. Returning the address of a local variable, as in int *f() { int x = 5; return &x; }. The storage for x ends when f returns, so the returned pointer dangles.

  2. Using a pointer after free(p);. free does not change p, and the memory may be handed out again by a later allocation. Reads or writes through the old pointer can then corrupt unrelated data.

  3. Two pointers to the same block, where the block is freed through one of them. Using the other afterwards is a use of a dangling pointer. Freeing it as well is a double free, which is also undefined behaviour.

  4. After a successful realloc, the old object's lifetime has ended. Any other pointer that pointed into the old block must not be used, even if realloc happened to return the same address.

NULL is a pointer value guaranteed not to point to any object. It marks 'no target': malloc returns it on failure, and it ends a linked list. Dereferencing a null pointer is undefined behaviour and usually crashes in practice. NULL is a known value you can test for. A dangling pointer looks like an ordinary non-null address, so no test on the pointer's value can detect it.

EXAM TIP

free(p) does not set p to NULL. So an if (p != NULL) check after free(p) does NOT guard against using a dangling pointer. Only an explicit p = NULL; after the free makes that check meaningful.

6. malloc/free and dynamic arrays

The allocation functions, declared in <stdlib.h>:

  • malloc(size) reserves size bytes on the heap and returns a void *, or NULL on failure. The memory is NOT initialised, so its contents are indeterminate.
  • calloc(n, size) reserves n * size bytes and sets every byte to zero.
  • realloc(p, newsize) resizes an earlier allocation. It may move the block, returning the new address, and the old block must not be used after a successful call. On failure it returns NULL and leaves the ORIGINAL block valid, and that block must still be freed.

In C, a void * converts to any object pointer type without a cast, so int *q = malloc(n * sizeof(int)); needs no cast. A void * itself cannot be dereferenced, and standard C does not allow arithmetic on it.

Rules for dynamic memory

  1. Check the result of malloc, calloc or realloc against NULL before dereferencing it.

  2. Free each successful allocation exactly once. Freeing the same pointer twice (a double free) is undefined behaviour. Never freeing it is a memory leak: a defect, but not undefined behaviour.

  3. Never use memory after freeing it. Pass free only a pointer returned by malloc, calloc or realloc, never an address inside a block or of a stack or global variable. free(NULL) is allowed and does nothing.

  4. Store the result of realloc in a temporary pointer first: tmp = realloc(p, newsize); if (tmp) p = tmp;. Writing p = realloc(p, newsize); loses the only pointer to the original block when realloc fails and returns NULL, which leaks it.

WORKED EXAMPLE

What is wrong with the following function, and what is the likely effect?

int *make_array(int n) {
    int arr[n];
    for (int i = 0; i < n; i++)
        arr[i] = i * i;
    return arr;
}
Solution
  1. arr is a variable-length array (C99) with automatic (stack) storage, local to make_array.

  2. return arr; returns the address of its first element, but the array's storage ends when make_array returns.

  3. The returned pointer therefore dangles as soon as the function returns. Any use of it in the caller is undefined behaviour, even if it 'happens to work' because that stack memory has not been overwritten yet.

  4. Fix: allocate on the heap with int *arr = malloc(n * sizeof(int));, check it for NULL, and return it. Heap memory lasts until it is freed.

Answer: It returns a dangling pointer to a local array, so using the result in the caller is undefined behaviour. Allocate the array with malloc instead.

EXAM TIP

Three common ways to build an R × C dynamic array of int:

(i) An array of row pointers, int **p, with each row allocated separately. The rows need not be next to each other in memory. Free each row, then p.

(ii) One block, int *b = malloc(R * C * sizeof(int));, indexed by hand as b[i * C + j]. The memory is contiguous and needs one free.

(iii) One block through a pointer to a row, int (*q)[C] = malloc(R * sizeof *q);. The memory is contiguous, needs one free, and is indexed as q[i][j].

Both (i) and (iii) use [i][j] syntax. Decide the layout from the pointer's declared TYPE, not from how it is indexed.

7. Structures and unions: sizeof and padding

A struct's members are stored in declaration order. The compiler may insert padding bytes before a member so that it starts at an offset that is a multiple of its alignment. The usual convention for these questions is that each basic type's alignment equals its size: char 1, short 2, int 4, double 8. The struct's total size is then rounded up to a multiple of the largest alignment among its members, so that every element of an array of the struct stays correctly aligned. The first member is always at offset 0.

MEMBER OFFSET (ALIGNMENT RULE)

Apply it member by member, in declaration order. 'End of previous member' is that member's offset plus its size, and it is 0 for the first member. align(m) is the alignment of m's type, equal to its size under the convention above.

STRUCT TOTAL SIZE (WITH TRAILING PADDING)

E is the end offset of the last member (its offset plus its size). A_max is the largest alignment among all members.

Reordering members can change the size. Padding depends on member order. Declaring members in decreasing order of alignment (all doubles, then ints, then shorts, then chars) generally gives the least padding. Alternating large and small members gives the most.

All the members of a union start at offset 0 and overlap, so there is no padding between them. Its size is the size of its largest member, rounded up to a multiple of the largest alignment among ALL its members, which need not be the largest member's own alignment. Only one member holds a meaningful value at a time. Reading a member other than the one last written reinterprets the stored bytes as the new type, so the value depends on how the machine represents data, for example its byte order.

UNION SIZE

S_max is the size of the largest member and A_max is the largest alignment among all members.

Declaration

Size (int 4 bytes with 4-byte alignment, char 1 byte)

Why

struct { char c1; int i; char c2; };

12 bytes

c1 (1) + padding (3) + i (4) + c2 (1) + trailing padding (3); worked in practice question 4

struct { int i; char c1; char c2; };

8 bytes

i (4) + c1 (1) + c2 (1) + trailing padding (2); putting the int first reduces padding

union { int i; char c1; char c2; };

4 bytes

every member at offset 0; largest member is int (4), already a multiple of 4

union { char c[5]; int i; };

8 bytes

largest member is 5 bytes, rounded up to a multiple of int's alignment (4)

Through a pointer to a struct, members are reached with ->: p->m is exactly (*p).m. sizeof(p) is the size of a pointer, while sizeof(*p) is the size of the whole structure. Structures never decay to pointers, so check which of the two a question uses.

EXAM TIP

When a question states its own alignment rule (for example 'every type's alignment equals its size, and a struct's size is padded to a multiple of its largest member's alignment'), apply exactly that rule. Real compilers can differ by platform, but the question's stated rule is the one to use.

PREVIOUS-YEAR QUESTIONS

11 questions from the CS papers

Try each question first, then open the answer. We placed each question under a topic ourselves; the exam paper does not label topics.

See these papers in full: GATE CSE 2026 Set 2 · GATE CSE 2025 Set 1 · GATE CSE 2025 Set 2 · GATE CSE 2024 Set 2 · GATE CSE 2022 · GATE CSE 2020 · GATE CSE 2018

GATE 2026 CS2 Q.60

GATE 2026 · CS2Q.60 · 2 marks · NAT

Consider the following ANSI-C program.

#include <stdio.h>

int main(){
	int *ptr, a, b, c;
	a=5; b=11; c=20;
	ptr=&a; *ptr=c; ptr=&c;
	a=*(&b); c=*ptr-a;
	printf("%d",c);
	return(0);
}

The output of this program is ____________. (answer in integer)

Note: Assume that the program compiles and runs successfully.

Answer and solution
See the question in the paper
GATE 2026 CS2 Q.60: answer and solution

GATE 2025 CS2 Q.19

GATE 2025 · CS2Q.19 · 1 mark · MCQ

Consider the following C program:

#include <stdio.h>
void stringcopy(char *, char *);
int main(){
    char a[30] = "@#Hello World!";
    stringcopy(a, a + 2);
    printf("%s\n", a);
    return 0;
}
void stringcopy(char *s, char *t) {
    while(*t)
        *s++ = *t++;
}

Which ONE of the following will be the output of the program?

  1. (A)

    @#Hello World!

  2. (B)

    Hello World!

  3. (C)

    ello World!

  4. (D)

    Hello World!d!

Answer and solution
See the question in the paper
GATE 2025 CS2 Q.19: answer and solution

GATE 2024 CS2 Q.33

GATE 2024 · CS2Q.33 · 1 mark · MSQ

Consider the following C function definition.

int fX(char *a){
    char *b = a;
    while(*b)
        b++;
    return b - a;}

Which of the following statements is/are TRUE?

  1. (A)

    The function call fX("abcd") will always return a value

  2. (B)

    Assuming a character array c is declared as char c[] = "abcd" in main(), the function call fX(c) will always return a value

  3. (C)

    The code of the function will not compile

  4. (D)

    Assuming a character pointer c is declared as char *c = "abcd" in main(), the function call fX(c) will always return a value

Answer and solution
See the question in the paper
GATE 2024 CS2 Q.33: answer and solution

GATE 2022 CS Q.21

GATE 2022 · CSQ.21 · 1 mark · MCQ

What is printed by the following ANSI C program?

#include<stdio.h>
int main(int argc, char *argv[])
{
    int x = 1, z[2] = {10, 11};
    int *p = NULL;
    p = &x;
    *p = 10;
    p = &z[1];
    *(&z[0] + 1) += 3;
    printf("%d, %d, %d\n", x, z[0], z[1]);
    return 0;
}
  1. (A)

    1, 10, 11

  2. (B)

    1, 10, 14

  3. (C)

    10, 14, 11

  4. (D)

    10, 10, 14

Answer and solution
See the question in the paper
GATE 2022 CS Q.21: answer and solution

GATE 2022 CS Q.43

GATE 2022 · CSQ.43 · 2 marks · MCQ

What is printed by the following ANSI C program?

#include<stdio.h>
int main(int argc, char *argv[])
{
        int a[3][3][3] =
        {{1, 2, 3, 4, 5, 6, 7, 8, 9},
         {10, 11, 12, 13, 14, 15, 16, 17, 18},
         {19, 20, 21, 22, 23, 24, 25, 26, 27}};
    int i = 0, j = 0, k = 0;
    for( i = 0; i < 3; i++ ){
        for(k = 0; k < 3; k++ )
            printf("%d ", a[i][j][k]);
        printf("\n");
    }
    return 0;
}
  1. (A)

    1 2 3
    10 11 12
    19 20 21

  2. (B)

    1 4 7
    10 13 16
    19 22 25

  3. (C)

    1 2 3
    4 5 6
    7 8 9

  4. (D)

    1 2 3
    13 14 15
    25 26 27

Answer and solution
See the question in the paper
GATE 2022 CS Q.43: answer and solution

GATE 2020 CS Q.48

GATE 2020 · CSQ.48 · 2 marks · NAT

Consider the following C functions.

int tob(int b, int* arr) {
    int i;
    for (i=0; b>0; i++) {
        if (b%2) arr[i]=1;
        else     arr[i]=0;
        b = b/2;
    }
    return (i);
}
int pp(int a, int b) {
    int arr[20];
    int i, tot = 1, ex, len;
    ex = a;
    len = tob(b,arr);
    for (i=0; i<len; i++) {
        if (arr[i]==1)
            tot = tot * ex;
        ex = ex * ex;
    }
    return (tot);
}

The value returned by pp(3,4) is ________.

Answer and solution
See the question in the paper
GATE 2020 CS Q.48: answer and solution

PRACTICE

7 questions to test yourself

PRACTICE 11 mark · NAT

An array is declared as int a[5][4]; with sizeof(int) = 4 bytes. It is stored in row-major order starting at base address 1000 (decimal, byte-addressable memory). What is the address, in decimal, of a[3][2]?

Answer and solution

Answer:

  1. Row-major address: Addr(a[i][j]) = Base + (i × C + j) × sizeof(int), where C is the number of columns.

  2. Here Base = 1000, C = 4, i = 3, j = 2 and sizeof(int) = 4.

  3. Element offset = i × C + j = 3 × 4 + 2 = 14.

  4. Address = 1000 + 14 × 4 = 1000 + 56 = 1056.

PRACTICE 21 mark · MCQ

Consider the function below (with <stdio.h> included).

void f(int arr[10]) {
    printf("%d", (int)(sizeof(arr) == sizeof(int *)));
}

What does the call f(arr) print, for any int arr[10] passed in from main?

  1. (A)

    0

  2. (B)

    1

  3. (C)

    10

  4. (D)

    Compile-time error

Answer and solution

Answer: (B)

  1. In a parameter list, the declaration int arr[10] is adjusted by the compiler to int *arr. The [10] is ignored.

  2. So inside f, arr is a pointer variable, and sizeof(arr) equals sizeof(int *) whatever length was written.

  3. The comparison sizeof(arr) == sizeof(int *) is true and evaluates to 1, and the cast to int leaves it as 1.

  4. The program prints 1. The code compiles; some compilers warn that sizeof is applied to an array parameter.

PRACTICE 32 marks · MSQ

A 2-D dynamic array of int with 3 rows and 4 columns is built as an array of row pointers. Assume every allocation succeeds and all object pointer types have the same size.

int **p = (int **)malloc(3 * sizeof(int *));
for (int i = 0; i < 3; i++)
    p[i] = (int *)malloc(4 * sizeof(int));

Which of the following statements are TRUE?

  1. (A)

    The 3 rows are not guaranteed to be next to each other in memory.

  2. (B)

    p[i][j] is equivalent to *(*(p + i) + j).

  3. (C)

    sizeof(p) equals 3 * sizeof(int *).

  4. (D)

    Calling free(p); alone releases all the memory allocated for the array.

Answer and solution

Answer: (A), (B)

  1. A: each p[i] is a separate malloc, so the three rows are separate blocks. Nothing guarantees they are next to each other in memory. TRUE.

  2. B: p[i][j] means *(*(p + i) + j). p + i is the address of the i-th row pointer, *(p + i) is that row's base address, and + j followed by * reaches the element. TRUE.

  3. C: p is a single variable of type int **, so sizeof(p) = sizeof(int **), one pointer. With all pointers the same size, that is not 3 × sizeof(int *). FALSE.

  4. D: free(p); releases only the table of 3 row pointers. The 3 row blocks are never freed and leak. Each p[i] must be freed before p. FALSE.

  5. The true statements are A and B.

PRACTICE 42 marks · NAT

Consider struct S { char c1; int i; char c2; };. int is 4 bytes and needs 4-byte alignment, char is 1 byte and needs 1-byte alignment, and the struct's size is padded to a multiple of its largest member alignment (4 bytes). What is sizeof(struct S) in bytes?

Answer and solution

Answer:

  1. c1 is at offset 0 and occupies 1 byte, so the next free offset is 1.

  2. i needs an offset that is a multiple of 4. The next such offset is 4, so 3 padding bytes are inserted. i occupies offsets 4 to 7.

  3. c2 goes at offset 8 (1 byte), so the members end at offset 9.

  4. The total is rounded up to a multiple of 4, giving 12 (3 trailing padding bytes).

  5. Size = 1 + 3 + 4 + 1 + 3 = 12 bytes.

PRACTICE 51 mark · MCQ

Consider:

char *s = "GATE";
char t[] = "GATE";

Which single statement, if executed next, causes undefined behaviour?

  1. (A)

    t[0] = 'g';

  2. (B)

    s[0] = 'g';

  3. (C)

    printf("%s", s);

  4. (D)

    printf("%s", t);

Answer and solution

Answer: (B)

  1. t is a character ARRAY initialised with a copy of the literal, in its own writable storage, so modifying t[0] is well defined.

  2. s is a pointer to the string literal itself. The literal is not copied and may be stored in read-only memory.

  3. Writing through s, as in s[0] = 'g';, attempts to modify a string literal. That is undefined behaviour: it may crash, appear to work, or do something else, depending on the platform.

  4. Printing either string with printf("%s", ...) only reads it, which is well defined.

  5. So the statement with undefined behaviour is s[0] = 'g';.

PRACTICE 62 marks · NAT

An array is declared as int a[3][4]; with sizeof(int) = 4 bytes, and it starts at byte address 2000. What is the value of the expression (char *)(&a + 1) - (char *)(a + 1)?

Answer and solution

Answer:

  1. &a has type int (*)[3][4], a pointer to the whole array of 3 × 4 × 4 = 48 bytes. So &a + 1 is 2000 + 48 = 2048, one past the end of the array.

  2. a decays to int (*)[4], a pointer to one row of 4 × 4 = 16 bytes. So a + 1 is 2000 + 16 = 2016.

  3. Both are cast to char *, so the subtraction counts bytes: 2048 − 2016 = 32.

  4. The base address cancels, so the answer does not depend on it.

PRACTICE 71 mark · MCQ

What does the following fragment print?

int a[] = {5, 10, 15};
int *p = a;
int x = *p++;
int y = ++*p;
printf("%d %d %d", x, y, *p);
  1. (A)

    5 11 11

  2. (B)

    6 11 11

  3. (C)

    5 10 11

  4. (D)

    5 11 15

Answer and solution

Answer: (A)

  1. *p++ is *(p++): x receives the value p pointed at, a[0] = 5, and p then moves on to a[1].

  2. ++*p is ++(*p): a[1] goes from 10 to 11, and y = 11. p does not move.

  3. *p is a[1], which is now 11.

  4. The fragment prints 5 11 11.

OFFICIAL GATE 2027 SYLLABUS WORDING

Copied from the GATE 2027 syllabus; see all 2027 syllabi.

GATE CS

Programming in C

Preparing in Kolkata? IAE Academy's course for GATE CSE.