为什么calloc只在代码的特定部分工作?

为什么calloc只在代码的特定部分工作?,c,calloc,C,Calloc,为什么第一个代码可以正常工作,而第二个代码不能?唯一的区别是我在次要函数中放置calloc。我问这个问题的原因是,考虑到只有在第二个函数中执行for循环后,除数的值才是已知的,我会认为第二个代码才有效。你能给我一个快速的解释吗?谢谢大家! 另外,在运行第二个代码时,我没有收到任何错误或警告。有没有一种方法可以让我从编译器那里获得一些额外的信息来引导我朝着正确的方向前进?谢谢大家! N.b.很抱歉代码从逻辑角度看不是很有条理,只是为了更好地掌握一些编程概念而努力 第1代码: #include &l

为什么第一个代码可以正常工作,而第二个代码不能?唯一的区别是我在次要函数中放置
calloc
。我问这个问题的原因是,考虑到只有在第二个函数中执行for循环后,
除数
的值才是已知的,我会认为第二个代码才有效。你能给我一个快速的解释吗?谢谢大家!

另外,在运行第二个代码时,我没有收到任何错误或警告。有没有一种方法可以让我从编译器那里获得一些额外的信息来引导我朝着正确的方向前进?谢谢大家!

N.b.很抱歉代码从逻辑角度看不是很有条理,只是为了更好地掌握一些编程概念而努力

第1代码:

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


int* save_number(int *int_number);

int main()
{
   int int_number, *divisors = 0, count_even, i;

   printf("Please enter an integer:");
   divisors = save_number(&int_number);

   for (i = 1; i <= int_number; ++i)
   {
      if (divisors[i-1] % 2 == 0 && divisors[i-1] != 0)
      {
         printf("%d\n", divisors[i-1]);
         count_even++;
      }
      continue;
   }
   printf("Total even divisors: ");
   printf("%d\n", count_even);
   return 0;
}

int* save_number(int *int_number)
{
   int i, *divisors = 0, divisor_number;
   divisors = calloc(divisor_number, sizeof *divisors);

   scanf("%d", int_number);

   for (i = 1; i <= *int_number; ++i)
   {
      if (*int_number % i == 0)
      {
         divisors[i-1] = i;
         printf("%d\n", divisors[i-1]);
         divisor_number++;
      }
      continue;
   }

   return divisors;
}
#包括
#包括
#包括
整数*保存整数(整数*整数);
int main()
{
整数,*除数=0,偶数,i;
printf(“请输入一个整数:”);
除数=save_number(&int_number);

对于(i=1;i只需在这里留下有效的最终代码,同时考虑@WeatherVane comment,并确保初始化了除数

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


int* save_number(int *int_number);

int main()
{
   int int_number, *divisors = 0, count_even, i;

   printf("Please enter an integer:");
   divisors = save_number(&int_number);

   for (i = 1; i <= int_number; ++i)
   {
      if (divisors[i-1] % 2 == 0 && divisors[i-1] != 0)
      {
         printf("%d\n", divisors[i-1]);
         count_even++;
      }
      continue;
   }
   printf("Total even divisors: ");
   printf("%d\n", count_even);
   return 0;
}

int* save_number(int *int_number)
{
   int i, *divisors = 0, divisor_number = 0;
   divisors = calloc(divisor_number, sizeof *divisors);

   scanf("%d", int_number);

   for (i = 1; i <= *int_number; ++i)
   {
      if (*int_number % i == 0)
      {
         divisors[i-1] = i;
         printf("%d\n", divisors[i-1]);
         divisor_number++;
      }
      continue;
   }

   return divisors;
}
#包括
#包括
#包括
整数*保存整数(整数*整数);
int main()
{
整数,*除数=0,偶数,i;
printf(“请输入一个整数:”);
除数=save_number(&int_number);

对于(i=1;i在第一种情况下

divisors = calloc(divisor_number, sizeof *divisors);
divisors[i-1] = i;
您分配了一个大小为“未初始化”的内存块。它可以是小的或大的,但它的大小可以是任何大小。它可能是意外工作的,因为初始化值恰好是一些大的值。它仍然是不正确的代码,您不应该这样做

在第二种情况下,当你

divisors = calloc(divisor_number, sizeof *divisors);
divisors[i-1] = i;
除数尚未初始化。因此它将尝试写入随机内存位置。这样“意外工作”的可能性就小得多。大多数内存地址都将无效,并且会立即发生崩溃,概率很高

正确的方法是计算所需的内存,然后分配内存,然后写入内存


或者,分配比您知道需要的更多的内容,然后写入。

您的代码中有几个未定义的行为:

calloc(除数_数,sizeof*除数)


当除数
尚未初始化时,使用分配的块

在您的第二个方案中,也在写入块后分配块

如果用户未输入有效整数,则不初始化
*int\u number

如果无法分配块(独立于前一个UB)


您可以轻松删除这些UBs

您知道需要保存的数字元素,因为它是您读取的数字的值

在第一个代码中,替换

inti,*除数;
如果((scanf(“%d”,整数)!=1)| |(*整数<1)){
…表示错误
出口(1);
}
if((除数=calloc(*int_number,sizeof*除数))==NULL){
…表示错误
出口(1);
}
...

我还鼓励您删除循环中无用的
continue;

两个版本的代码都有错误

在第一个版本中,您有以下内容:

int i, *divisors = 0, divisor_number;
divisors = calloc(divisor_number, sizeof *divisors);

这里,
除数\u number
未初始化,因此其值是不确定的这是可行的,要么是因为碰巧传递给
calloc
的值足够大,无法分配足够的内存,要么是因为它不够大,并且您通过写入已分配内存的末尾来设法不破坏任何内容

在第二段代码中:

     divisors[i-1] = i;
     printf("%d\n", divisors[i-1]);
     divisor_number++;

此时,
divisiors
被设置为NULL,因此您正在取消对NULL指针的引用,这也是未定义的行为。此外,与第一个代码一样,
divisior\u number
未初始化,但您尝试增加它

您发布的答案也不起作用,因为您告诉
calloc
分配0字节的内存,所以对分配数组元素的任何访问都会读取到末尾

相反,您应该使用读入
int\u number
的值来分配内存,然后立即调用
calloc
。这是您实际需要的空间量

int* save_number(int *int_number)
{
   int i, *divisors = 0;

   scanf("%d", int_number);
   divisors = calloc(*int_number, sizeof *divisors);

   for (i = 1; i <= *int_number; ++i)
   {
      if (*int_number % i == 0)
      {
         divisors[i-1] = i;
         printf("%d\n", divisors[i-1]);
      }
   }

   return divisors;
}
int*保存编号(int*int\u编号)
{
整数i,*除数=0;
scanf(“%d”,整数);
除数=calloc(*整数,sizeof*除数);

for(i=1;i)两个代码都是错误的,因为
除数
(传递给
calloc
)从未初始化。它们都有未定义的行为,并且这是一种抽签,如果有,则按预期工作。“除数(u)的值只有在我执行for循环后才知道”在第二个版本中,你有UB,因为当你索引它时,
除数
没有指向循环中的有效内存。
calloc
分配内存,在你已经尝试使用所述内存之后,你不能分配内存。你必须先分配它。@WeatherVane-在第一段中:
*除数=0
初始化指针,然后
除数=save_number(&int_number)
返回分配的内存。UB在第一段中的位置是什么?“运行第二段代码时,我没有收到任何错误或警告。”,请查看valgrind以查看两个版本执行时的错误(除数,sizeof*除数)
除数=0
分配一个块大小为0的
除数
在两个代码中都是未初始化的。在第二个代码中,除数是递增的,但不少于未定义的。这一点很好,但在第二种情况下,崩溃甚至会在使用
除数
之前发生。警告必须是
calloc(*int_number…
这就是我在回答中所做的(事实上,我添加了测试以避免UB,如果无效的数字等);
int i, *divisors = 0, divisor_number;
divisors = calloc(divisor_number, sizeof *divisors);
     divisors[i-1] = i;
     printf("%d\n", divisors[i-1]);
     divisor_number++;
int* save_number(int *int_number)
{
   int i, *divisors = 0;

   scanf("%d", int_number);
   divisors = calloc(*int_number, sizeof *divisors);

   for (i = 1; i <= *int_number; ++i)
   {
      if (*int_number % i == 0)
      {
         divisors[i-1] = i;
         printf("%d\n", divisors[i-1]);
      }
   }

   return divisors;
}