Common Errors Related to Memory in C Language Programs


In day-to-day development, memory-related errors are the most annoying. A memory problem occurs, and the program often does not manifest itself until some distance from the source of the error. The following lists and analyzes several errors related to memory:

1. Indirect reference to illegal pointer

If a pointer to data that has no meaning is dereferenced, the operating system terminates the program abnormally with a segmentation fault

For example:

scanf("%d", value);

In this case, scanf will regard the content of value as an address and try to write an integer data to this location. This causes the program to throw an exception and terminate immediately.

2. Operating uninitialized memory

int *func(int **A, int *x, int n)
{
    
    
    int i, j;
    int *p = (int *)malloc(n * sizeof(int));

    for(i = 0; i < n; i++)
    {
    
    
        for(j = 0; j < n; j++)
        {
    
    
            p[i] += A[i][j] * x[j]
        }
    }
    return y;
}

In the example, the newly-applied memory address (the address pointed to by p) is not initialized to zero. You should explicitly set y[i]memset to zero, or use the calloc function to apply for memory.

3. Stack buffer overflow

void func()
{
    
    
    char buf[10];
    gets(buf);
    
    return;
}

This function writes to the target buffer on the stack without checking the size of the input string, and a buffer overflow error will occur, because the gets function simply copies a string of any length to the buffer, and does not limit the size of the input string. The way to solve this problem is to use the fgets function that limits the size of the input string.

4. Different sizes of pointers and objects pointed to by pointers lead to heap overflow

A common mistake is to assume that pointers to objects are the same size as the objects they point to, example program:

int **makeArray(int n, int m)
{
    
    
    int i;
    int **A = (int **)malloc(n * sizeof(int));
    
    for(i = 0; i < n; i++)
    {
    
    
        A[j] = (int *)malloc(m * sizeof(int));
    }
    return A;
}

The purpose of this program is to create an array of n pointers, each pointing to an array containing m int type data. However, the program then writes sizeof(int *) as sizeof(int) in the initialization, and the code actually creates an array of ints instead of n pointers of type int as envisaged when designing the program. So this code will only work well on machines where the int and the pointer to int are the same size, otherwise an error will occur.

5. Loop boundary leads to memory out of bounds

int **makeArray(int n, int m)
{
    
    
    int i;
    int **A = (int **)malloc(n * sizeof(int *)); 

    for(i = 0; i <= n; i++) /* 注意循环终止条件 */
    {
    
    
        A[j] = (int *)malloc(m * sizeof(int));
    }
    return A;
}

In the last step of the program loop, because the boundary value is incorrect, it will try to initialize the n+1th element of the array, and this process will overwrite a certain memory location behind the A array.

Six, the priority of the operator leads to the operation of the pointer and the object pointed to by the pointer

If we don't pay enough attention to the precedence and associativity of C operators, we will incorrectly manipulate pointers or objects pointed to by pointers.
For example: the code to reduce the value of an integer pointed to by a pointer is written as follows:

*ptr--;

However, since the unary operators “--”and “*”have the same precedence, they associate from right to left. Then the actual effect of the above code is *(ptr--)that the value of the pointer itself is reduced, and the address of the pointer is moved, not the value of the integer it points to.

The corrected code is as follows:

(*ptr)--;

7. Pointer operation unit error

The arithmetic operation of the pointer is performed in units of the size of the object pointed to by the pointer, not necessarily a byte

For example, to loop over an array of ints and return a pointer to the first occurrence of val:

int *func(int *p, int val)
{
    
    
    while(*p && *p != val)
    {
    
    
        p += sizeof(int);
    }
    return p;
}

If it is a 64-bit machine, the pointer p is added by 4 (a number of bytes of int type) every time it loops, and the int type pointer is 8 bytes, so the p pointer is moved to after 4 int data , incorrectly scans every 4 integers in the array

8. Operate local variables that are not in the life cycle

int *func()
{
    
    
    int val;

    return &val;
}

This function returns a pointer (assumed to be ptr) to a local variable on the stack, and pops its stack frame. Although ptr still points to a valid memory location, it no longer points to a valid variable.

When other functions are called later in the program, the memory will reuse their stack frames. If the program assigns to *ptr, it may actually be modifying data in another containing stack frame, with potentially disastrous consequences.

Nine, refer to the data in the heap block that has been released

int *func(int n, int m)
{
    
    
    int i;
    int *x, *y;

    x = (int *)malloc(n * sizeof(int));
    free(x);

    y = (int *)malloc(m * sizeof(int));
    for(i = 0; i < m; i++)
    {
    
    
        y[i] = x[i]++;
    }

    return y;
}

Operating x[i] after free(x), its content may already be part of some other allocated heap block, causing the program running results to be inconsistent with expectations, and an error occurs

10. Memory leaks

A memory leak occurs when you accidentally forget to release the allocated memory block during development and create garbage in the heap

void func(int n)
{
    
    
    int *x = (int *)malloc(n * sizeof(int));

    return;
}

If this function is called frequently, gradually the heap will be filled with garbage, causing a memory leak. Also, it can sometimes cause program termination or other problems.

Guess you like

Origin blog.csdn.net/future_sky_word/article/details/131489284