C undefined behavior

In the C language,"undefined behavior" (undefined behavior)refers to behavior that is not clearly defined in the C language standard, so it can manifest as any result.

This means that when a program exhibits undefined behavior, it may produce unpredictable results, including program crashes, data corruption, security vulnerabilities, and even may appear to run normally.

Undefined behavior is an important concept in the C language because it relates to program correctness and safety.

The following are some common situations that may lead to undefined behavior:

array out of bounds

When we attempt to access an out-of-bounds element of an array, that is, accessing an element before the 0th element or beyond the array length, the compiler cannot determine what is stored in the memory space being accessed, thus causing undefined behavior. For example:

int arr[3] = {1, 2, 3};
printf("%d\n", arr[5]); // 越界访问,结果未定义

Dereferencing a null pointer

When we attempt to dereference a null pointer, the compiler cannot determine the content stored in the memory space to be accessed, thus causing undefined behavior. For example:

int *ptr = NULL;
printf("%d\n", *ptr); // 解引用空指针,结果未定义

Uninitialized local variables

When we use an uninitialized local variable, its value is undefined, thus leading to undefined behavior. For example:

int x;
printf("%d\n", x); // x 未初始化,结果未定义

Dividing a floating-point number by zero

When we attempt to divide a floating-point number by zero, the result is undefined. For example:

float x = 1.0;
float y = x / 0.0; // 浮点数除以零,结果未定义

integer division by zero

When we attempt to divide an integer by zero, the result is undefined. For example:

int x = 10;
int y = x / 0; // 整数除以零,结果未定义

signed overflow

When an integer operation causes the result to exceed the range representable by the integer type, the result is undefined. For example:

signed char x = 127;
x = x + 1; // signed char 溢出,结果未定义

Shift operation operand too large

When performing a shift operation, if the number of bits shifted is greater than or equal to the number of bits in the operand, the result is undefined. For example:

int x = 1;
int y = x << 32; // 位移操作数太大,结果未定义

Incorrect type conversion

When we perform an unsafe type conversion, the result is undefined. For example:

int *ptr = (int *)malloc(sizeof(int));
float *fptr = (float *)ptr; // 错误的类型转换,结果未定义

memory out of bounds

When we write data to memory that has already been freed or is unallocated, the result is undefined. For example:

int *ptr = (int *)malloc(sizeof(int));
free(ptr);
*ptr = 10; // 内存越界,结果未定义

Undefined floating-point behavior

For example, comparing two NaN (Not a Number) values for equality is undefined behavior. For example:

float x = sqrt(-1);
float y = sqrt(-1);
if (x == y) {
    printf("NaN values are equal\n");
}

Other

There are also some other undefined behaviors:

  • Using undefined floating-point features:Floating-point behavior that depends on specific hardware or implementation, such as the precision or rounding behavior of floating-point numbers.

  • Mismatched number of function arguments:The number of arguments provided when calling a function does not match the function definition, such asprintf("%s %d", "Name")。

  • Modifying string literals:Attempting to modify the contents of a string literal, such aschar *str = "Hello"; str[0] = 'h';。

  • Using undefined program state:Depending on undefined program state, such as the initial values of global variables.

  • Violating strict syntax rules:Violating C language strict grammar rules, such as using undeclared identifiers.

  • Race conditions in multithreading:In a multi-threaded environment, unsynchronized access to shared resources may lead to undefined behavior.

  • Using undefined standard library function behavior:The behavior of some standard library functions under specific conditions may be undefined, such as the behavior of fscanf() when it fails to match any input.

These are all situations that need to be avoided during programming because they may cause the program to produce unpredictable behavior in different environments, making the code non-portable and potentially leading to program errors.

how to avoid

To avoid undefined behavior, during the development process we need to:

  • Carefully read and follow the C language standard: Understand which operations may lead to undefined behavior, and avoid these operations.
  • Use static analysis tools: These tools can help detect potential undefined behavior.
  • Conduct thorough testing: Test different execution paths of the program to ensure it runs correctly under various conditions.
  • Avoid relying on undefined behavior: Do not assume that undefined behavior will produce specific results.
  • Use safe functions and libraries: Use well-defined functions provided by the standard library, and avoid non-standard or unsafe functions that may lead to undefined behavior.

Undefined behavior is a complex and dangerous concept in the C language. It requires developers to have a deep understanding of the rules of C and to take appropriate measures to avoid it. By following best practices and using appropriate tools, the risk of undefined behavior can be minimized, thereby improving the reliability and safety of the program.

other extensions