12 Interesting C Interview Questions and Answers

by Himanshu Arora on August 17, 2012

In this article, we will discuss some interesting problems on C language that can help students to brush up their C programming skills and help them prepare their C fundamentals for interviews.

1. gets() function

Question: There is a hidden problem with the following code. Can you detect it?

#include<stdio.h>

int main(void)
{
    char buff[10];
    memset(buff,0,sizeof(buff));

    gets(buff);

    printf("\n The buffer entered is [%s]\n",buff);

    return 0;
}

Answer: The hidden problem with the code above is the use of the function gets(). This function accepts a string from stdin without checking the capacity of buffer in which it copies the value. This may well result in buffer overflow. The standard function fgets() is advisable to use in these cases.

2. strcpy() function

Question: Following is the code for very basic password protection. Can you break it without knowing the password?

#include<stdio.h>

int main(int argc, char *argv[])
{
    int flag = 0;
    char passwd[10];

    memset(passwd,0,sizeof(passwd));

    strcpy(passwd, argv[1]);

    if(0 == strcmp("LinuxGeek", passwd))
    {
        flag = 1;
    }

    if(flag)
    {
        printf("\n Password cracked \n");
    }
    else
    {
        printf("\n Incorrect passwd \n");

    }
    return 0;
}

Answer: Yes. The authentication logic in above password protector code can be compromised by exploiting the loophole of strcpy() function. This function copies the password supplied by user to the ‘passwd’ buffer without checking whether the length of password supplied can be accommodated by the ‘passwd’ buffer or not. So if a user supplies a random password of such a length that causes buffer overflow and overwrites the memory location containing the default value ’0′ of the ‘flag’ variable then even if the password matching condition fails, the check of flag being non-zero becomes true and hence the password protection is breached.

For example :

$ ./psswd aaaaaaaaaaaaa

 Password cracked

So you can see that though the password supplied in the above example is not correct but still it breached the password security through buffer overflow.

To avoid these kind of problems the function strncpy() should be used.

Note from author : These days the compilers internally detect the possibility of stack smashing and so they store variables on stack in such a way that stack smashing becomes very difficult. In my case also, the gcc does this by default so I had to use the the compile option ‘-fno-stack-protector’ to reproduce the above scenario.

3. Return type of main()

Question: Will the following code compile? If yes, then is there any other problem with this code?

#include<stdio.h>

void main(void)
{
    char *ptr = (char*)malloc(10);

    if(NULL == ptr)
    {
        printf("\n Malloc failed \n");
        return;
    }
    else
    {
        // Do some processing

        free(ptr);
    }

    return;
}

Answer: The code will compile error free but with a warning (by most compilers) regarding the return type of main()function. Return type of main() should be ‘int’ rather than ‘void’. This is because the ‘int’ return type lets the program to return a status value. This becomes important especially when the program is being run as a part of a script which relies on the success of the program execution.

4. Memory Leak

Question: Will the following code result in memory leak?

#include<stdio.h>

void main(void)
{
    char *ptr = (char*)malloc(10);

    if(NULL == ptr)
    {
        printf("\n Malloc failed \n");
        return;
    }
    else
    {
        // Do some processing
    }

    return;
}

Answer: Well, Though the above code is not freeing up the memory allocated to ‘ptr’ but still this would not cause a memory leak as after the processing is done the program exits. Since the program terminates so all the memory allocated by the program is automatically freed as part of cleanup. But if the above code was all inside a while loop then this would have caused serious memory leaks.

Note : If you want to know more on memory leaks and the tool that can detect memory leaks, read our article on Valgrind.

5. The free() function

Question: The following program seg-faults (crashes) when user supplies input as ‘freeze’ while it works fine with input ‘zebra’. Why?

#include<stdio.h>

int main(int argc, char *argv[])
{
    char *ptr = (char*)malloc(10);

    if(NULL == ptr)
    {
        printf("\n Malloc failed \n");
        return -1;
    }
    else if(argc == 1)
    {
        printf("\n Usage  \n");
    }
    else
    {
        memset(ptr, 0, 10);

        strncpy(ptr, argv[1], 9);

        while(*ptr != 'z')
        {
            if(*ptr == '')
                break;
            else
                ptr++;
        }

        if(*ptr == 'z')
        {
            printf("\n String contains 'z'\n");
            // Do some more processing
        }

       free(ptr);
    }

    return 0;
}

Answer: The problem here is that the code changes the address in ‘ptr’ (by incrementing the ‘ptr’) inside the while loop. Now when ‘zebra’ is supplied as input, the while loop terminates before executing even once and so the argument passed to free() is the same address as given by malloc(). But in case of ‘freeze’ the address held by ptr is updated inside the while loop and hence incorrect address is passed to free() which causes the seg-fault or crash.

6. atexit with _exit

Question: In the code below, the atexit() function is not being called. Can you tell why?

#include<stdio.h>

void func(void)
{
    printf("\n Cleanup function called \n");
    return;
}

int main(void)
{
    int i = 0;

    atexit(func);

    for(;i<0xffffff;i++);

    _exit(0);
}

Answer: This behavior is due to the use of function _exit(). This function does not call the clean-up functions like atexit() etc. If atexit() is required to be called then exit() or ‘return’ should be used.

7. void* and C structures

Question: Can you design a function that can accept any type of argument and returns an integer? Also, is there a way in which more than one arguments can be passed to it?

Answer: A function that can accept any type of argument looks like :

 int func(void *ptr)

if more than one argument needs to be passed to this function then this function could be called with a structure object where-in the structure members can be populated with the arguments that need to be passed.

8. * and ++ operators

Question: What would be the output of the following code and why?

#include<stdio.h>

int main(void)
{
    char *ptr = "Linux";
    printf("\n [%c] \n",*ptr++);
    printf("\n [%c] \n",*ptr);

    return 0;
}

Answer: The output of the above would be :

[L] 

[i]

Since the priority of both ‘++’ and ‘*’ are same so processing of ‘*ptr++’ takes place from right to left. Going by this logic, ptr++ is evaluated first and then *ptr. So both these operations result in ‘L’. Now since a post fix ‘++’ was applied on ptr so the next printf() would print ‘i’.

9. Making changes in Code(or read-only) segment

Question: The following code seg-faults (crashes). Can you tell the reason why?

#include<stdio.h>

int main(void)
{
    char *ptr = "Linux";
    *ptr = 'T';

    printf("\n [%s] \n", ptr);

    return 0;
}

Answer: This is because, through *ptr = ‘T’, the code is trying to change the first byte of the string ‘Linux’ kept in the code (or the read-only) segment in the memory. This operation is invalid and hence causes a seg-fault or a crash.

10. Process that changes its own name

Question: Can you write a program that changes its own name when run?

Answer: Following piece of code tries to do the required :

#include<stdio.h>

int main(int argc, char *argv[])
{
    int i = 0;
    char buff[100];

    memset(buff,0,sizeof(buff));

    strncpy(buff, argv[0], sizeof(buff));
    memset(argv[0],0,strlen(buff));

    strncpy(argv[0], "NewName", 7);

    // Simulate a wait. Check the process
    // name at this point.
    for(;i<0xffffffff;i++);

    return 0;
}

11. Returning address of local variable

Question: Is there any problem with the following code?If yes, then how it can be rectified?

#include<stdio.h>

int* inc(int val)
{
  int a = val;
  a++;
  return &a;
}

int main(void)
{
    int a = 10;

    int *val = inc(a);

    printf("\n Incremented value is equal to [%d] \n", *val);

    return 0;
}

Answer: Though the above program may run perfectly fine at times but there is a serious loophole in the function ‘inc()’. This function returns the address of a local variable. Since the life time of this local variable is that of the function ‘inc()’ so after inc() is done with its processing, using the address of its local variable can cause undesired results. This can be avoided by passing the address of variable ‘a’ from main() and then inside changes can be made to the value kept at this address.

12. Processing printf() arguments

Question: What would be the output of the following code?

#include<stdio.h>

int main(void)
{
    int a = 10, b = 20, c = 30;

    printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2));

    return 0;
}

Answer: The output of the above code would be :

110..40..60

This is because the arguments to the function are processed from right to left but are printed from left to right.


Linux Sysadmin Course Linux provides several powerful administrative tools and utilities which will help you to manage your systems effectively. If you don’t know what these tools are and how to use them, you could be spending lot of time trying to perform even the basic administrative tasks. The focus of this course is to help you understand system administration tools, which will help you to become an effective Linux system administrator.
Get the Linux Sysadmin Course Now!

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

{ 20 comments… read them below or add one }

1 Jack Wilborn August 17, 2012 at 10:06 am

Hi, like your articles, but as a ‘C’ instructor for many years I think I should point out that C is machine independent, which is misleading because C does not specify how many variables are stored or how they are operated on, since some machines may do it much more efficiently another way. One, such as the example of over writing of a variable is often the most spoken about way to hang yourself. If you know your machine and compiler you can re-order your variables to make the order such that this couldn’t happen. Better still, make the if one that has an else and set the flag there, this would guarantee that it operates properly. Also your example of the pointer post incrementing executes exactly how you would expect it and your explanation was difficult and not needed.

You also have multiple entries of “include “, is this what you intended or is it something in my browser or just a typo?

I tell most students that try to increment anything in various areas of if or while statements that the best way is the K&R intended was to use the proper for or do while that has the option of incrementing the variable at the proper place to handle all situations.

I have to admit you usually have a lot of good suggestions, but this time it seems you have relied on sloppy coding practices and not the standard goofs people make in the C language. Believe me, as I’m sure you do, that C has plenty of rope to hang yourself, but also lets you do things you cannot do in other languages.

A good point that many outside of programmers don’t realize that is also impacting the malware group is that of a Data Segment to prevent executing of code that is actually data. The IBM 370 has such a feature and is wisely used in their code generation of C, where as the PC’s are rather sloppy and inconsistent along with absolutely wrong in versions that I have exposed during my tenure in teaching and in actual programming.

Please keep up the good work, and I’m sure you can find better examples to show people that this time. Also, if you just help people with a good coding practice basic rules to help them avoid these pitfalls.

Jack k. Wilborn

Please write me if you feel like it.

2 JohnP August 17, 2012 at 10:30 am

Professional code doesn’t look like these samples. These are definitely for a test at the end of a first Programming in C class. Pros use other techniques to avoid most of these constructs completely.

As an example, I’ve never used memset() is any program the last 15 yrs programming and leaving variables uninitialized is a noob mistake, IMHO. Sure, lots of people do this because they incorrectly believe it is a waste of effort. The goal is to have your program break ASAP in the development cycle, not ever in front of a customer.

3 Himanshu Arora August 17, 2012 at 12:56 pm

For all those who believe that the examples are sloppy or not professionaly used..please read the first paragraph..this article is not for experienced professionals..this article is aimed to explain basic stuff to newbies with easy to understand examples…

4 jalal hajigholamali August 18, 2012 at 3:33 am

hi,
thanks a lot
i sent this article to my “C” course students

5 Ethan August 18, 2012 at 5:18 am

I’ve never made my mind to touch the devel C language. As a medical student, I prefer the pearl and python in Linux environment.

6 Yuvaraj A August 21, 2012 at 1:25 pm

1. Processing printf() arguments :

In C language the Order in which the function arguments are evaluated is not specified . That is implementation defined . But c guarantees that the arguments should get evaluated before the function is called. There are many calling conventions .

To know more about calling convention the below link can be referred:
http://en.wikipedia.org/wiki/X86_calling_conventions

Gcc follows cdecl calling convention in which arguments are evaluated from right to left .

2.Making changes in Code(or read-only) segment:

The example program given for this is trying to modify a constant string literal.

According to the c standard the behavior of modifying constant string literal is undefined .
It means that the program whether produce segfault or not depends on the environment where it is running.

For example if runs in Linux environment it will produce segfault since it is overwriting
Read only segment . If the same code runs in embedded environment where there is no concepts of segments then that statement simply overwrites that memory and the program runs fine.

7 Alistra August 29, 2012 at 4:31 am

alistra@bialobrewy ~ % cat 1.c
#include

int main(void)
{
int a = 10, b = 20, c = 30;

printf(“\n %d..%d..%d \n”, a+b+c, (b = b*2), (c = c*2));

return 0;
}
alistra@bialobrewy ~ % clang 1.c
alistra@bialobrewy ~ % ./a.out

60..40..60

8 Aleksei Kozadaev August 29, 2012 at 10:27 am

I believe the answer 12 is not exactly correct. (Yuvaraj A +1)
It would be platform/compiler dependant. I would prefer thinking of the function argument evaluation order as undefined and avoid the code like the one in the question.

9 Charles Banas August 29, 2012 at 7:41 pm

12 is wrong.

Aleksei and Alistra correctly point this out.

The behavior is entirely compiler-specific, and the C standard makes no guarantee as to the expected behavior. It’s specifically called out as undefined behavior. Any compiler is free to handle the arguments in any order, and could easily print out any of: “60..40..60″, “80..40..60″, “90..40..60″, or “110..40..60″, and some will.

GCC happens to do right-to-left evaluation on most platforms. Clang does left-to-right evaluation.

10 Zishan Shaikh August 31, 2012 at 12:34 am

I guess the question 2 about “password crack” can also be corrected by declaring the variables as follows:
char passwd[10];
int flag = 0;

instead of
int flag = 0;
char passwd[10];

By declaring “flag” after “passwd”, flag will be allocated memory before passwd in the stack. So even if you overflow the buffer with incorrect, lenghty passwd, the code will work just fine displaying “incorrect password”. This way there will be no memory over write or anything, IMHO.

I just confirmed this by printing their addresses, by changing declaration order as mentioned. In the original way mentioned, flag gets allocated after passwd (genuine stack behavior i guess).

11 Mohammed Abdelkhaliq September 2, 2012 at 1:56 pm

regarding Q3, It would be more accurate if we should it’s the default return type of any [undeclared function] is int.

and it’s not special for main function

12 Peter September 3, 2012 at 3:14 am

In the 5th question,

if(*ptr == ”)

should be

if(*ptr == ”) // backslash and zero

Most likely, your blogging software removed the terminating null (backslash and zero) for security reasons.

13 Aleksei Kozadaev September 3, 2012 at 5:00 am

JohnP: I couldn’t agree more.
They could have used better C facilities to initialize memory/arrays.
Something like
char passwd[10] = { 0 };
or
char *passwd = calloc(10, sizeof(*passwd));

IMHO: Casting malloc (like (char*)malloc…) is a bad idea because it would hide compile-time warnings (if any) while not gaining anything.
like: char *ptr = (char*)malloc(10); –> char *ptr = malloc(10 * sizeof(*ptr));

14 Ganesh Shinde December 26, 2012 at 7:11 am

About Question#10: Process that changes its own name
Are you sure this will change the process name or just a command line argument copy of process name?
Very tricky!!
I am practicing C from many years but you made me also think a bit on your solution.

Process name can be changed but not this way.

I am surprise how could nobody challenges this.

15 Pramod Yadav January 5, 2013 at 1:52 am

please tell me, if you know, how we can work with alt, ctrl and window key, i am seeking the answer of this question from so long, if someone know then please tell me…

16 Mike Yue February 21, 2013 at 1:24 am

To Ganesh about Question#10:

Following code can change process’s name(up to 16 bytes,doesn’t matter lenght the original executable).

#include
#include

int main(int argc, char *argv[])
{
int i = 0;

memset(argv[0],0,strlen(argv[0]));

if (prctl(PR_SET_NAME, “NewName”)) {
printf(“unable to change process name”);
return 1;
}

// Simulate a wait. Check the process name at this point.
for(;i<1000;i++)sleep(1);

return 0;
}

The original post solution have some limits.
1. The new name should be shorter.
2. with 'ps' you won't notice the process's name changed. Only "ps -f" you can.
3. you might not see the process in another terminal(maybe my x86 Lubuntu box's problem).

17 Robert Sun March 12, 2013 at 11:09 am

Ex. #9 is actually perfectly okay. The string “Tinux” was printed successfully. No crash at all.

18 Ken Robb April 23, 2013 at 3:37 pm

Another solution to #2 is to look at the text strings in the executable, since the password is stored in cleartext. I know that this isn’t a tutorial on encryption, but just wanted to point out that sometimes there is more than one solution.

You could use the “strings” command as follows:

$ strings pw-hack
/lib64/ld-linux-x86-64.so.2
__gmon_start__
libc.so.6
strcpy
puts
__libc_start_main
GLIBC_2.2.5
l$ L
t$(L
|$0H
LinuxGeek
Password cracked
Incorrect passwd

19 vamshi August 7, 2013 at 12:40 am

11th question looks no problem.
when we call a function ( jump to a new function) , stack is used to store function variables before processing new function.
and inc() , in this case it will first return the address of a to main function it exist from the function.

20 Nachiket P February 11, 2014 at 9:54 pm

The output of 8 is correct. But it is not because ‘++’ and ‘*’ have same precedence. The post-fix operator has higher precedence than indirection. But by definition of postfix, the incrementing of ptr happens as a post-side effect and will take place just before the next print statement. Hence for the first print statement, value printed is still *ptr which is [L].

Leave a Comment

Previous post:

Next post: