Skip to main content

C Puzzles Part 1

· 19 min read
Jose Vargas

The C Puzzles by Gowri Kumar are fifty C language programs meant to be compiled, run and explained. It's a great resource for those who are learning C. You'll learn the most if you use this post to compare with your own answers.

I took the time to understand and answer the first 25 problems. I'm not a C programmer (just know the basics) but did my best to explain each program both from my knowledge and googling for answers. Don't believe these are the correct answers as they may in fact be wrong. Most of the C puzzles answers may be found on different forums and Q&A sites but were not gathered on a single place before. Hope it helps. Please leave feedback and let me know any errors you find using the comments at the end of the page.

  1. The expected output of the following C program is to print the elements in the array. But when actually run, it doesn't do so.
    #include <stdio.h>
    #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
    int array[] = {23,34,12,17,204,99,16};
    int main(){
    int d;
    for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
    printf("%d\n",array[d+1]);
    return 0;
    }

    Find out what's going wrong.

    A: The for loop is not being executed. TOTAL_ELEMENTS is of type size_t which is unsigned. d is of type signed int. When an operator has a signed and an unsigned argument, and the unsigned argument is of greater or equal size to the signed argument, then the signed argument is converted to unsigned. So -1 is converted to 2^32 - 1 which of course is larger than TOTAL_ELEMENTS - 2. The comparisson is false. Forcing conversion to signed int with
    for(d=-1;d <= (signed int)(TOTAL_ELEMENTS-2);d++)

    makes it work as expected.

  2. I thought the following program was a perfect C program. But on compiling, I found a silly mistake. Can you find it out (without compiling the program :-) ?
    #include<stdio.h>

    void OS_Solaris_print()
    {
    printf("Solaris - Sun Microsystems\n");
    }

    void OS_Windows_print()
    {
    printf("Windows - Microsoft\n");

    }
    void OS_HP-UX_print()
    {
    printf("HP-UX - Hewlett Packard\n");
    }

    int main()
    {
    int num;
    printf("Enter the number (1-3):\n");
    scanf("%d",&num);
    switch(num)
    {
    case 1:
    OS_Solaris_print();
    break;
    case 2:
    OS_Windows_print();
    break;
    case 3:
    OS_HP-UX_print();
    break;
    default:
    printf("Hmm! only 1-3 :-)\n");
    break;
    }

    return 0;
    }
    A: There's a dash in the function name void OS_HP-UX_print(). C does not accept dashes in function names. It must be deleted or changed by an underscore for the program to work.
  3. What's the expected output for the following program and why?
    enum {false,true};

    int main()
    {
    int i=1;
    do
    {
    printf("%d\n",i);
    i++;
    if(i < 15)
    continue;
    }while(false);
    return 0;
    }
    A: Output: 1 The continue statement passes control to the next iteration of the nearest enclosing do, for, or while statement in which it appears. Within a do or a while statement, the next iteration starts by reevaluating the expression of the do or while statement. In this case, the expression is false so there are no more iterations.
  4. The following program doesn't "seem" to print "hello-out". (Try executing it)
    #include <stdio.h>
    #include <unistd.h>
    int main()
    {
    while(1)
    {
    fprintf(stdout,"hello-out");
    fprintf(stderr,"hello-err");
    sleep(1);
    }
    return 0;
    }
    A: What is sent to stdout is kept in a buffer and is printed until a newline is sent. If the program ends without sending a newline to stdout nothing is printed. What is sent to stderr is printed immediatly. That's why just "hello-err" is printed. Change "hello-out" for "hello-out\n" to print it.
  5. Consider the following:
    #include <stdio.h>
    #define f(a,b) a##b
    #define g(a) #a
    #define h(a) g(a)

    int main()
    {
    printf("%s\n",h(f(1,2)));
    printf("%s\n",g(f(1,2)));
    return 0;
    }

    Just by looking at the program one "might" expect the output to be the same for both the printf statements. But on running the program you get it as: 12 f(1,2)

    A: The definitions at the beggining are macros, read by the C preprocesor. The transforming or "evaluating" of a macro into a sequence is known as expansion. The parameter of a function-like macro is expanded unless it is the operand # or ##. Because g's parameter is the operand #, the argument is not expanded but instead immediately stringified "f(1,2)". Because h's parameter is not the operand # nor ##, the argument f(1,2) is expanded (12), then substituted (g(12)). Then the code is rescanned and further expansion occurs ("12").

    Source: stackoverflow.com/questions/4364971/and-in-macros

  6. Consider the following:
    #include<stdio.h>
    int main()
    {
    int a=10;
    switch(a)
    {
    case '1':
    printf("ONE\n");
    break;
    case '2':
    printf("TWO\n");
    break;
    defa1ut:
    printf("NONE\n");
    }
    return 0;
    }

    If you expect the output of the above program to be NONE, I would request you to check it out!!

    A: The program doesn't print anything because the switch has no default case. It says "defau1t", not "default" so it is simply ignored. It is not a syntax error as it is treated as a normal C label.
  7. The following C program segfaults on IA-64, but works fine on IA-32.
    int main()
    {
    int* p;
    p = (int*)malloc(sizeof(int));
    *p = 10;
    return 0;
    }

    Why does it happen so?

    A: It happens because on IA-64 a pointer takes up 64 bits, more than an integer (32 bits). Without the header file stdlib.h the return type of malloc is assumed to be integer, not pointer (int is the default return type in C). The returned 64 bits are truncated to 32 and then the line p = (int*)malloc(sizeof(int)); casts it to a 64 bit pointer. This, of course, damages the pointer causing a segfault when trying to write. Add stdlib.h to tell C to expect a pointer returned from malloc and fix the problem.
  8. Here is a small piece of program(again just 14 lines of program) which counts the number of bits set in a number.
    Input   Output
    0 0(0000000)
    5 2(0000101)
    7 3(0000111)
    int CountBits (unsigned int x )
    {
    static unsigned int mask[] = { 0x55555555,
    0x33333333,
    0x0F0F0F0F,
    0x00FF00FF,
    0x0000FFFF
    };

    int i ;
    int shift ; /* Number of positions to shift to right*/
    for ( i =0, shift =1; i < 5; i ++, shift *= 2)
    x = (x & mask[i ])+ ( ( x >> shift) & mask[i]);
    return x;
    }

    Find out the logic used in the above program.

    A: This algorithm counts the number of ones in a number in binary. It does so by "dividing" the original number in pairs of bits and adding both bits of each pair. It keeps adding the results until there are no more groups to add. For example, for 11010101:
    | 1 + 1 | 0 + 1 | 0 + 1 | 0 + 1 |  
    | 1 0 + 0 1 | 0 1 + 0 1 |
    | 0 0 1 1 + 0 0 1 0 |
    | 0 0 0 0 0 1 0 1 |

    The code given doesn't look as intuitive because it uses masks to add the groups of bits, optimizing the number of operations. Take, for example, the first iteration. The first mask is 0x55555555 = 01010101...0101 The logical AND of this mask and x (x & mask[1]) makes zero all odd positions of x:

      1 1 0 1 0 1 0 1
    & 0 1 0 1 0 1 0 1
    = 0 1 0 1 0 1 0 1

    Shifting x to the right and then applying the mask to get ((x >> shift) & mask[1]) makes zero all even positions:

      1 1 1 0 1 0 1 0
    & 0 1 0 1 0 1 0 1
    = 0 1 0 0 0 0 0 0

    Now adding up to get (x & mask[1])+ ((x >> shift) & mask[1]):

      0 1 0 1 0 1 0 1
    + 0 1 0 0 0 0 0 0
    = 1 0 0 1 0 1 0 1

    This effectively adds up each pair of bits in x as it ends up adding each odd position to the next (even) position of the original number. The masks used on next iterations are then 00110011, 00001111... and so on.

  9. What do you think would be the output of the following program and why? (If you are about to say "f is 1.0", I would say check it out again)
    #include <stdio.h>

    int main()
    {
    float f=0.0f;
    int i;

    for(i=0;i<10;i++)
    f = f + 0.1f;

    if(f == 1.0f)
    printf("f is 1.0 \n"
    else
    printf("f is NOT 1.0\n");

    return 0;
    }
    A: Output: f is NOT 1.0 Floats can't represent value 0.1 exactly because it has an infinite binary expansion the same way 1/3 has an infinite decimal expansion. 0.1 in binary is 0.000110011001100110011001100... When truncated, the decimal value used is approximately 0.100000001490116119. So after the for loop f has a value near but not exactly 1.0. The number must be compared using a narrow interval close to 1.0. A function used to compare floats is:
    #define EPSILON 0.00001f
    inline int floatsEqual(float f1, float f2)
    {
    return fabs(f1 - f2) < EPSILON; // or fabsf
    }

    Source: stackoverflow.com/questions/9577179/c-floating-point-precision

  10. I thought the following C program was perfectly valid (after reading about the comma operator in C). But there is a mistake in the following program, can you identify it?
    #include <stdio.h>

    int main()
    {
    int a = 1,2;
    printf("a : %d\n",a);
    return 0;
    }
    A: The mistake is on the line int a = 1,2;. Comma in C may act as an operator or as a separator deppending on context. Comma acts as a separator when used with function calls and definitions, function like macros, variable declarations, enum declarations, and similar constructs. So here the comma is a separator, not an operator because the variable "a" is being declared. C expects another variable name after the comma: int a = 1, b = 2;. The line a = 1, 2; is also valid.

    Source: www.geeksforgeeks.org/comna-in-c-and-c/

  11. What would be the output of the following C program? (Is it a valid C program?)
    #include <stdio.h>
    int main()
    {
    int i=43;
    printf("%d\n",printf("%d",printf("%d",i)));
    return 0;
    }
    A: It is a valid C program. Its output is 4321. It's valid because printf's return type is integer. This allows nesting printfs that way. The innermost printf prints 43 and returns the number of printed characters: 2. This is printed by the second printf which returns 1, printed by the outermost printf.
  12. Consider the following:
    void duff(register char *to, register char *from, register int count)
    {
    register int n=(count+7)/8;
    switch(count%8){
    case 0: do{ *to++ = *from++;
    case 7: *to++ = *from++;
    case 6: *to++ = *from++;
    case 5: *to++ = *from++;
    case 4: *to++ = *from++;
    case 3: *to++ = *from++;
    case 2: *to++ = *from++;
    case 1: *to++ = *from++;
    }while( --n >0);
    }
    }

    Is the above valid C code? If so, what is it trying to acheive and why would anyone do something like the above?

    A: It is valid C code, known as the Duff's Device. It is a memory-to-memory copy speed optimized alogorithm that copies "count" bits beggining at adress "from" into adress "to". It's an example of loop unrolling or unwinding, which tries to reduce the frequency of branches and loop maintenance instructions at the expense of making the code longer. An unoptimized but simpler code would be:
    do { 
    *to++ = *from++;
    } while(--count > 0);

    See Duff's Device Wikipedia article for more information on why C allows to write a do-while loop inside a switch statement.

  13. Here is yet another implementation of CountBits. Verify whether it is correct (how do you that???). If so, find out the logic used.
    int CountBits(unsigned int x)
    {
    int count=0;
    while(x)
    {
    count++;
    x = x&(x-1);
    }
    return count;
    }
    A: The code is correct. It counts the numbers of bits set in x. The while loop iterates once for each 1 bit of the original x because the line x = x&(x-1); reduces the number of bits by one each time. When doing logical and of x and x-1 the rightmost 1 bit is always changed to 0, for example:
      10011010
    & 10011001
    = 10011000

    This is repeated until x is zero.

  14. Are the following two function prototypes same?
    int foobar(void);
    int foobar();

    The following programs should be of some help in finding the answer: (Compile and run both the programs and see what happens) Program 1:

    #include <stdio.h>
    void foobar1(void)
    {
    printf("In foobar1\n");
    }

    void foobar2()
    {
    printf("In foobar2\n");
    }

    int main()
    {
    char ch = 'a';
    foobar1();
    foobar2(33, ch);
    return 0;
    }

    Program 2:

    #include <stdio.h>
    void foobar1(void)
    {
    printf("In foobar1\n");
    }

    void foobar2()
    {
    printf("In foobar2\n");
    }

    int main()
    {
    char ch = 'a';
    foobar1(33, ch);
    foobar2();
    return 0;
    }
    A: The function prototypes are different. When using void the function cannot receive parameters. Passing a parameter results in a "too many arguments to function 'foobar1'" compiling error (program 2). When using int foobar() prototype parameters may be passed to the function, but will just be ignored. Then program 1 is valid.
  15. What's the output of the following program and why?
    #include <stdio.h>
    int main()
    {
    float a = 12.5;
    printf("%d\n", a);
    printf("%d\n", *(int *)&a);
    return 0;
    }
  16. A: Output: 0 1095237632 The first printf takes a float and prints it as integer. This causes print f to print "0". The second printf takes the address of a float, casts it to an integer pointer and then dereferences that as an integer. There are problems here as an integer pointer may not have the same size of a float pointer. The representations of integers and floats are also different. The behaviour may be differente depending on the compiler, but in most cases the second printf will print a big strange number.

    Source: stackoverflow.com/questions/6574134/unexpected-output-printing-a-float-cast-as-an-int

  17. The following is a small C program split across files. What do you expect the output to be, when both of them compiled together and run? File1.c
    int arr[80];

    File2.c

    extern int *arr;
    int main()
    {
    arr[1] = 100;
    return 0;
    }
    A: Pointer and arrays are not the same. Declaring with extern int *arr tells C to use the bytes at location "arr" as if they point to something. However an array was defined there with int arr[80]; and array data is stored there, not a pointer. The result is that the first bytes of the array ("garbage") are dereferenced as if they were a pointer producing a segfault.

    The solution is to make both the declaration and the definition the same with, for example extern int arr[] . This way C knows it is an array.

  18. Explain the output of the following C program (No, the output is not 20).
    #include<stdio.h>
    int main()
    {
    int a=1;
    switch(a)
    {
    int b=20;
    case 1: printf("b is %d\n",b);
    break;
    default:printf("b is %d\n",b);
    break;
    }
    return 0;
    }
    A: Depending on the compiler there could be a warning message ("unreachable code at beginning of switch statement") or no messages at all. The output is a random integer (garbage). The line int b=20; is never executed because the switch jumps to a case (or default if there are no cases). b is therefore not initialized. Move the line right above the switch statement to get 20 as output. There is no "b not declared error" because of the inner workings of the compiler. When it gets to int b it sets up a variable b before assigning a value to it. The line isn't completely ignored: b is declared but not initialized.

    Source: stackoverflow.com/questions/1683417/variable-declared-and-initialized-in-a-switch-statement

  19. What is the output of the following program? (Again, it is not 40, (if the size of integer is 4)).
    #define SIZE 10
    void size(int arr[SIZE])
    {
    printf("size of array is:%d\n",sizeof(arr));
    }

    int main()
    {
    int arr[SIZE];
    size(arr);
    return 0;
    }
    A: The output is "size of array is:4". arr is passed to the function by reference. A pointer, which takes up 4 bytes is sent to the function so in its context sizeof(arr) is 4. Moving the printf line to main() outputs the expected value of 40.
  20. The following is a simple c program, in which there is a function called Error to display errors. Can you see a potential problem with the way Error is defined?
    #include <stdlib.h>
    #include <stdio.h>
    void Error(char* s)
    {
    printf(s);
    return;
    }

    int main()
    {
    int *p;
    p = malloc(sizeof(int));
    if(p == NULL)
    {
    Error("Could not allocate the memory\n");
    Error("Quitting....\n");
    exit(1);
    }
    else
    {
    /*some stuff to use p*/
    }
    return 0;
    }
    A: When compiling a warning is generated because printf(s) expects a string literal as its first argument, not a char*. Changing this line to printf("%s", s); does the job as it passes a string literal as first argument. The following arguments may be strings if the correct modifier is used.
  21. What is the differnce between the following function calls to scanf? (Please notice the space carefully in the second call. Try removing it and observe the behaviour of the program)
    #include <stdio.h>
    int main()
    {
    char c;
    scanf("%c",&c);
    printf("%c\n",c);

    scanf(" %c",&c);
    printf("%c\n",c);

    return 0;
    }
    A: The first scanf scans just for a character. If there is no space before %c, when Enter is pressed the first scanf captures the character, but the newline character \n is kept and captured by the second scanf. This is normally undesired and is fixed by adding the space. Keep in mind that newlines, spaces and tabs are characters. The space makes the second scanf scan for one or more whitespace characters followed by any character. Because the newline is whitespace character, the space "eats" it and the next character is captured as expected.
  22. What is the potential problem with the following C program?
    #include <stdio.h>
    int main()
    {
    char str[80];
    printf("Enter the string:");
    scanf("%s",str);
    printf("You entered:%s\n",str);

    return 0;
    }
    A: The problem is that %s scans for any number of non-whitespace characters, stopping at the first whitespace character found. This means that just the first word will be captured. To capture a complete line use scanf("%[^\n]",str); which captureseverything until a newline character is entered.
  23. What is the output of the following program?
    #include <stdio.h>
    int main()
    {
    int i;
    i = 10;
    printf("i : %d\n",i);
    printf("sizeof(i++) is: %d\n",sizeof(i++));
    printf("i : %d\n",i);
    return 0;
    }
    A: Output:
    i : 10
    sizeof(i++) is: 4
    i : 10

    The expression in a sizeof is not evaluated because sizeof is not a function, but a compile-time operator. Only its operand's type is used to determine size. The sizeof(i++) expresion is then replaced by a constant, so there is no "++" at run-time.

    Source: stackoverflow.com/questions/1581839/whats-the-mechanism-of-sizeof-in-c-c

  24. Why does the following program give a warning? (Please remember that sending a normal pointer to a function requiring const pointer does not give any warning)
    #include <stdio.h>
    void foo(const char **p) { }
    int main(int argc, char **argv)
    {
    foo(argv);
    return 0;
    }
    A: A char** is being passed to a function that requires a const char**. A pointer to a constant is one through which one cannot change the value of variable it points. These pointers can change the address they point to but cannot change the value kept at those addresses. This is known as const-correctness. There is no cast from char** to const char** even when there is between char* and const char* because it would not be const-correct. If this assignment was allowed the following code would be valid:
    char const        c = 'a';
    char* p = 0;
    char const** pp = &p; // not allowed in C

    *pp = &c; // p now points to c.
    *p = 'b'; // changing a const value!

    Source: stackoverflow.com/questions/7016098/implicit-cast-from-char-to-const-char

  25. What is the output of the following program?
    #include <stdio.h>
    int main()
    {
    int i;
    i = 1,2,3;
    printf("i:%d\n",i);
    return 0;
    }
    A: Output: i:1 Here comma acts as a separator. It separates expressions and returns the value of the expression to its right. i = 1,2,3; is equivalent to (i=1),2,3;. The whole line evaluates to the last expression: 3, but it is not being stored. i = (1,2,3); stores 3 into i.

    Source: en.wikipedia.org/wiki/Comma_operator

  26. The following is a piece of code which implements the reverse Polish Calculator. There are serious bugs in the code. Find them!!! Assume that the function getop returns the appropriate return values for operands, opcodes, EOF etc...
    #include <stdio.h>
    #include <stdlib.h>

    #define MAX 80
    #define NUMBER '0'

    int getop(char[]);
    void push(double);
    double pop(void);
    int main()
    {
    int type;
    char s[MAX];

    while((type = getop(s)) != EOF)
    {
    switch(type)
    {
    case NUMBER:
    push(atof(s));
    break;
    case '+':
    push(pop() + pop());
    break;
    case '*':
    push(pop() * pop());
    break;
    case '-':
    push(pop() - pop());
    break;
    case '/':
    push(pop() / pop());
    break;
    /* ...
    * ...
    * ...
    */
    }
    }
    }
    A: Reverse Polish notation is a mathematical notation in which every operator follows all of its operands. Here the operands are stacked and accessed when an operator is processed. In case '-' and case '/' the expected output is the first operand minus/divided by the second one. Supposing it's a LIFO stack the order is incorrect. The second operand (last stacked) should be substracted from the first one:
    case '-':
    op2 = pop();
    push(pop() - op2);
    break;
    case '/':
    op2 = pop();
    if (op2 != 0.0)
    push(pop() / op2);
    else
    printf("error: zero divisor\n");
    break;

    This code also checks if the second operand is zero, to avoid trying to divide by zero.

The C puzzles questions, by CH Gowri Kumar are licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.