Correct Answer: To identify and fix errors in the code
Explanation: A debugger is a tool that helps programmers identify and fix errors in their code by allowing them to step through the code, inspect variables, and understand the program’s flow.
Correct Answer: GDB
Explanation: GDB (GNU Debugger) is a popular debugging tool for C and C++ programs that allows developers to examine the program’s execution and identify errors.
Correct Answer: Runtime error
Explanation: A runtime error occurs during the execution of a program and causes it to crash or produce incorrect results. It is different from syntax and logical errors.
Correct Answer: Loop unrolling
Explanation: Loop unrolling is an optimization technique that improves the efficiency of loops by reducing the overhead of loop control code and increasing the amount of work done per iteration.
Correct Answer: Measuring the performance of a program
Explanation: Profiling is the process of measuring the performance of a program to identify which parts of the code are consuming the most resources, allowing developers to focus optimization efforts on those areas.
Correct Answer: -O3
Explanation: The -O3 optimization level in GCC provides the most aggressive optimizations, including function inlining, loop unrolling, and vectorization, to improve the performance of the generated code.
Correct Answer: Detecting memory leaks
Explanation: Valgrind is a programming tool used to detect memory leaks, memory corruption, and other memory-related errors in programs, helping developers ensure their programs are memory-safe.
Correct Answer: Logical error
Explanation: Logical errors are the most difficult to debug because they do not cause the program to crash or produce error messages but result in incorrect behavior or output due to flaws in the program’s logic.
Correct Answer: To catch errors during development
Explanation: Assert statements are used to catch errors during development by checking for conditions that should always be true. If the condition is false, the program will terminate and provide an error message.
Correct Answer: Inline expansion
Explanation: Inline expansion, or inlining, is an optimization technique where the compiler replaces a function call with the actual code of the function, reducing the overhead associated with function calls and potentially improving performance.
Correct Answer: gdb a.out
Explanation: To start a GDB session with an executable file named “a.out,” you use the command `gdb a.out`. This initializes the GDB debugger with the specified executable.
Correct Answer: break [line_number]
Explanation: The `break` command in GDB is used to set a breakpoint at a specific line number in the source file, allowing the program to pause execution at that line for debugging purposes.
Correct Answer: Starts the execution of the program
Explanation: The `run` command in GDB starts the execution of the program being debugged. If the program has any input arguments, they can be specified after the `run` command.
Correct Answer: print [variable_name]
Explanation: The `print` command in GDB is used to display the value of a variable. For example, `print myVariable` will show the current value of `myVariable`.
Correct Answer: step
Explanation: The `step` command in GDB allows you to step through the code line by line, entering into functions. The `next` command is similar but does not enter functions.
Correct Answer: list
Explanation: The `list` command in GDB displays the source code around the current line. This helps in understanding the context of the code being debugged.
Correct Answer: To resume execution until the next breakpoint
Explanation: The `continue` command in GDB resumes the execution of the program until the next breakpoint is reached or the program terminates.
Correct Answer: backtrace
Explanation: The `backtrace` command in GDB shows the call stack, which is a list of the function calls that were made leading up to the current point of execution. This is useful for understanding how the program arrived at a certain state.
Correct Answer: delete [breakpoint_number]
Explanation: The `delete` command in GDB is used to remove a breakpoint. You need to specify the breakpoint number to delete a specific breakpoint.
Correct Answer: quit
Explanation: The `quit` command in GDB exits the debugger and terminates the debugging session. Any unsaved changes or breakpoints will be lost.
Correct Answer: Print statements
Explanation: Using print statements to output the values of variables at different points in the program is a common debugging technique to track the program’s state and identify where things go wrong.
Correct Answer: To check for conditions that should always be true
Explanation: Assert statements are used in debugging to check for conditions that should always be true during program execution. If the condition is false, the program terminates and provides an error message.
Correct Answer: Unit testing
Explanation: Unit testing involves isolating and testing smaller parts or units of the code individually to ensure that each part functions correctly. This technique helps identify errors in specific parts of the code.
Correct Answer: Examining code for errors by peers or teammates
Explanation: Code review is the process of examining code for errors by peers or teammates to identify potential bugs, improve code quality, and ensure adherence to coding standards.
Correct Answer: Single stepping
Explanation: Single stepping is a debugging technique that involves using a debugger to step through the code execution one line at a time, allowing the developer to closely examine the program’s behavior and identify errors.
Correct Answer: To record program execution details for later analysis
Explanation: Logging involves recording program execution details, such as variable values and function calls, to a log file. This information can be analyzed later to identify and diagnose issues in the code.
Correct Answer: Divide and conquer
Explanation: Divide and conquer is a debugging technique that involves breaking the program into smaller sections and testing each section individually to identify the source of an error.
Correct Answer: It helps locate the exact point of failure quickly
Explanation: Binary search debugging involves systematically narrowing down the code to find the exact point of failure, which helps locate bugs more quickly by halving the search space with each step.
Correct Answer: Git bisect
Explanation: Git bisect is a debugging technique used in version control systems to review the history of code changes and identify the specific commit that introduced a bug by performing a binary search through the commit history.
Correct Answer: Measuring the performance of code
Explanation: Profiling is the process of measuring the performance of code, such as execution time and memory usage, to identify bottlenecks and optimize the program’s performance.
#include <stdio.h>
int main() {
int num;
printf("Enter a number: ");
scanf("%d", &num);
if (num > 10) {
printf("Number is greater than 10\n");
} else {
printf("Number is less than or equal to 10\n");
}
return 0;
}
Correct Answer: Number is less than or equal to 10
Explanation: The input is 5, which is less than or equal to 10, so the output will be “Number is less than or equal to 10”.
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) {
printf("%d ", arr[i]);
}
return 0;
}
Correct Answer: Array index out of bounds
Explanation: The loop iterates from 0 to 5, but the valid indices for the array `arr` are 0 to 4. Accessing `arr[5]` causes an array index out of bounds error.
#include <stdio.h>
int main() {
int a = 5, b = 0;
int c = a / b;
printf("%d\n", c);
return 0;
}
Correct Answer: Runtime error
Explanation: The code attempts to divide by zero, which causes a runtime error (division by zero).
#include <stdio.h>
int main() {
int *ptr = NULL;
*ptr = 10;
printf("%d\n", *ptr);
return 0;
}
Correct Answer: Using a debugger to inspect pointers
Explanation: Using a debugger to inspect the pointer `ptr` would reveal that it is `NULL`, and dereferencing it causes a segmentation fault.
#include <stdio.h>
#include <string.h>
int main() {
char str[10];
printf("Enter a string: ");
gets(str);
printf("You entered: %s\n", str);
return 0;
}
Correct Answer: Undefined behavior
Explanation: The `gets` function does not check for buffer overflow, so if the input exceeds the buffer size (10 in this case), it leads to undefined behavior.
#include <stdio.h>
int main() {
char *str;
strcpy(str, "Hello, World!");
printf("%s\n", str);
return 0;
}
Correct Answer: Using uninitialized pointer
Explanation: The pointer `str` is uninitialized and points to an arbitrary location, causing a segmentation fault when `strcpy` tries to copy the string to it.
#include <stdio.h>
void func(int *a) {
*a = 10;
}
int main() {
int x = 5;
func(&x);
printf("%d\n", x);
return 0;
}
Correct Answer: 10
Explanation: The function `func` modifies the value of `x` through its pointer, so the output is 10.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int) * 10);
ptr[0] = 1;
return 0;
}
Correct Answer: Memory leak
Explanation: The allocated memory using `malloc` is not freed, causing a memory leak.
#include <stdio.h>
int main() {
char *str = "Hello, World!";
str[0] = 'h';
printf("%s\n", str);
return 0;
}
Correct Answer: Declare `str` as an array instead of a pointer
Explanation: Declaring `str` as an array (`char str[] = "Hello, World!";`) instead of a pointer will allow modification of the string.
#include <stdio.h>
int main() {
int a = 5;
if (a = 10) {
printf("a is 10\n");
} else {
printf("a is not 10\n");
}
return 0;
}
Correct Answer: Assignment instead of comparison
Explanation: The condition `if (a = 10)` assigns 10 to `a` instead of comparing. It should be `if (a == 10)` for comparison.
Correct Answer: To define a macro
Explanation: The `#define` preprocessor directive is used to define a macro, which is a fragment of code that can be inserted into the program wherever the macro name is used.
#include <stdio.h>
#define SQUARE(x) ((x) * (x))
int main() {
int a = 3;
printf("%d\n", SQUARE(a+1));
return 0;
}
Correct Answer: 16
Explanation: The macro `SQUARE(x)` expands to `((x) * (x))`, so `SQUARE(a+1)` becomes `((a+1) * (a+1))`, which is `((3+1) * (3+1)) = 16`.
#define MAX(a, b) a > b ? a : b
Correct Answer: Lack of parentheses
Explanation: The macro `MAX(a, b)` should be defined with parentheses to ensure correct evaluation: `#define MAX(a, b) ((a) > (b) ? (a) : (b))`.
#include <stdio.h>
#define PRINT_VAR(var) printf(#var " = %d\n", var)
int main() {
int x = 5;
PRINT_VAR(x);
return 0;
}
Correct Answer: x = 5
Explanation: The `#` operator in the macro `PRINT_VAR` converts the variable name `x` to a string, resulting in the output `x = 5`.
#include <stdio.h>
#define DOUBLE(x) (2 * x)
int main() {
int a = 5;
printf("%d\n", DOUBLE(a++));
return 0;
}
Correct Answer: 12
Explanation: The macro `DOUBLE(x)` expands to `(2 * a++)`, which increments `a` after the expression is evaluated, resulting in `2 * 5 = 10` and then `a` becomes 6. However, due to side effects in the macro expansion, the output is 12.
#define INCREMENT(x) x++
Correct Answer: Use parentheses around the macro parameter
Explanation: The macro should be defined as `#define INCREMENT(x) (x++)` to ensure proper evaluation order.
Correct Answer: To check if a macro is defined
Explanation: The `#ifdef` directive checks if a macro is defined and includes the following code only if the condition is true.
#include <stdio.h>
#define DEBUG_PRINT(x) printf("Debug: %s = %d\n", #x, x)
int main() {
int value = 10;
#ifdef DEBUG
DEBUG_PRINT(value);
#endif
return 0;
}
Correct Answer: Debug: value = 10
Explanation: If `DEBUG` is defined, the `DEBUG_PRINT` macro will be included and executed, resulting in the output `Debug: value = 10`.
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
Correct Answer: Variables not passed by reference
Explanation: The function `swap` does not actually swap the values of `a` and `b` in the calling function because the variables are not passed by reference. Using pointers instead would fix the issue.
#include <stdio.h>
#define LOG_ERROR(message) fprintf(stderr, "Error: %s\n", message)
int main() {
LOG_ERROR("File not found");
return 0;
}
Correct Answer: Error: File not found
Explanation: The `LOG_ERROR` macro logs an error message to the standard error stream, resulting in the output `Error: File not found`.
#include <stdio.h>
#define DEBUG_PRINT(x) printf("Debug: %s = %d\n", #x, x)
int main() {
int value = 10;
#ifndef DEBUG
DEBUG_PRINT(value);
#endif
return 0;
}
Correct Answer: Debug: value = 10
Explanation: The `#ifndef DEBUG` directive checks if `DEBUG` is not defined. Since it is not defined, the `DEBUG_PRINT` macro will be included and executed, resulting in the output `Debug: value = 10`.
#define ABS(x) x < 0 ? -x : x
Correct Answer: Lack of parentheses
Explanation: The macro `ABS(x)` should be defined with parentheses to ensure correct evaluation: `#define ABS(x) ((x) < 0 ? -(x) : (x))`.
#include <stdio.h>
#define SQUARE(x) (x * x)
int main() {
int a = 3;
printf("%d\n", SQUARE(a++));
return 0;
}
Correct Answer: 12
Explanation: The macro `SQUARE(x)` expands to `(a++ * a++)`, which increments `a` twice and results in `3 * 4 = 12`.
Correct Answer: To undefine a macro
Explanation: The `#undef` directive is used to undefine a macro, removing its definition.
#include <stdio.h>
#define MESSAGE "Hello, World!"
int main() {
printf("%s\n", MESSAGE);
return 0;
}
Correct Answer: Hello, World!
Explanation: The macro `MESSAGE` is defined as `"Hello, World!"`, so the output will be `Hello, World!`.
#define MUL(a, b) a * b
Correct Answer: Lack of parentheses
Explanation: The macro `MUL(a, b)` should be defined with parentheses to ensure correct evaluation: `#define MUL(a, b) ((a) * (b))`.
#include <stdio.h>
#define CONCAT(a, b) a##b
int main() {
int xy = 10;
printf("%d\n", CONCAT(x, y));
return 0;
}
Correct Answer: 10
Explanation: The `##` operator in the macro `CONCAT(a, b)` concatenates `x` and `y` to form `xy`, which has the value 10.
#include <stdio.h>
#ifdef DEBUG
#define LOG(message) printf("Debug: %s\n", message)
#else
#define LOG(message)
#endif
int main() {
LOG("Starting program");
return 0;
}
Correct Answer: Debug: Starting program
Explanation: If `DEBUG` is defined, the `LOG` macro logs the message to the standard output, resulting in `Debug: Starting program`.
#include <stdio.h>
#define PRINT_VAR(var) printf(#var " = %d\n", var)
int main() {
int x = 5;
PRINT_VAR(x);
return 0;
}
Correct Answer: x = 5
Explanation: The `#` operator in the macro `PRINT_VAR` converts the variable name `x` to a string, resulting in the output `x = 5`.
#define MAX(a, b) a > b ? a : b
Correct Answer: Lack of parentheses
Explanation: The macro `MAX(a, b)` should be defined with parentheses to ensure correct evaluation: `#define MAX(a, b) ((a) > (b) ? (a) : (b))`.
Correct Answer: To verify that code works as expected
Explanation: Writing test cases helps to ensure that the code functions correctly and meets its requirements by checking various input and output scenarios.
Correct Answer: It is repeatable and produces consistent results
Explanation: A good test case should be repeatable and produce consistent results regardless of when or where it is executed.
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int main() {
printf("%d\n", add(2, 3));
return 0;
}
Correct Answer: 5
Explanation: The test case calls the `add` function with arguments 2 and 3, and the function returns their sum, which is 5.
Correct Answer: The percentage of code executed by the test cases
Explanation: Test coverage measures the percentage of the code that is executed when the test cases are run, helping to identify untested parts of the code.
Correct Answer: To check conditions at runtime
Explanation: Assertions are used in test cases to check whether a condition is true at runtime. If the condition is false, the assertion typically causes the program to terminate and indicate the failure.
#include <assert.h>
#include <stdio.h>
int main() {
int x = 5;
assert(x == 10);
printf("Assertion passed\n");
return 0;
}
Correct Answer: Assertion failed
Explanation: The `assert` statement checks if `x` is equal to 10. Since `x` is 5, the assertion fails, causing the program to terminate and indicate that the assertion failed.
Correct Answer: strcmp
Explanation: The `strcmp` function compares two strings for equality. It returns 0 if the strings are equal.
#include <assert.h>
int add(int a, int b) {
return a + b;
}
void test_add() {
assert(add(2, 3) == 5);
assert(add(-1, 1) == 0);
assert(add(0, 0) == 0);
}
int main() {
test_add();
return 0;
}
Correct Answer: To test the `add` function
Explanation: The `test_add` function contains test cases for the `add` function, verifying that it returns the correct results for different input values.
#include <assert.h>
#include <stdio.h>
void test_function() {
assert(1 + 1 == 2);
assert(2 * 2 == 4);
}
int main() {
test_function();
printf("All tests passed\n");
return 0;
}
Correct Answer: All tests passed
Explanation: If all assertions in the `test_function` pass, the program continues to execute and prints "All tests passed".
Correct Answer: CUnit
Explanation: CUnit is a unit testing framework for C that provides a set of macros to write and execute test cases, making it easier to test C programs.