C Language interview questions

C Language quiz questions

  • 1.

    How can you pass an array to a function by value?

    Answer:

    An array can be passed to a function by value by declaring in the called function the array name with square brackets ([ and ]) attached to the end. When calling the function, simply pass the address of the array (that is, the array's name) to the called function. For instance, the following program passes the array x[] to the function named byval_func() by value:

    #include <stdio.h>
    void byval_func(int[]);       /* the byval_func() function is passed an
                                     integer array by value */
    void main(void);
    void main(void)
    {
         int x[10];
         int y;
         /* Set up the integer array. */
         for (y=0; y<10; y++)
              x[y] = y;
         /* Call byval_func(), passing the x array by value. */
         byval_func(x);
    }
    /* The byval_function receives an integer array by value. */
    void byval_func(int i[])
    {
         int y;
         /* Print the contents of the integer array. */
         for (y=0; y<10; y++)
              printf("%d\n", i[y]);
    }
    

    In this example program, an integer array named x is defined and initialized with 10 values. The functionbyval_func() is declared as follows:

    int byval_func(int[]);

    The int[] parameter tells the compiler that the byval_func() function will take one argument—an array of integers. When the byval_func() function is called, you pass the address of the array to byval_func():byval_func(x);

    Because the array is being passed by value, an exact copy of the array is made and placed on the stack. The called function then receives this copy of the array and can print it. Because the array passed to byval_func() is a copy of the original array, modifying the array within the byval_func() function has no effect on the original array.

    Passing arrays of any kind to functions can be very costly in several ways. First, this approach is very inefficient because an entire copy of the array must be made and placed on the stack. This takes up valuable program time, and your program execution time is degraded. Second, because a copy of the array is made, more memory (stack) space is required. Third, copying the array requires more code generated by the compiler, so your program is larger.

    Instead of passing arrays to functions by value, you should consider passing arrays to functions by reference: this means including a pointer to the original array. When you use this method, no copy of the array is made. Your programs are therefore smaller and more efficient, and they take up less stack space. To pass an array by reference, you simply declare in the called function prototype a pointer to the data type you are holding in the array.

    Consider the following program, which passes the same array (x) to a function:

    #include <stdio.h>
    void const_func(const int*);
    void main(void);
    void main(void)
    {
         int x[10];
         int y;
         /* Set up the integer array. */
         for (y=0; y<10; y++)
              x[y] = y;
         /* Call const_func(), passing the x array by reference. */
         const_func(x);
    }
    /* The const_function receives an integer array by reference.
       Notice that the pointer is declared as const, which renders
       it unmodifiable by the const_func() function. */
    void const_func(const int* i)
    {
         int y;
         /* Print the contents of the integer array. */
         for (y=0; y<10; y++)
              printf("%d\n", *(i+y));
    }
    

    In the preceding example program, an integer array named x is defined and initialized with 10 values. Thefunction const_func() is declared as follows:

    int const_func(const int*);

    The const int* parameter tells the compiler that the const_func() function will take one argument—a constant pointer to an integer. When the const_func() function is called, you pass the address of the array to const_func():

    const_func(x);

    Because the array is being passed by reference, no copy of the array is made and placed on the stack. The called function receives simply a constant pointer to an integer. The called function must be coded to be smart enough to know that what it is really receiving is a constant pointer to an array of integers. The const modifier is used to prevent the const_func() from accidentally modifying any elements of the original array.

    The only possible drawback to this alternative method of passing arrays is that the called function must be coded correctly to access the array—it is not readily apparent by the const_func() function prototype or definition that it is being passed a reference to an array of integers. You will find, however, that this method is much quicker and more efficient, and it is recommended when speed is of utmost importance.

    View
  • 2.

    Should a function contain a return statement if it does not return a value?

    Answer:

    In C, void functions (those that do not return a value to the calling function) are not required to include a return statement. Therefore, it is not necessary to include a return statement in your functions declared as being void.

    In some cases, your function might trigger some critical error, and an immediate exit from the function might be necessary. In this case, it is perfectly acceptable to use a return statement to bypass the rest of the function's code. However, keep in mind that it is not considered good programming practice to litter your functions with return statements-generally, you should keep your function's exit point as focused and clean as possible.

    View
  • 3.

    What is a static function?

    Answer:

    static function is a function whose scope is limited to the current source file. Scope refers to the visibility of a function or variable. If the function or variable is visible outside of the current source file, it is said to have global, or external, scope. If the function or variable is not visible outside of the current source file, it is said to have local, or static, scope.

    static function therefore can be seen and used only by other functions within the current source file. When you have a function that you know will not be used outside of the current source file or if you have a function that you do not want being used outside of the current source file, you should declare it as static. Declaring local functions as static is considered good programming practice. You should use static functions often to avoid possible conflicts with external functions that might have the same name.

    For instance, consider the following example program, which contains two functions. The first function,open_customer_table(), is a global function that can be called by any module. The second function,open_customer_indexes(), is a local function that will never be called by another module. This is because you can't have the customer's index files open without first having the customer table open. Here is the code:

    #include <stdio.h>
    int open_customer_table(void);       /* global function, callable from
                                            any module */
    static int open_customer_indexes(void); /* local function, used only in
                                               this module */
    int open_customer_table(void)
    {
         int ret_code;
         /* open the customer table */
         ...
         if (ret_code == OK)
         {
              ret_code = open_customer_indexes();
         }
         return ret_code;
    }
    static int open_customer_indexes(void)
    {
         int ret_code;
         /* open the index files used for this table */
         ...
         return ret_code;
    }
    

    Generally, if the function you are writing will not be used outside of the current source file, you should declare it as static.

    View
  • 4.

    How many parameters should a function have?

    Answer:

    There is no set number or "guideline" limit to the number of parameters your functions can have. However, it is considered bad programming style for your functions to contain an inordinately high (eight or more) number of parameters. The number of parameters a function has also directly affects the speed at which it is called—the more parameters, the slower the function call. Therefore, if possible, you should minimize the number of parameters you use in a function. If you are using more than four parameters, you might want to rethink your function design and calling conventions.

    One technique that can be helpful if you find yourself with a large number of function parameters is to put your function parameters in a structure. Consider the following program, which contains a function namedprint_report() that uses 10 parameters. Instead of making an enormous function declaration and proto- type, the print_report() function uses a structure to get its parameters:

    #include <stdio.h>
    typedef struct
    {
         int       orientation;
         char      rpt_name[25];
         char      rpt_path[40];
         int       destination;
         char      output_file[25];
         int       starting_page;
         int       ending_page;
         char      db_name[25];
         char      db_path[40];
         int       draft_quality;
    } RPT_PARMS;
    void main(void);
    int print_report(RPT_PARMS*);
    void main(void)
    {
         RPT_PARMS rpt_parm; /* define the report parameter
                                structure variable */
         ...
         /* set up the report parameter structure variable to pass to the
           print_report() function */
           rpt_parm.orientation = ORIENT_LANDSCAPE;
         rpt_parm.rpt_name = "QSALES.RPT";
         rpt_parm.rpt_path = "C:\REPORTS";
         rpt_parm.destination = DEST_FILE;
         rpt_parm.output_file = "QSALES.TXT";
         rpt_parm.starting_page = 1;
         rpt_parm.ending_page = RPT_END;
         rpt_parm.db_name = "SALES.DB";
         rpt_parm.db_path = "C:\DATA";
         rpt_parm.draft_quality = TRUE;
         /* Call the print_report() function, passing it a pointer to the
         parameters instead of passing it a long list of 10 separate
            parameters. */
         ret_code = print_report(&rpt_parm);
         ...
    }
    int print_report(RPT_PARMS* p)
    {
         int rc;
         ...
         /* access the report parameters passed to the print_report()
            function */
         orient_printer(p->orientation);
         set_printer_quality((p->draft_quality == TRUE) ? DRAFT : NORMAL);
         ...
         return rc;
    }
    

    The preceding example avoided a large, messy function prototype and definition by setting up a predefined structure of type RPT_PARMS to hold the 10 parameters that were needed by the print_report()function.  The only possible disadvantage to this approach is that by removing the parameters from the function definition, you are bypassing the compiler's capability to type-check each of the parameters for validity during the compile stage.

    Generally, you should keep your functions small and focused, with as few parameters as possible to help with execution speed. If you find yourself writing lengthy functions with many parameters, maybe you should rethink your function design or consider using the structure-passing technique presented here. Additionally, keeping your functions small and focused will help when you are trying to isolate and fix bugs in your programs.

    View
  • 5.

    Why should I prototype a function?

    Answer:

    A function prototype tells the compiler what kind of arguments a function is looking to receive and what kind of return value a function is going to give back. This approach helps the compiler ensure that calls to a function are made correctly and that no erroneous type conversions are taking place. For instance, consider the following prototype:

    int some_func(int, char*, long);

    Looking at this prototype, the compiler can check all references (including the definition of some_func()) to ensure that three parameters are used (an integer, a character pointer, and then a long integer) and that a return value of type integer is received. If the compiler finds differences between the prototype and calls to the function or the definition of the function, an error or a warning can be generated to avoid errors in your source code. For instance, the following examples would be flagged as incorrect, given the preceding

    prototype of some_func():

    x = some_func(1);                    /* not enough arguments passed */
    x = some_func("HELLO!", 1, "DUDE!"); /* wrong type of arguments used */
    x = some_func(1, str, 2879, "T");    /* too many arguments passed */
    
    /* In the following example, the return value expected 
       from some_func() is not an integer: */
    
    long* lValue;
    lValue = some_func(1, str, 2879);    /* some_func() returns an int,
                                            not a long* */
    

    Using prototypes, the compiler can also ensure that the function definition, or body, is correct and correlates with the prototype. For instance, the following definition of some_func() is not the same as its prototype, and it therefore would be flagged by the compiler:

    int some_func(char* string, long lValue, int iValue)  /* wrong order of
                                                             parameters */
    {
        ...
    }
    

    The bottom line on prototypes is that you should always include them in your source code because they provide a good error-checking mechanism to ensure that your functions are being used correctly. Besides, many of today's popular compilers give you warnings when compiling if they can't find a prototype for a function that is being referenced.

    View
  • 6.

    When should I declare a function?

    Answer:

    Functions that are used only in the current source file should be declared as static, and the function's declaration should appear in the current source file along with the definition of the function. Functions used outside of the current source file should have their declarations put in a header file, which can be included in whatever source file is going to use that function. For instance, if a function named stat_func() is used only in the source file stat.c, it should be declared as shown here:

    /* stat.c */
    #include <stdio.h>
    static int stat_func(int, int);  /* static declaration of stat_func() */
    void main(void);
    void main(void)
    {
         ...
         rc = stat_func(1, 2);
         ...
    }
    /* definition (body) of stat_func() */
    static int stat_func(int arg1, int arg2)
    {
         ...
         return rc;
    }
    

    In this example, the function named stat_func() is never used outside of the source file stat.c. There is therefore no reason for the prototype (or declaration) of the function to be visible outside of the stat.csource  file. Thus, to avoid any confusion with other functions that might have the same name, the declaration of stat_func() should be put in the same source file as the declaration of stat_func().

    In the following example, the function glob_func() is declared and used in the source file global.c and is used in the source file extern.c. Because glob_func() is used outside of the source file in which it's declared, the declaration of glob_func() should be put in a header file (in this example, named proto.h) to be included in both the global.c and the extern.c source files. This is how it's done:

    /* proto.h */
    int glob_func(int, int);  /* declaration of the glob_func() function */
    

     

    /* global.c */
    #include <stdio.h>
    #include "proto.h"   
    /* include this proto.h file for the declaration of glob_func() */
    void main(void);
    void main(void)
    {
         ...
         rc = glob_func(1, 2);
         ...
    }
    /* definition (body) of the glob_func() function */
    int glob_func(int arg1, int arg2)
    {
         ...
         return rc;
    }
    

     

    /* extern.c */
    #include <stdio.h>
    #include "proto.h"   
    /* include this proto.h file for the declaration of glob_func() */
    void ext_func(void);
    void ext_func(void)
    {
         ...
         /* call glob_func(), which is defined in the global.c source file */
         rc = glob_func(10, 20);
         ...
    }
    

    In the preceding example, the declaration of glob_func() is put in the header file named proto.h becauseglob_func() is used in both the global.c and the extern.c source files. Now, whenever glob_func() is going to be used, you simply need to include the proto.h header file, and you will automatically have the function's declaration. This will help your compiler when it is checking parameters and return values from global functions you are using in your programs. Notice that your function declarations should always appear before the first function declaration in your source file.

    In general, if you think your function might be of some use outside of the current source file, you should put its declaration in a header file so that other modules can access it. Otherwise, if you are sure your function will never be used outside of the current source file, you should declare the function as static and include the declaration only in the current source file.

    View
  • 7.

    What is the benefit of using const for declaring constants?

    Answer:

    The benefit of using the const keyword is that the compiler might be able to make optimizations based on the knowledge that the value of the variable will not change. In addition, the compiler will try to ensure that the values won't be changed inadvertently.

    Of course, the same benefits apply to #defined constants. The reason to use const rather than #define to define a constant is that a const variable can be of any type (such as a struct, which can't be represented by a #defined constant). Also, because a const variable is a real variable, it has an address that can be used, if needed, and it resides in only one place in memory.

    View
  • 8.

    Can static variables be declared in a header file?

    Answer:

    You can't declare a static variable without defining it as well (this is because the storage class modifiers static and extern are mutually exclusive). A static variable can be defined in a header file, but this would cause each source file that included the header file to have its own private copy of the variable, which is probably not what was intended.

    View
  • 9.

    What is the difference between declaring a variable and defining a variable?

    Answer:

    Declaring a variable means describing its type to the compiler but not allocating any space for it. Defining a variable means declaring it and also allocating space to hold the variable. You can also initialize a variable at the time it is defined. Here is a declaration of a variable and a structure, and two variable definitions, one with initialization:

    extern int decl1;  /* this is a declaration */
    struct decl2 
    {
        int member;
    }; /* this just declares the type--no variable mentioned */
    int     def1 = 8;      /* this is a definition */
    int     def2;          /* this is a definition */
    

    To put it another way, a declaration says to the compiler, "Somewhere in my program will be a variable with this name, and this is what type it is." A definition says, "Right here is this variable with this name and this type."

    A variable can be declared many times, but it must be defined exactly once. For this reason, definitions do not belong in header files, where they might get #included into more than one place in your program.

    View
  • 10.

    Is it acceptable to declare/define a variable in a C header?

    Answer:

    global variable that must be accessed from more than one file can and should be declared in a header file. In addition, such a variable must be defined in one source file. Variables should not be defined in header files, because the header file can be included in multiple source files, which would cause multiple definitions of the variable.

    The ANSI C standard will allow multiple external definitions, provided that there is only one initialization. But because there's really no advantage to using this feature, it's probably best to avoid it and maintain a higher level of portability.

    "Global" variables that do not have to be accessed from more than one file should be declared static and should not appear in a header file.

    View
  • 11.

    When should a type cast not be used?

    Answer:

    A type cast should not be used to override a const or volatile declaration. Overriding these type modifiers can cause the program to fail to run correctly.

    A type cast should not be used to turn a pointer to one type of structure or data type into another. In the rare events in which this action is beneficial, using a union to hold the values makes the programmer's intentions clearer.

    View
  • 12.

    When should a type cast be used?

    Answer:

    There are two situations in which to use a type cast. The first use is to change the type of an operand to an arithmetic operation so that the operation will be performed properly. The variable f1 is set to the result of dividing the integer i by the integer j. The result is 0, because integer division is used. The variable f2 is set to the result of dividing i by j as well. However, the (float) type cast causes i to be converted to a float. That in turn causes floating-point division to be used and gives the result 0.75.

    #include <stdio.h>
    main()
    {
        int i = 3;
        int j = 4;
        float f1 = i / j;
        float f2 = (float) i / j;
        printf("3 / 4 == %g or %g depending on the type used.\n", f1, f2);
    }
    

    The second case is to cast pointer types to and from void * in order to interface with functions that expect or return void pointers. For example, the following line type casts the return value of the call to malloc() to be a pointer to a foo structure.

    struct foo *p = (struct foo *) malloc(sizeof(struct foo));

    View
  • 13.

    What is operator promotion?

    Answer:

    If an operation is specified with operands of two different types, they are converted to the smallest type that can hold both values. The result has the same type as the two operands wind up having. To interpret the rules, read the following table from the top down, and stop at the first rule that applies.

    If Either Operand Is   And the Other Is   Change Them To
    long double - any other type - long double
    double - any smaller type - double
    float - any smaller type - float
    unsigned long - any integral type - unsigned long
    long - unsigned > LONG_MAX - long
    long - any smaller type - long
    unsigned - any signed type - unsigned

    The following example code illustrates some cases of operator promotion. The variable f1 is set to 3/4. Because both 3 and 4 are integers, integer division is performed, and the result is the integer 0. The variablef2 is set to 3/4.0. Because 4.0 is a float, the number 3 is converted to a float as well, and the result is the float 0.75.

    #include <stdio.h>
    main()
    {
        float f1 = 3 / 4;
        float f2 = 3 / 4.0;
        printf("3 / 4 == %g or %g depending on the type used.\n", f1, f2);
    }
    View
  • 14.

    Are there any problems with performing mathematical operations on different variable types?

    Answer:

    C has three categories of built-in data types: pointer types, integral types, and floating-point types. Pointer types are the most restrictive in terms of the operations that can be performed on them. They are limited to

    - subtraction of two pointers, valid only when both pointers point to elements in the same array. The result is the same as subtracting the integer subscripts corresponding to the two pointers.

    + addition of a pointer and an integral type. The result is a pointer that points to the element which would be selected by that integer.

    Floating-point types consist of the built-in types float, double, and long double. Integral types consist of char, unsigned char, short, unsigned short, int, unsigned int, long, and unsigned long. All of these types can have the following arithmetic operations performed on them:

    + Addition

    - Subtraction

    * Multiplication

    / Division

    Integral types also can have those four operations performed on them, as well as the following operations: % Modulo or remainder of division

    << Shift left

    >> Shift right

    & Bitwise AND operation

    | Bitwise OR operation

    ^ Bitwise exclusive OR operation

    ! Logical negative operation

    ~ Bitwise "one's complement" operation

    Although C permits "mixed mode" expressions (an arithmetic expression involving different types), it actually converts the types to be the same type before performing the operations (except for the case of pointer arithmetic described previously). The process of automatic type conversion is called "operator promotion."

    View
  • 15.

    How can you determine the maximum value that a numeric variable can hold?

    Answer:

    The easiest way to find out how large or small a number that a particular type can hold is to use the values defined in the ANSI standard header file limits.h. This file contains many useful constants defining the values that can be held by various types, including these:

    Value   Description
    CHAR_BIT - Number of bits in a char
    CHAR_MAX - Maximum decimal integer value of a char
    CHAR_MIN - Minimum decimal integer value of a char
    MB_LEN_MAX - Maximum number of bytes in a multibyte character
    INT_MAX - Maximum decimal value of an int
    INT_MIN - Minimum decimal value of an int
    LONG_MAX - Maximum decimal value of a long
    LONG_MIN - Minimum decimal value of a long
    SCHAR_MAX - Maximum decimal integer value of a signed char
    SCHAR_MIN - Minimum decimal integer value of a signed char
    SHRT_MAX - Maximum decimal value of a short
    SHRT_MIN - Minimum decimal value of a short
    UCHAR_MAX - Maximum decimal integer value of unsigned char
    UINT_MAX - Maximum decimal value of an unsigned integer
    ULONG_MAX - Maximum decimal value of an unsigned long int
    USHRT_MAX - Maximum decimal value of an unsigned short int

    For integral types, on a machine that uses two's complement arithmetic (which is just about any machine you're likely to use), a signed type can hold numbers from -2(number of bits - 1) to +2(number of bits - 1) - 1.

    An unsigned type can hold values from 0 to +2(number of bits)- 1. For instance, a 16-bit signed integer can hold numbers from -215(-32768) to +215 - 1 (32767).

    View
  • 16.

    How reliable are floating-point comparisons?

    Answer:

    Floating-point numbers are the "black art" of computer programming. One reason why this is so is that there is no optimal way to represent an arbitrary number. The Institute of Electrical and Electronic Engineers (IEEE) has developed a standard for the representation of floating-point numbers, but you cannot guarantee that every machine you use will conform to the standard.

    Even if your machine does conform to the standard, there are deeper issues. It can be shown mathematically that there are an infinite number of "real" numbers between any two numbers. For the computer to distinguish between two numbers, the bits that represent them must differ. To represent an infinite number of different bit patterns would take an infinite number of bits. Because the computer must represent a large range of numbers in a small number of bits (usually 32 to 64 bits), it has to make approximate representations of most numbers.

    Because floating-point numbers are so tricky to deal with, it's generally bad practice to compare a floating- point number for equality with anything. Inequalities are much safer. If, for instance, you want to step through a range of numbers in small increments, you might write this:

    #include <stdio.h>
    const float first = 0.0;
    const float last = 70.0;
    const float small = 0.007;
    main()
    {
            float   f;
            for (f = first; f != last && f < last + 1.0; f += small)
                    ;
            printf("f is now %g\n", f);
    }
    

    However, rounding errors and small differences in the representation of the variable small might cause f to never be equal to last (it might go from being just under it to being just over it). Thus, the loop would go past the value last. The inequality f < last + 1.0 has been added to prevent the program from running on for a very long time if this happens. If you run this program and the value printed for f is 71 or more, this is what has happened.

    A safer way to write this loop is to use the inequality f < last to test for the loop ending, as in this example:

    float   f;
    for (f = first; f < last; f += small)
            ;
    

    You could even precompute the number of times the loop should be executed and use an integer to count iterations of the loop, as in this example:

    float   f;
    int     count = (last - first) / small;
    for (f = first; count-- > 0; f += small)
    View
  • 17.

    When should the const modifier be used?

    Answer:

    There are several reasons to use const pointers. First, it allows the compiler to catch errors in which code accidentally changes the value of a variable, as in

    while (*str = 0) /* programmer meant to write *str != 0 */
    {
        /* some code here */
        str++;
    }
    

    in which the = sign is a typographical error. Without the const in the declaration of str, the program would compile but not run properly.

    Another reason is efficiency. The compiler might be able to make certain optimizations to the code generated if it knows that a variable will not be changed.

    Any function parameter which points to data that is not modified by the function or by any function it calls should declare the pointer a pointer to const. Function parameters that are passed by value (rather than through a pointer) can be declared const if neither the function nor any function it calls modifies the data.

    In practice, however, such parameters are usually declared const only if it might be more efficient for the compiler to access the data through a pointer than by copying it.

    View
  • 18.

    Can a variable be both const and volatile?

    Answer:

    Yes. The const modifier means that this code cannot change the value of the variable, but that does not mean that the value cannot be changed by means outside this code. For instance, the timer structure was accessed through a volatile const pointer. The function itself did not change the value of the timer, so it was declared const. However, the value was changed by hardware on the computer, so it was declared volatile. If a variable is both const and volatile, the two modifiers can appear in either order.

    View
  • 19.

    When should the volatile modifier be used?

    Answer:

    The volatile modifier is a directive to the compiler's optimizer that operations involving this variable should not be optimized in certain ways. There are two special cases in which use of the volatile modifier is desirable. The first case involves memory-mapped hardware (a device such as a graphics adaptor that appears to the computer's hardware as if it were part of the computer's memory), and the second involves shared memory (memory used by two or more programs running simultaneously).

    Most computers have a set of registers that can be accessed faster than the computer's main memory. A good compiler will perform a kind of optimization called "redundant load and store removal." The compiler looks for places in the code where it can either remove an instruction to load data from memory because the value is already in a register, or remove an instruction to store data to memory because the value can stay in a register until it is changed again anyway.

    If a variable is a pointer to something other than normal memory, such as memory-mapped ports on a peripheral, redundant load and store optimizations might be detrimental. For instance, here's a piece of code that might be used to time some operation:

    time_t time_addition(volatile const struct timer *t, int a)
    {
            int     n;
            int     x;
            time_t  then;
            x = 0;
            then = t->value;
            for (n = 0; n < 1000; n++)
            {
                    x = x + a;
            }
           return t->value - then;
    }
    

    In this code, the variable t->value is actually a hardware counter that is being incremented as time passes. The function adds the value of a to x 1000 times, and it returns the amount the timer was incremented by while the 1000 additions were being performed.

    Without the volatile modifier, a clever optimizer might assume that the value of t does not change during the execution of the function, because there is no statement that explicitly changes it. In that case, there's no need to read it from memory a second time and subtract it, because the answer will always be 0. The compiler might therefore "optimize" the function by making it always return 0.

    If a variable points to data in shared memory, you also don't want the compiler to perform redundant load and store optimizations. Shared memory is normally used to enable two programs to communicate with each other by having one program store data in the shared portion of memory and the other program read the same portion of memory. If the compiler optimizes away a load or store of shared memory, communication between the two programs will be affected.

    View
  • 20.

    When should the register modifier be used? Does it really help?

    Answer:

    The register modifier hints to the compiler that the variable will be heavily used and should be kept in the CPU's registers, if possible, so that it can be accessed faster. There are several restrictions on the use of the register modifier.

    First, the variable must be of a type that can be held in the CPU's register. This usually means a single value of a size less than or equal to the size of an integer. Some machines have registers that can hold floating-point numbers as well.

    Second, because the variable might not be stored in memory, its address cannot be taken with the unary and operator. An attempt to do so is flagged as an error by the compiler. Some additional rules affect how useful the register modifier is. Because the number of registers is limited, and because some registers can hold only certain types of data (such as pointers or floating-point numbers), the number and types of register modifiers that will actually have any effect are dependent on what machine the program will run on. Any additional register modifiers are silently ignored by the compiler.

    Also, in some cases, it might actually be slower to keep a variable in a register because that register then becomes unavailable for other purposes or because the variable isn't used enough to justify the overhead of loading and storing it.

    So when should the register modifier be used? The answer is never, with most modern compilers. Early C compilers did not keep any variables in registers unless directed to do so, and the register modifier was a valuable addition to the language. C compiler design has advanced to the point, however, where the compiler will usually make better decisions than the programmer about which variables should be stored in registers. In fact, many compilers actually ignore the register modifier, which is perfectly legal, because it is only a hint and not a directive.

    In the rare event that a program is too slow, and you know that the problem is due to a variable being stored in memory, you might try adding the register modifier as a last resort, but don't be surprised if this action doesn't change the speed of the program.

    View

© 2017 QuizBucket.org