Tuesday 13 September 2016

Interesting C Interview Questions and Answers

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

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

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

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

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

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

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

int main(void)
{
    int i = 0;

    atexit(func);

    for(;i<0xffffff _exit="" i="" pre="">
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

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

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

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 0="" i="" pre="" return="">

11. Returning address of local variable

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


#include

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

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.

What is I/O Scheduler for a Hard Disk on linux?

What is I/O Scheduler for a Hard Disk on linux?

The 2.6 LinuxKernel includes selectable I/O schedulers. They control the way the Kernel commits reads and writes to disks – the intention of providing different schedulers is to allow better optimisation for different classes of workload.

Why does kernel need IO scheduler?

ANS : Without an I/O scheduler, the kernel would basically just issue each request to disk in the order that it received them. This could result in massive HardDisk thrashing: if one process was reading from one part of the disk, and one writing to another, the heads would have to seek back and forth across the disk for every operation. The scheduler’s main goal is to optimise disk access times.

An I/O scheduler can use the following techniques to improve performance:

a)Request merging : The scheduler merges adjacent requests together to reduce disk seeking.
b)Elevator : The scheduler orders requests based on their physical location on the block device, and it basically tries to seek in one direction as much as possible.
c)Prioritisation : The scheduler has complete control over how it prioritises requests, and can do so in a number of ways

All I/O schedulers should also take into account resource starvation, to ensure requests eventually do get serviced!

How to view Current Disk scheduler ?

Assuming that we have a disk name /dev/sda, type :

# cat /sys/block/{DEVICE-NAME}/queue/scheduler
# cat /sys/block/sda/queue/scheduler

Sample output:

noop anticipatory deadline [cfq]

Here used scheduler is cfq.

How to set I/O Scheduler For A Hard Disk ?

To set a specific scheduler, simply type the command as follows:

# echo {SCHEDULER-NAME} > /sys/block/{DEVICE-NAME}/queue/scheduler
For example, set noop scheduler, enter:
# echo noop > /sys/block/hda/queue/scheduler

OR

Edit /boot/grub/grub.conf and enter in kernel line "elevator=noop" or any other scheduler available.

There are currently 4 available IO schedulers :

* No-op Scheduler
* Anticipatory IO Scheduler (AS)
* Deadline Scheduler
* Complete Fair Queueing Scheduler (CFQ)

A) No-op Scheduler : This scheduler only implements request merging.

B) Anticipatory IO Scheduler : The anticipatory scheduler is the default scheduler in older 2.6 kernels – if you've not specified one, this is the one that will be loaded. It implements request merging, a one-way elevator, read and write request batching, and attempts some anticipatory reads by holding off a bit after a read batch if it thinks a user is going to ask for more data. It tries to optimise for physical disks by avoiding head movements if possible – one downside to this is that it probably give highly erratic performance on database or storage systems.

C) Deadline Scheduler : The deadline scheduler implements request merging, a one-way elevator, and imposes a deadline on all operations to prevent resource starvation. Because writes return instantly within Linux, with the actual data being held in cache, the deadline scheduler will also prefer readers – as long as the deadline for a write request hasn't passed. The kernel docs suggest this is the preferred scheduler for database systems, especially if you have TCQ aware disks, or any system with high disk performance.

D) Complete Fair Queueing Scheduler (CFQ) : The complete fair queueing scheduler implements both request merging and the elevator, and attempts to give all users of a particular device the same number of IO requests over a particular time interval. This should make it more efficient for multiuser systems. It seems that Novel SLES sets cfq as the scheduler by default, as does the latest Ubuntu release. As of the 2.6.18 kernel, this is the default schedular in kernel.org releases. RHEL 6 uses default scheduler CFQ.

Changing Schedulers :

The most reliable way to change schedulers is to set the kernel option “elevator” at boot time. You can set it to one of “as”, “cfq”, “deadline” or “noop”, to set the appropriate scheduler. elevator=cfq

It seems under more recent 2.6 kernels (2.6.11, possibly earlier), you can change the scheduler at runtime by echoing the name of the scheduler into /sys/block/$devicename/queue/scheduler, where the device name is the basename of the block device, eg “sda” for /dev/sda.

doc : /usr/src/linux/Documentation/block/switching-sched.txt,

syslog on linux

Syslog :

Whenever syslogd, the syslog dæmon, receives a log message, it acts based on the message's type (or facility) and its priority. syslog's mapping of actions to facilities and priorities is specified in /etc/syslog.conf. Each line in this file specifies one or more facility/priority selectors followed by an action. A selector consists of a facility or facilities and a (single) priority.

In the following syslog.conf line, mail.notice is the selector and /var/log/mail is the action (i.e., “write messages to /var/log/mail”):

mail.notice /var/log/mail

facility.level_of_priority file_where_msg_will_be_saved

Within the selector, “mail” is the facility (message category) and “notice” is the level of priority.

Facilities :

Facilities are simply categories. Supported facilities in Linux are auth, authpriv, cron, dæmon, kern, lpr, mail, mark, news, syslog, user, UUCP and local0 through local7. Some of these are self-explanatory, but of special note are:

* auth: used for many security events.
* authpriv: used for access-control-related messages.
* dæmon: used by system processes and other dæmons.
* kern: used for kernel messages.
* mark: messages generated by syslogd itself that contain only a timestamp and the string “--MARK--”. To specify how many minutes should transpire between marks, invoke syslogd with the -m [minutes] flag.
* user: the default facility when none is specified by an application or in a selector.
* local7: boot messages.
* *: wildcard signifying “any facility”.
* none: wildcard signifying “no facility”

----

Priorities :

Unlike facilities, which have no relationship to each other, priorities are hierarchical. Possible priorities in Linux are (in increasing order of urgency): debug > info > notice > warning > err > crit > alert and > emerg. Note that the urgency of a given message is determined by the programmer who wrote it; facility and priority are set by the programs that generate messages, not by syslog.

If you specify a single priority in a selector (without modifiers), you're actually specifying that priority plus all higher priorities. Thus the selector mail.notice translates to “all mail-related messages having a priority of notice or higher”, i.e., having a priority of notice, warning, err, crit, alert or emerg.

This behaviour can be cancelled by prepending an = to the priority. The selector mail.=notice translates to “all mail-related messages having a priority of notice”. Priorities may also be negated: mail.!notice is equivalent to “all mail messages except those with priority of notice or higher”, and mail.!=notice corresponds to “all mail messages except those with the priority notice”.

If overall system performance becomes an important factor in regard to logging, you can tell syslogd **not** to sync the disk each time it writes to a log file. This is done by putting a minus sign (-) in front of the file name, like this:

lpr.info -/var/adm/printer.log

Sending the log messages to another machine is done by using an at-sign (@) in front of the machine name as the action. For example:

*.emerg @logserver

details abnout rsyslog : http://www.linuxhomenetworking.com/wiki/index.php/Quick_HOWTO_:_Ch05_:_Troubleshooting_Linux_with_syslog


Logrotate :

The Linux utility logrotate renames and reuses system error log files on a periodic basis so that they don't occupy excessive disk space.

The /etc/logrotate.conf File :
This is logrotate's general configuration file in which you can specify the frequency with which the files are reused.

* You can specify either a weekly or daily rotation parameter. In the case below the weekly option is commented out with a #, allowing for daily updates.
* The rotate parameter specifies the number of copies of log files logrotate will maintain. In the case below the 4 copy option is commented out with a #, while allowing 7 copies.
* The create parameter creates a new log file after each rotation

Sample conf file:

# rotate log files weekly
#weekly

# rotate log files daily
daily

# keep 4 weeks worth of backlogs
#rotate 4

# keep 7 days worth of backlogs
rotate 7

# create new (empty) log files after rotating old ones
create
-----

The /etc/logrotate.d Directory :

Most Linux applications that use syslog will put an additional configuration file in this directory to specify the names of the log files to be rotated. It is a good practice to verify that all new applications that you want to use the syslog log have configuration files in this directory. Here are some sample files that define the specific files to be rotated for each application.

Here is an example of a custom file located in this directory that rotates files with the .tgz extension which are located in the /data/backups directory. The parameters in this file will override the global defaults in the /etc/logrotate.conf file. In this case, the rotated files won't be compressed, they'll be held for 30 days only if they are not empty, and they will be given file permissions of 600 for user root.

/data/backups/*.tgz {

daily
rotate 30
nocompress
missingok
notifempty
create 0600 root root
}

Activating logrotate :

The above logrotate settings in the previous section will not take effect until you issue the following command:
#logrotate -f

If you want logrotate to reload only a specific configuration file, and not all of them, then issue the logrotate command with just that filename as the argument like this:

[root@me]# logrotate -f /etc/logrotate.d/syslog

To compress log file use "compress" in main conf file.


How to check the logrotate status?

To check the current logrotate status, e.g. which files are covered by logrotate, what are their last processed date etc.

You can check the /var/lib/logrotate/status file