【问题标题】:Calling printf changes array passed by address C调用 printf 更改地址 C 传递的数组
【发布时间】:2017-09-22 04:34:14
【问题描述】:

我有一段代码,它通过地址(指针)获取一个数组 这个函数简单地接受一个数组和一个整数值,它是它的长度,并一个一个地打印出数组的元素。

void dumpInts(int *array, int count, int hex){
    int i;    
    for(i = 0; i < count; i++){
        printf("  %*d",10,array[i]);
    } 

上面的代码是高度简化的,下面是其余的代码。它没有那么大。

#include <stdio.h>
#include <stdlib.h>
int LAST = -1059786739;
int FIRST = -559038737;
//Input is a series of integers read in from stdin separated by whitespace
void dumpInts(int *array, int count, int hex);
void initArray(int *array, int size);

int main(){
    int bufsiz ;
    if(scanf("%d",&bufsiz) < 1){
        fprintf(stderr,"CAN\'T READ BUFSIZ\n");
        return 1;
    }
    if(bufsiz < 1){
        fprintf(stderr,"BAD BUFSIZ=%d\n",bufsiz);
        return 1;
    }
    bufsiz+=2;
    int *array = (int *)malloc((size_t)bufsiz);
    if(array == NULL){
        fprintf(stderr,"NO MORE MEM\n");
        return 1;
    }
    array[0] = FIRST;        
    array[bufsiz-1] = LAST;   
    initArray(array,bufsiz);
    dumpInts(array,bufsiz,0);
    dumpInts(array,bufsiz,1);
    return 0;
}

void initArray(int *array, int size){
    int i;
    for(i = 1; i < size - 1; i++){
        array[i] = i;
    }
}

void dumpInts(int *array, int count, int hex){
    int i;    
    for(i = 0; i < count; i++){
        printf("  %*d",10,array[i]);
} 

事情就是这样。当数组的大小小于 7 时,效果很好。从我的终端查看这个 sn-p:

-559038737 1 2 3 4 -1059786739

虽然突然间,当大小超过 7 时,最后一个元素被覆盖,随后的所有内存都设置为随机值,但第 7 个元素总是设置为相同的值...1041

下面是一个大小为 8 的数组

-559038737 1 2 3 4 5 1041 0 892677408 942878777 现在,在我走得更远之前,我觉得我应该努力,我确实花时间仔细缩小阵列被破坏的确切位置。在它进入函数之前,它是我所期望的,一旦第一个循环打印出来,数组就会被破坏。

好的,这就是它变得奇怪的地方。我通过 SSH 连接到我大学的 linux 机器,然后运行我的代码。没有破坏,数组就好了。

7 号的对比如下

我的:

-559038737 1 2 3 4 5 1041

大学:

-559038737 1 2 3 4 5 -1059786739

我要重启我的机器....

*刚刚重启,同样的问题依然存在……同样的数字……同样的大小……

另外,当我在大学计算机上编译代码并将其复制到我的机器上时,数组仍然被破坏。当我在我的机器上编译它时不是这样。

我的机器和大学机器都是基于unix的系统,运行linux。

这到底是怎么回事?!

这没有任何意义。我在内存中传递数组的地址,所以我不希望堆栈与此有任何关系?

【问题讨论】:

  • 我在内存中传递数组的地址,所以我不希望堆栈与此有任何关系? - 如果实际元素在堆栈,那么当然堆栈可能与它有关。当然,现在还不能说。 minimal reproducible example 是关键。还可以尝试使用 -Wall -Wextra 进行编译,并可能使用诸如 clang-tidy 或 cppcheck 之类的东西来轻松开始。
  • 我的水晶球说错误出在您未显示的代码中。在调用该函数之前,您可能已经完全损坏了内存,但我们无法判断。
  • 不能确定它是否解决任何问题,但malloc 需要字节,而不是对象计数。
  • 您正在使用 malloc 分配内存。您询问您需要的元素数量,而您应该询问您需要的字节数。因此,您应该请求 malloc((size_t)bufsiz * sizeof(*array) ) 字节而不是 malloc((size_t)bufsiz)(sizeof(*array) 与 sizeof(int) 相同 - 它是数据类型。)通过请求第一个元素的大小,我们在维护时避免了问题通过使用不同的数据类型并忘记在我们的 malloc 调用中修改元素大小来编写以后的代码。 @chris 似乎已经提到了这个问题。 ;)
  • 您分配的内存太少。 Printf 也分配内存。 Printf 覆盖它分配的内存,因为它是正确的。在另一台机器上,它的行为不同,因为 printf 和/或 malloc 的实现方式不同。

标签: c arrays printf


【解决方案1】:

您的问题中隐含的诊断调用 printf 更改按地址传递的数组 不正确:您不按地址传递数组,而只是按值传递数组元素,printf 不会改变数组。您观察到的是未定义行为的影响。

在元素数量和分配数组的大小之间存在一些混淆:您可以为数组的元素数量 (count) 使用更有意义的名称,而不是数组的大小(以字节为单位) ,传递给malloc()

按照编码,你没有为数组分配足够的内存,初始化这个数组的代码和打印它的代码都有未定义的行为,因为你访问数组超出了它的边界。

此外,您不要使用hex 参数在十进制和十六进制输出之间进行选择。

这是一个更正的版本:

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

int LAST = -1059786739;
int FIRST = -559038737;

//Input is a series of integers read in from stdin separated by whitespace
void dumpInts(const int *array, int count, int hex);
void initArray(int *array, int size, int start);

int main(void) {
    int count;
    if (scanf("%d", &count) != 1) {
        fprintf(stderr,"Cannot read the number of elements\n");
        return 1;
    }
    if (count < 1) {
        fprintf(stderr,"Invalid count=%d\n", count);
        return 1;
    }
    count += 2;
    int *array = malloc(sizeof(*array), count);
    if (array == NULL) {
        fprintf(stderr,"Out of memory\n");
        return 1;
    }
    array[0] = FIRST;        
    initArray(array + 1, count - 2, 1);
    array[count - 1] = LAST;   
    dumpInts(array, count, 0);
    dumpInts(array, count, 1);
    return 0;
}

void initArray(int *array, int count, int start) {
    for (int i = 0; i < count; i++) {
        array[i] = start + i;
    }
}

void dumpInts(count int *array, int count, int hex) {
    for (int i = 0; i < count; i++) {
        if (hex) {
            printf("  %10x", array[i]);
        } else {
            printf("  %10d", array[i]);
        }
    }
} 

【讨论】:

  • 那么,虽然变量array 本身是一个整数内存地址,它是我的数组的开头,但将array 传递给函数会传递实际的数组吗?所以你的意思是如果我传递函数&amp;array,那么我将通过地址传递?
  • @TylerGabb:不,如果您传递了arrayarray + i,您将传递数组的地址,但您传递的是array[i],它是索引@ 处元素的值987654332@,一个按值传递的intprintf 不知道值的来源。
【解决方案2】:
int *array = (int *)malloc((size_t)bufsiz);

应该是:

int *array = (int *)malloc((sizeof(int))*bufsiz);

完整解释:

void *malloc(size_t size);

是malloc的原型。如你所知 size_t 是长无符号整数。

但类型转换不是问题。

假设我输入 bufsize = 8; 然后:int *array = (int *)malloc((size_t)bufsiz);

变成int *array = (int *)malloc(10);

实际上应该是int *array = (int *)malloc(4*10);

因为我们要存储 10 个整数并考虑 sizeof(int) = 4。

所以指针指向 10 个字节而不是所需的 40 个字节。因此没有足够的内存来存储整数。

这是一个 ArrayIndexOutOfBoundsException,在 C 中不存在

C 做边界检查,即它从不给出这个错误。 因此,如果您继续增加指针,它只是简单地进入 malloc 的缓冲区(确实需要读/写操作)并且代码不会出现段错误,直到它用完系统分配给程序的内存。

你没有释放内存; free(array) 丢失了。

编辑:malloc 不计算对象计数。它占用要分配的字节数。您正在使用 malloc 作为 calloc。

普罗斯特!

【讨论】:

  • 没有人建议在 C 中强制转换它。它可以使用但没有任何优势。仅在 C++ 中是必需的
  • 是的,我同意你的观点,但我对此持非常中立的立场:)
  • @DevX -- 在这个问题上不要太虔诚是件好事;就个人而言,我不喜欢投,如果你在你的答案中使用这样的(不必要的)投,请准备好从便宜的座位上买一些西红柿;)似乎是投malloc()等人的结果的唯一原因.在 C 中是在某些情况下传达意图;我很少觉得有必要这样做。但是,就在今天,我正在重用一些代码,将SDL_Color *color = malloc(sizeof *color * numcolors); 更改为RGBA *color = malloc(sizeof *color * numcolors);;我很欣赏只有一件事需要改变,而且犯错的机会更少。
  • size_tunsigned整数类型,但不一定是unsigned longint可能不是4字节宽,但必须至少是2字节宽。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-25
  • 1970-01-01
  • 2013-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多