C не может получить номер поиска в массиве внутри цикла while для работы и ошибка «двойное освобождение или повреждение»

Я новичок в программировании на C, и я прошу прощения за этот вопрос, который может быть очень простым, но я не могу решить эту проблему после поиска весь день. Я пытаюсь написать программу, которая генерирует случайное число, проверяя, что его еще нет в списке, содержащемся в файле number.txt. В конце программа должна спросить, хотите ли вы извлечь еще один номер и, если да, запустить ее повторно. Я пробовал различные циклы for и while, но ни один из них не работал: числа в списке часто извлекаются, что не так?

Более того, иногда после различных итераций программа останавливается с ошибкой "двойное освобождение или повреждение (! предыдущее)", чем это вызвано?

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define rangeMAX 27 //Upper limit of range.
#define rangeMIN 1  //Lower limit of range.


int main()
{
  int get, i, n;
  int num[5];
  char r;
  FILE *filer;
    filer = fopen("numbers.txt", "r");
    printf("When you are ready press any key to continue\n");
    getchar();
    if (filer == NULL)
    {
        printf("ERROR Creating File!");
        exit(1);
    }
    do {
        num[5] = 0;
        n = 0;
        i = 0;
        free(filer);
        get = 0;
        r = 0;
            srand(time(0)); // this will ensure that every time, program will generate different set of numbers. If you remove this, same set of numbers will generated every time you run the program.
            get = ((rand() % (rangeMAX-rangeMIN+1)) + rangeMIN); // generate random number.
        for (n = 0; n < 5; n++){
        fscanf(filer, "%d\n", &num[n]);
        }
            for (n = 0; n < 5; n++){
                if (get == num[n]){
                printf("false\n");
                printf("%d\n", n);
                break;
                }
            }
                i=get;
                printf("%d\n",i);
    printf ("Do you want another number? Y/N ");
    scanf (" %c", &r);
    } while (r == 'y' || r == 'Y');
    return(0);

}

Вы также можете опубликовать вход numbers.txt?

stensal 27.02.2019 22:54

Почему free(filer);?

dbush 27.02.2019 22:55
num[5] обращается за пределами массива (индексируется от 0 до 4)
Phil M 27.02.2019 22:59

Спасибо, Фил М, счет начинается с 0!

caesar753 01.03.2019 18:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
68
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

В вашем коде есть несколько проблем

1) printf("ERROR Creating File!"); это плохое сообщение, вы не пытаетесь создать файл, вы открываете его, чтобы прочитать внутри

2) num[5] = 0; имеет неопределенное поведение, потому что вы пишете из число, размер которого равен 5 (int num[5];)

3) free(filer);файлер — это FILE *, поведение не определено, что вы хотели сделать? Это, вероятно, причина вашего:

Moreover, sometimes, after various iterations, the program stops with the error "double free or corruption (! prev)", what causes it?

4) get = 0; бесполезно, вы не используете получить, прежде чем присвоить его снова с результатом ранд()

5) r = 0; тоже бесполезно, потому что вы не используете р перед тем, как сделать scanf (" %c", &r);

6) srand(time(0)); это нужно сделать только один раз в начале программы, а не несколько раз, потому что если вы сделаете два раза в одну и ту же секунду, rand() вернет одно и то же значение.

7) ты делаешь

    for (n = 0; n < 5; n++){
    fscanf(filer, "%d\n", &num[n]);
    }

для каждого do .. while, но вы никогда не возвращаетесь к началу файла, поэтому каждый раз, когда вы продвигаетесь вперед, вы не проверяете конец файла. Когда вы достигаете конца файла, fscanf(filer, "%d\n", &num[n]); ничего не делает, и число не меняется.

Вам просто нужно прочитать числа из файла только один раз в начале выполнения

8) Вы просите ранд() вернуть значение от 1 до 27, так что только несколько возможностей, возможно, поэтому у вас есть:

numbers in the list are often extracted.


Здесь предложение с учетом замечаний (кроме последнего о диапазоне значений), числа.txt не ограничивается 5 числами.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define rangeMAX 27 //Upper limit of range.
#define rangeMIN 1  //Lower limit of range.

int main()
{
  srand(time(0)); // this will ensure that every time, program will generate different set of numbers. If you remove this, same set of numbers will generated every time you run the program.

  FILE * filer = fopen("numbers.txt", "r");

  if (filer == NULL)
  {
    printf("ERROR cannot read numbers.txt");
    exit(1);
  }

  int * nums = NULL;
  int num;
  size_t sz = 0, nnums = 0;

  while (fscanf(filer, "%d", &num) == 1) {
    if (nnums == sz) {
      sz += 100;
      nums = realloc(nums, sz * sizeof(int));
    }
    nums[nnums++] = num;
  }
  fclose(filer);

  printf("When you are ready press any key to continue\n");
  getchar();

  char yn[16];

  do {
    int get = ((rand() % (rangeMAX-rangeMIN+1)) + rangeMIN); // generate random number.
    size_t i = 0;

    for (;;) {
      if (i == nnums) {
        printf("%d is not in the file\n", get);
        break;
      }
      if (get == nums[i]) {
        printf("%d is the number rank %d in the file\n", get, i + 1);
        break;
      }
      i += 1;
    }

    printf ("Do you want another number? Y/N ");
    if (scanf ("%15s", yn) != 1)
      break;
  } while (*yn == 'y' || *yn == 'Y');

  return(0);
}

Компиляция и исполнение:

pi@raspberrypi:/tmp $ gcc -pedantic -Wall -g r.c
pi@raspberrypi:/tmp $ cat numbers.txt 
1 3 7 9 10 20 23
pi@raspberrypi:/tmp $ ./a.out
When you are ready press any key to continue

12 is not in the file
Do you want another number? Y/N Y
12 is not in the file
Do you want another number? Y/N Y
4 is not in the file
Do you want another number? Y/N Y
3 is the number rank 2 in the file
Do you want another number? Y/N Y
12 is not in the file
Do you want another number? Y/N Y
15 is not in the file
Do you want another number? Y/N Y
16 is not in the file
Do you want another number? Y/N Y
8 is not in the file
Do you want another number? Y/N N

Ууууу! Ваш код делает больше вещей, чем я могу себе представить! Большое спасибо! Изучу и если будет что-то неясно спрошу еще раз!

caesar753 28.02.2019 00:37

@ caesar753 уверен, что сможешь

bruno 28.02.2019 20:33

1) я не могу понять, что делает ваш первый цикл while 2) что означает for (;;)? 3) в вашем цикле for я не могу понять, как переменная i получает числа, содержащиеся в файле, и считывается в цикле while (я думаю), и поэтому, как программа знает, какая строка содержит извлеченный номер, если он присутствует в number.txt

caesar753 28.02.2019 20:45

@ caesar753 пока в начале считывает все целые числа из файла, в то время как сканф считывает целое число и возвращает 1 (поскольку формат обрабатывает только% d, поэтому 1 элемент). Чтобы быть совместимым с любым количеством чисел в файле, я не использую массив с фиксированным размером, а перераспределяю, чтобы увеличить его размер, роюсь в Интернете о перераспределение

bruno 28.02.2019 20:54

@ caesar753 for(;;) похож на for(;true;) или while(1), он зацикливается без условий, для выхода необходимо выполнить ломать (или, конечно, возврат)

bruno 28.02.2019 20:56

@caesar753 яя> — это просто индекс в массиве, nums[i] — одно из прочитанных чисел. Программа не знает строку, в которой было число, только ранг числа. Как вы видите в моих файлах все числа находятся в одной строке, но использование нескольких строк ничего не меняет ни в коде, ни в исполнении, фсканф обходит символы новой строки

bruno 28.02.2019 20:58

@ caesar753 при необходимости выполните программу в отладчике шаг за шагом, если вы все еще не понимаете, и посмотрите, что происходит / меняются переменные

bruno 28.02.2019 21:03

Хорошо, я изучил ваш код и более или менее понял, что делает ваша программа и как назначаются переменные, но теперь это моя проблема: я установил размерность массива в пять, потому что я хочу прочитать только последнее (или первое, пока) число из пяти в number.txt (которое должно быть извлечено последним, потому что на следующем шаге я хотел бы добавить каждое извлеченное число в этом файле), поэтому мне удалось изменить ваш код, чтобы сделать это (просто изменил первый цикл while), и это работает нормально. Чего я не могу решить, так это почему ваш цикл for(;;) отлично работает, а мой код нет, поэтому в моем коде

for (n = 0; n < sizeof(num[5]); n++){
        if (get == num[n]){
        printf("false\n");
        printf("%d\n", n);
        break;
        }
else{
        printf("true");
        break
    }
}

(добавлено еще) работает только для первого числа в массиве, но если извлекается число, которое является вторым, третьим... в массиве (которые загружаются в массив, потому что я могу его распечатать) программа не не вижу.

@stensal В моем файле numbers.txt каждое число расположено в новой строке.

Хорошо, моя программа выросла и теперь имеет много новых функций:

  1. можно выбрать файл, из которого загружается список имен
  2. вы можете выбрать файл, содержащий числа, которые были извлечены в предыдущих сеансах
  3. вы можете добавить номера учеников, которые не присутствовали на прошлом уроке или у которых есть обоснование того, что он не учился на уроке (эта программа нужна мне для вызова учеников на уроке)
  4. Если число уже было извлечено, программа пытается выполнить второе, а затем третье извлечение.

и это код (комментарии на итальянском языке)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#define rangeMAX 27 //Upper limit of range.
#define rangeMIN 1  //Lower limit of range.

int main()
{
  srand(time(0)); // this will ensure that every time, program will generate different set of numbers. If you remove this, same set of numbers will generated every time you run the program.

 char elenco[20], filelenco[30];
    printf("inserire il nome del file elenco: ");
    fgets(elenco, 20, stdin);
    elenco[strlen(elenco)-1]='\0';
    sprintf(filelenco, "%s.txt", elenco);

//lettura dell'elenco dei nomi
char nomi[27][20];
FILE * nomif = fopen(filelenco, "r");

  if (nomif == NULL)
  {
    printf("ERROR impossibile leggere %s",filelenco);
    exit(1);
  }

    size_t g = 0;

        for (g = 0; g <=27; g++) {
                fscanf(nomif, "%s", nomi[g]);
                }


//prompt per la scelta del file in cui leggere i numeri di quelli già chiamati
    char fname[20], filename[30];
    printf("inserire il nome del file: ");
    fgets(fname, 20, stdin);
    fname[strlen(fname)-1]='\0';
    sprintf(filename, "%s.txt", fname);
   FILE * filer = fopen(filename, "r");

  if (filer == NULL)
  {
    printf("ERROR impossibile leggere %s\n", fname);
    exit(1);
  }

//creazione dell'array contenente i numeri di quelli già chiamati
  int * nums = NULL;
  int num;
  size_t sz = 0, nnums = 0;
  char yn[16];
  int c;

  while (fscanf(filer, "%d", &num) == 1) {
    if (nnums == sz) {
      sz += 100;
      nums = realloc(nums, sz * sizeof(int));
    }
    nums[nnums++] = num;
  }
  fclose(filer);

//creazione del file contenente gli assenti della volta precedente e i giustificati
char data[30], nome[20];
char ass[20];
struct tm ora;
time_t now;
now = time(NULL);//epoch time
ora = *(localtime(&now)); //conversione nell'ora locale
strftime(data,30,"%Y%m%d",&ora); //formattazione della data secondo lo standard  YYYYMMDD
sprintf(nome, "%s_giustificati_%s.txt", data, fname); //creazione della stringa da usare come nome file
//printf("%s\n", nome);

FILE * assenti;

    assenti = fopen(nome,  "w+"); //creazione vera e propria del file, con il nome formato al comando precedente
    printf("Inserite i numeri degli assenti/giustificati\n");
    scanf("%[^\n]", ass);
    fprintf(assenti, "0 %s", ass);
    fclose(assenti);

//creazione dell'array degi assenit/giustificati tramite lettura del file appena creato
  assenti = fopen(nome, "r");
  int * nums1 = NULL;
  int num1;
  size_t sz1 = 0, nnums1 = 0;
  int d;

  while (fscanf(assenti, "%d", &num1) == 1) {
    if (nnums1 == sz1) {
      sz1 += 100;
      nums1 = realloc(nums1, sz1 * sizeof(int));
    }
    nums1[nnums1++] = num1;
  }
  fclose(assenti);

//attesa dell'input
  while ((getchar()) != '\n'); 

  printf("Se siete pronti premete un tasto per continuare\n");
  getchar();

//scelta della più grande tra le due variabili nnums (numero di elementi dell'array dei già chiamati) e nnums1 (numero di elementi dell'array degli assenti/giustificati) - serve per le iterazioni del ciclo for all'interno del do...while
    size_t max = ((nnums >= nnums1) ? nnums : nnums1);
//    printf("max %d\n", max);

//somma delle due variabili nnums (numero di elementi dell'array dei già chiamati) e nnums1 (numero di elementi dell'array degli assenti/giustificati)
//  size_t total = nnums + nnums1;
//  printf("total %d\n", total);

  do {
    int get = ((rand() % (rangeMAX-rangeMIN+1)) + rangeMIN); // generate random number.
    size_t i = 0;

//ciclo for che verifica che il numero non sia presente nel file dei già chiamati e in quello degli assenti e giustificati. Solo dietro queste due condizioni estrae il numero
    for (;;) {

    if (i == max) {
        printf("E' stato estratto il %d,\n ovvero\n", get);
    sleep(2);
    printf("%s\n", nomi[get-1]);
    filer = fopen(filename, "a");
    fprintf (filer, "%d\n", get);
    fclose(filer);
        break;
      }

//se il numero è gia presente nell'array dei già chiamati crea un interrupt e fa ripartire il do
      if (get == nums[i]) {
//      printf("%d is the number rank %d in the file\n", get, i + 1);
    printf("prima estrazione: Il %d e' gia' stato chiamato\n", get);
    get = ((rand() % (rangeMAX-rangeMIN+1)) + rangeMIN);
    size_t z = 0;
    for (;;) {
        if (z == max) {
            printf("E' stato estratto il %d,\n ovvero\n", get);
                sleep(2);
                printf("%s\n", nomi[get-1]);
                filer = fopen(filename, "a");
                fprintf (filer, "%d\n", get);
                fclose(filer);
                break;}

        if (get == nums[z]){
            printf("seconda estrazione: Il %d e' gia' stato chiamato\n", get);
                get = ((rand() % (rangeMAX-rangeMIN+1)) + rangeMIN);
                    size_t w = 0;
                    for (;;) {
                            if (w == max) {
                                    printf("E' stato estratto il %d,\n ovvero\n", get);
                                    sleep(2);
                                    printf("%s\n", nomi[get-1]);
                                    filer = fopen(filename, "a");
                                    fprintf (filer, "%d\n", get);
                                    fclose(filer);
                                    break;}

                            if (get == nums[w]){
                        printf("e' stato di nuovo estratto il %d\n", get);
                        break;}

                    w += 1;}
            break;}

        z += 1;}

        break;
    }

//se il numero è presente nell'array degli assenti/giustificati crea un messaggio a video e fa ripartire il do
    if (get == nums1[i]) {
    printf("Il %d, ovvero %s, era assente o e' giustificato\n", get, nomi[get-1]);
    break;
      }

      i += 1;
    }

//scelta se continuare ad estrarre o meno
    printf ("Volete estrarre un altro numero? Y/N ");
    if (scanf ("%15s", yn) != 1)
      break;
  } while (*yn == 'y' || *yn == 'Y');


  return(0);
}

Другие вопросы по теме