重新调整动态分配的数组大小

时间:2015-10-23 18:30:36

标签: c pointers malloc free

如果一个项目在存储时超出范围,我编写了以下代码来调整数组大小。此代码按预期工作。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//! Our location struct for storing locations
struct location
{
    char loc_name[35];
    char description[85];
    float latitude;
    float longitude;
};

void runMenu();
void add_location(struct location** p_location_array, int* p_array_size, int* p_current_size);
void resize_array(struct location** p_location_array, int* p_array_size);
void print (struct location* p_array, int p_current_size);

// Print out the main menu to the user.
void runMenu()
{
    printf("[A]dd additional Locations\n");
    printf("[P]rint the current list of locations\n");\
    printf("[Q]uit the program\n");
}

//! Resize the array to two times it's origional size.
void resize_array(struct location** p_location_array, int* p_array_size)
{
    // Allocate enough space for two times the size of the array
    int new_size = 2 * (*p_array_size);
    struct location* new_location_array = malloc(new_size * sizeof(struct location));
    if (!new_location_array)
    {
        printf ("Cannot add more elements heap has exhausted all space\n");
        exit(1);
    }
    // Copy the old array to the new array.
    memcpy(new_location_array, *p_location_array, ((*p_array_size ) * sizeof(struct location)));
    // We will update the current size of the array for later checking.
    *p_array_size = 2 * (*p_array_size);
    // We have a copy of the old array so we can free it.
    free(*p_location_array);
    // The contents of the pointer reference get the array we malloced in this function
    *p_location_array = new_location_array;
}

//! Add a new location to our array. If the array isn't large enough resize it then insert the new struct.
void add_location(struct location** p_location_array, int* p_array_size, int* p_current_size )
{
    // Get the users input
    struct location new_location;
    printf("Enter the new location name\n ");
    fscanf(stdin, "%s", new_location.loc_name); 
    printf("Enter a description of the location\n");
    fscanf(stdin, "%s", new_location.description),
    printf("Enter the latitude\n");
    fscanf(stdin, "%f", &new_location.latitude);
    printf("Enter the longitude\n");
    fscanf(stdin, "%f", &new_location.longitude);

    // Check to see if the size is correct.
    if (*p_array_size <= *p_current_size)
    {
        // If not the correct size resize the array
        resize_array(p_location_array, p_array_size);
    }
    // Insert our sruct
    (*p_location_array)[*p_current_size] = new_location;
}

//! Loop over and print out the locations
void print (struct location* p_array, int p_current_size)
{
    int i;
    for (i = 0; i < p_current_size; i++)
    {
        struct location current = p_array[i];
        printf("%s :  %s  : %f : %f\n", current.loc_name, current.description, current.latitude, current.longitude);
    } 
}



int main()
{
    char choice = ' ';
    short control = 1;
    int size;
    int currentSize = 0;

    printf("Enter the inital size of the array\n");
    scanf(" %d", &size);

    // Make a new struct array from the heap
    struct location* m_location_array = 
         malloc(size * sizeof(struct location));

    // Make sure we have a valid chunk of the heap.
    if (!m_location_array)
        exit(1);

    while(control)
    {
        runMenu();
        scanf(" %c", &choice);

        switch (choice)
        {
            case 'a':
            case 'A':
                // Do Add additional
                add_location(&m_location_array, &size, &currentSize);
                currentSize++;
                break;
            case 'p':
            case 'P':
                // Do printing
                print (m_location_array, currentSize);
                break;
            case 'Q':
            case 'q':
                control = 0;
                break;
            default:
                printf("Invalid input\n");
        }   
    }
    // clean up after ourselves.
    free (m_location_array);
    return 0;

}

然而,当我最初编写这个函数时,我认为可以只传入一个指向数组的指针,而不是像这样引用指针:

 void resize_array(struct location* p_location_array, int* p_array_size)

在没有引用指针的情况下调用此函数会抛出一个段错误,指示内存被双重释放。那是因为传递给函数的指针在某种程度上被释放并重新分配了吗?此外,为什么有必要像这样通过引用传递指针?即使指针是原始指针的副本也不会指向同一块内存?非常感谢正确方向上的任何一点。

2 个答案:

答案 0 :(得分:1)

你给了一个指向该函数的指针,你在那里调用了free。所以内存被释放了。之后使用该指针会导致未定义的行为,您可能无法使用它。

修改函数内部的指针变量不会改变函数外部的指针。这就是为什么你需要一个指向指针的指针,以便你可以修改函数外的指针变量。

  

即使指针是原始指针的副本,它仍然指向同一块内存吗?

是的,这就是重点:它将继续指向同一个地方,除非你改变它。如果你做一个新的malloc,它将指向一个完全不同的地方。

另外一个提示:realloc可能值得一试。

答案 1 :(得分:1)

如果您将指针传递给函数,则它是原始指针的副本。即使你在函数内部指定了这个指针,比如

p_location_array = new_location_array;

原始指针(在函数外部)仍然具有未更改的值。因此,如果原始指针指向某个内存区域并且您已将其传递给函数

void resize_array(struct location *p_location_array, int* p_array_size)

并且您已在函数内调用free()并将NULL指定给指针,在函数返回后,原始指针将comapre指定为NULL

// warning, changed prototype
void resize_array(struct location *p_location_array, int* p_array_size);

struct location *loc = malloc(size * sizeof(struct location)); // assume loc = 0x12345678
if (loc == NULL) EXIT_FAILURE;
// change pointer inside the function
// assign NULL to the pointer
resize_array(loc, size_p);
if (loc != NULL)
    free(loc);  // this will be called, loc is still 0x12345678,
                // double free, UB