首页 > 解决方案 > calloc'd后无法修改C中的数组

问题描述

我对 C 编程还是很陌生。这是一个程序,它接收一个由指针(int * 而不是 int[])组成的 int 数组,并将其中的每个 int 乘以 2。

任何大于 10 的 int 都会结转附加值,并且如果需要,数组的大小将加倍。所以,如果我输入

{9, 0, 4, 8},

它应该产生

{0, 0, 0, 1, 8, 0, 9, 6}

但是这个程序产生了 {0, 0, 0, 0, 9, 0, 4, 8}。

Valgrind 在调用doubleStorage(...)函数后告诉我multiply(...),我收到一个 InvalidRead 和 InvalidWrite 警告,我认为这是问题所在,在我调用数组以调整它的大小之后停止。但我不知道如何解决它。

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

int * doubleStorage(int ** array, int initialBlocks);
void printArray(int ** array, int blocks);
int * multiply(int ** array, int multiplier, int blocks);


int main() {
    int sizeOfArr = 4 * sizeof(int);
    int * arr = malloc(sizeOfArr);
    arr[0] = 9;
    arr[1] = 0;
    arr[2] = 4;
    arr[3] = 8;

    arr = multiply(&arr, 2, 4);
    printArray(&arr, 8);

    free(arr);

    return 0;
}

// multiplies each int in array by 2
// has a doubleStorage function to double array size if any multiplied int exceeds 10
int * multiply(int ** array, int multiplier, int blocks) {
    int currentBlocks = blocks;

    for (int i = 0; i < blocks; i++) {
        if ((*array)[i] * multiplier > 10) {
            if (i == 0) {
                *array = doubleStorage(array, currentBlocks);
                currentBlocks *= 2;
            }
            (*array)[i - 1] += 1; /* <-- Valgrind reports InvalidRead and InvalidWrite here*/
        }
        (*array)[i] = (*array)[i] * multiplier % 10;
    }

    return *array;
}

// doubles storage of array and relocates the original ints to the back
// doubleStorage({9, 0, 4, 8}, 4) => {0, 0, 0, 0, 9, 0, 4, 8}
int * doubleStorage(int ** array, int initialBlocks) {
    int * more = realloc(*array, 2 * initialBlocks * sizeof(int));
    if (more == NULL) {
        free(*array);
    } else {
        *array = more;
        for (int i = 0; i < initialBlocks; i++) {
            int num = (*array)[i];
            (*array)[i + initialBlocks] = num;
            (*array)[i] = 0;
        }
    }
    return *array;
}

// prints Array
void printArray(int ** array, int blocks) {
    printf("Array: ");
    for (int i = 0; i < blocks; i++) {
        printf("%d ", (*array)[i]);
    }
    printf("\n");
}

标签: cvalgrind

解决方案


问题是,当(或如果)您的multiply函数需要将数组大小加倍时,您会将想要/需要更改的值移动到数组的新“后部”。

(*array)[i]因此,像这样的表达式(或任何其他使用i内的值[])引用的元素将是错误的!当i值为零并且您尝试访问时,Valgrind 会注意到这一点(*array)[i - 1];但是,在这种大小更改后使用的每个参考也是错误的。i

为了解决这个问题,如果/当大小发生变化时,设置一个新变量来“监控”。在下面的代码中(当我测试它时,它按预期工作),这被称为offset

int* multiply(int** array, int multiplier, int blocks)
{
    int currentBlocks = blocks;
    int offset = 0; // This will need to change if we double our array size.
    for (int i = 0; i < blocks; i++) {
        if ((*array)[i + offset] * multiplier > 10) {
            if (i == 0) {
                *array = doubleStorage(array, currentBlocks);
                offset += currentBlocks; // Need to adjust offset following array doubling!
                currentBlocks *= 2;
            }
            (*array)[i - 1 + offset] += 1; // Always add the offset to our "i" value...
        }
        (*array)[i + offset] = (*array)[i + offset] * multiplier % 10; // ...and here, also!
    }
    return *array;
}

通过使用第三个变量(例如,p您设置的位置),可以使代码更“高效”(也许),p = i + offset但我试图使更改保持清晰和明显。

随时要求进一步澄清和/或解释。


推荐阅读