Почему callo c работает только в определенных c частях кода? - PullRequest
0 голосов
/ 19 июня 2020

Почему первый код работает нормально, а второй нет? Единственная разница в том, где я помещаю calloc во вторичную функцию. Причина, по которой я спрашиваю, заключается в том, что я мог подумать, что второй код - это тот, который работает, учитывая, что значение divisor_number известно только после того, как я сделаю для l oop во вторичной функции. Не могли бы вы дать мне быстрое объяснение по этому поводу? Спасибо!

Кроме того, я не получаю никаких ошибок или предупреждений при запуске второго кода. Есть ли способ получить дополнительную информацию от компилятора, которая направит меня в правильном направлении? Спасибо!

Nb Прошу прощения за то, что код не очень организован с логической точки зрения, просто работаю над ним, чтобы лучше asp получить некоторые концепции программирования.

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;
}

2-й код:

#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;
   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;
   }
   divisors = calloc(divisor_number, sizeof *divisors);
   return divisors;
}

Ответы [ 4 ]

0 голосов
/ 19 июня 2020

Обе версии кода содержат ошибки.

В первой версии у вас есть это:

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

Здесь divisor_number не инициализирован, поэтому его значение неопределенно . Простое использование значения вызывает неопределенное поведение . Вам «повезло», что все работает, либо потому, что значение, которое случайно было передано в calloc, было достаточно большим, чтобы выделить достаточно памяти, либо потому, что оно недостаточно велико, и вам удалось ничего не сломать, написав за конец выделенной памяти.

Во втором коде:

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

В этот момент divisiors установлен в NULL, поэтому вы разыменовываете указатель NULL, что также является неопределенным поведением. Кроме того, как и в первом коде, divisor_number не инициализирован, но вы пытаетесь его увеличить.

Ваш опубликованный ответ также не работает, потому что вы говорите calloc выделить 0 байт памяти, поэтому любой доступ к элементу выделенного массива - это чтение после конца.

Вместо этого вы должны использовать значение, считанное в int_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;
}
0 голосов
/ 19 июня 2020

В первом случае, когда вы делаете

divisors = calloc(divisor_number, sizeof *divisors);

, вы выделяете блок памяти размером «неинициализированный». Он может быть маленьким или большим, но его размер может быть любым. Вероятно, это сработает случайно, потому что инициализированное значение оказывается чем-то огромным. Это все еще неправильный код, и вам не следует этого делать.

Во втором случае, когда вы делаете

divisors[i-1] = i;

делители еще не инициализированы. Таким образом, он попытается записать в произвольную область памяти. У этого гораздо меньше шансов «сработать случайно». Большая часть адресов памяти будет недействительной, и вы сразу получите ошибку sh с высокой вероятностью.

Правильный способ - подсчитать необходимую память, затем выделить ее и записать в нее.

Или выделите больше, чем вы знаете, что вам нужно, а затем напишите на него.

0 голосов
/ 19 июня 2020

В вашем коде есть несколько неопределенных вариантов поведения:

Doing

callo c (divisor_number, sizeof * divisors);

while divisor_number еще не инициализирован, затем с использованием выделенного блока

В вашем втором предложении также выделяется блок после записи в него.

В случае, если пользователь не введите действительное целое число, пусть *int_number не инициализировано.

Если вы не можете выделить блок (независимо от предыдущего UB)


Вы можете легко удалить эти UB.

Вы знаете числовые элементы, которые вам нужно будет сохранить, потому что это значение числа, которое вы читаете

В первом коде замените

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

 scanf("%d", int_number);

на

int i, *divisors;

if ((scanf("%d", int_number) != 1) || (*int_number < 1)) {
  ..indicate error
  exit(1); 
}
if ((divisors = calloc(*int_number, sizeof *divisors)) == NULL) {
  ..indicate error
  exit(1); 
}

Во втором коде замените

int i, *divisors = 0, divisor_number;
scanf("%d", int_number);
...

divisors = calloc(divisor_number, sizeof *divisors);

на

int i, *divisors;

if ((scanf("%d", int_number) != 1) || (*int_number < 1)) {
  ..indicate error
  exit(1); 
}
if ((divisors = calloc(*int_number, sizeof *divisors)) == NULL) {
  ..indicate error
  exit(1); 
}

...

Я также призываю вас удалить ненужные continue; в петли

0 голосов
/ 19 июня 2020

Просто оставьте здесь последний работающий код, также учитывая комментарий @WeatherVane и убедитесь, что divisor_number инициализирован.

#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;
}
...