December Code

C arrays and strings interview questions, with answers

Arrays and strings are where C's low-level model shows through most often. An array is a fixed block of elements that turns into a pointer at almost every opportunity, and a string is nothing more than an array of char ending in a zero byte. Interview questions probe exactly those two facts: what an array name really is, what the terminator costs and protects, why two string declarations that look alike behave differently, and how to copy text without overrunning a buffer. Every answer below comes with code that was compiled and run.

The questions start with arrays and pointers, move through strings and the library functions that handle them, and end with two-dimensional arrays and a classic in-place exercise. Then take the free C diagnostic — ten questions across every C topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.How are arrays and pointers related in C?

    In short: In most expressions an array name converts to a pointer to its first element, so a[i] means *(a + i), but an array is not a pointer: it has its own size and cannot be assigned.

    An array is a contiguous block of elements, and the name of an array used in an expression decays to a pointer to element 0. That is why a[i] is defined as *(a + i), why the odd-looking 2[a] also compiles, and why an array can be passed wherever a pointer is expected. But the array itself is not a pointer object. Its size is fixed and known to the compiler, it cannot be assigned or incremented, and there are three places where it does not decay: as the operand of sizeof, as the operand of &, where &a is a pointer to the whole array, and as a string literal initialising a char array. Remembering that decay throws the size away prevents most array bugs in C.

    int a[4] = {4, 8, 15, 16};
    int *q = a;
    printf("%d %d %d\n", a[2],
        *(q + 2), 2[a]);
    // 15 15 15
  2. 2.What is a string in C, and why does it end with '\0'?

    In short: A C string is an array of char that ends with a zero byte; no length is stored, so every string function finds the end by scanning for that terminator.

    C has no string type. A string is a sequence of characters followed by the null character, '\0', and a string literal such as "dog" is an array of four chars: d, o, g and the terminator. Functions such as strlen, strcpy and printf's %s find where a string ends only by scanning for that byte, so strlen is O(n), and a buffer without a terminator makes them read past its end, which is undefined behaviour and a classic security bug. Every buffer therefore needs one byte more than the longest text it will hold. An embedded '\0' ends the string early as far as these functions are concerned, as the code shows: the array still holds work, but strlen and %s stop after net.

    char s[] = "net\0work";
    size_t n = strlen(s);
    printf("%zu %s\n", n, s);
    // 3 net
  3. 3.What is the difference between char s[] = "..." and char *s = "..." in C?

    In short: The array form makes a modifiable copy of the literal in s; the pointer form points at the literal itself, which is read-only, so writing through it is undefined behaviour.

    char s[] = "fox" declares an array sized to fit the literal and initialises it with a copy, so s owns its characters and s[0] = 'b' is fine; sizeof s is the length plus one. char *p = "fox" declares a pointer to the string literal, an array with static storage that the program must not modify. For historical reasons the literal's type in C is char[], not const char[], so the compiler accepts a write through a plain char *, but the behaviour is undefined, and on most systems the literal sits in read-only memory and the write crashes. The pointer can later be aimed at another string, and sizeof p is the pointer's size. Declaring it const char *, as below, turns the mistake into a compile error.

    char s[] = "fox";
    s[0] = 'b';
    printf("%s\n", s);
    // box
    const char *p = "fox";
    p[0] = 'c';
    // does not compile
  4. 4.Why are strcpy and strcat unsafe, and what should you use instead?

    In short: They write until the source's terminator with no idea how big the destination is, so a long source overflows the buffer; snprintf bounds the write and reports truncation.

    strcpy(dst, src) copies bytes up to and including src's terminator, and strcat appends the same way, so neither can stop when dst is full. A source longer than the buffer overwrites whatever lies after it, the textbook buffer overflow; gets, which read a line with no limit at all, was removed in C11 for the same reason. The safe way to build a string is snprintf(dst, size, ...), which never writes more than size bytes, always terminates the result when size is not zero, and returns the length it wanted, so truncation can be detected, as below. strncpy is a trap rather than a fix: it does not terminate the destination when the source is too long, and it pads with zeros when it is short.

    char buf[6];
    const char *src = "overflow";
    int n = snprintf(buf,
        sizeof buf, "%s", src);
    printf("%s %d\n", buf, n);
    // overf 8
  5. 5.How do you compare two strings in C, and why does == give the wrong answer?

    In short: Use strcmp, which compares characters and returns zero for equal strings; == compares the two addresses, which differ for separate arrays even when the text is the same.

    Applied to two strings, == compares pointers: each array decays to the address of its first element, so two separate arrays holding the same text are unequal, and two identical literals may or may not compare equal depending on whether the compiler merges them. strcmp(a, b) compares character by character and returns 0 when the strings match, a negative value when a sorts first, and a positive value otherwise, so the equality test is strcmp(a, b) == 0; writing if (strcmp(a, b)) is a common bug, since that is true when the strings differ. strncmp limits the comparison to n characters, and standard C has no case-insensitive version: POSIX has strcasecmp and Windows _stricmp.

    char a[] = "yes", b[] = "yes";
    printf("%d %d\n", a == b,
        strcmp(a, b) == 0);
    // 0 1
  6. 6.How is a two-dimensional array stored in C, and how do you pass one to a function?

    In short: Row by row in one contiguous block, so m[i][j] is element i × cols + j; a function parameter must state every dimension but the first, as in int m[][3].

    C stores int m[2][3] as two rows of three ints laid end to end, which is row-major order, so the element after m[0][2] in memory is m[1][0], and walking row by row is the cache-friendly order. To compute the address of m[i][j] the compiler needs the row length, so every dimension except the first must be part of the parameter type, as in void show(int m[][3], int rows). Since C99 a variable-length array parameter can take the width at run time: void show(int r, int c, int m[r][c]). An array of row pointers, int *rows[2], looks the same when indexed, but it is a different structure with separately allocated rows, and the two cannot be passed to the same parameter.

    void show(int m[][3], int r) {
        for (int i = 0; i < r; i++)
            printf("%d ", m[i][2]);
    }
    
    int main(void) {
        int m[2][3] = {
            {1, 2, 3}, {4, 5, 6}};
        show(m, 2);
        int *row = m[1];
        printf("%d\n", row[1]);
    }
    // 3 6 5
  7. 7.How do you reverse a string in place in C?

    In short: Walk two indexes toward each other, from the first character and from the last one before the terminator, swapping as you go; it takes O(n) time and no extra memory.

    The two-index swap is the standard answer: start i at 0 and j at strlen(s) - 1, swap s[i] and s[j], and move both inward while i < j. The terminator never moves, so the result is still a valid string. Three details show care. The string must be writable, so this works on a char array but not on a pointer to a string literal. An empty string needs a guard, because with a size_t index strlen(s) - 1 wraps round to a huge value; converting the length to int, as below, avoids that. And the loop condition is i < j, not i != j, so the indexes stop as soon as they meet or cross. The loop makes n/2 swaps and uses O(1) extra space.

    char s[] = "stack";
    int i = 0;
    int j = (int)strlen(s) - 1;
    while (i < j) {
        char t = s[i];
        s[i++] = s[j];
        s[j--] = t;
    }
    printf("%s\n", s);
    // kcats

How the diagnostic asks it

One question from the C bank, exactly as a sitting would show it. The bank has 3 on arrays & strings and 30 across C.

Arrays & Strings · easyCL-017

What does this C code print? (<string.h> is included.)

char s[] = "hello";
printf("%zu %zu\n", sizeof(s), strlen(s));
  1. 15 5
  2. 26 5correct
  3. 35 6
  4. 46 6

A string literal ends with a null character, '\0', so char s[] = "hello" creates an array of 6 chars: the five letters and the terminator. sizeof(s) is the size of the whole array, 6, while strlen counts the characters before the first '\0', 5. 5 5 forgets the terminator that sizeof includes. 5 6 swaps the two. 6 6 has strlen counting the terminator, but strlen stops at it. That extra byte is the classic off-by-one when allocating: a copy of s needs strlen(s) + 1 bytes.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 C questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated