C 无法在 while 循环工作和 "double free or corruption" 错误中获取数组中的搜索编号

C cannot get search number in array inside while loop to work and "double free or corruption" error

我是C编程的菜鸟,很抱歉这个问题本来可以很简单,但是我找了一个下午都没有解决这个问题。 我正在尝试编写一个生成随机数的程序,检查它是否已存在于文件 number.txt 中包含的列表中。最后,程序必须询问您是否要提取另一个数字,如果是,则重新 运行 它。 我尝试了各种 for 和 while 循环,但其中 none 有效:列表中的数字经常被提取,​​怎么了?

此外,有时,在多次迭代后,程序会因错误而停止 "double free or corruption (! prev)",是什么原因造成的?

#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);

}

你的代码有几个问题

1)printf("ERROR Creating File!");是个坏消息,你不要尝试创建文件,你打开它阅读里面

2) num[5] = 0; 具有未定义的行为,因为您写出了大小为 5 (int num[5];)

num

3) free(filer); filer 是一个 FILE *,行为未定义,您想做什么?这很可能是你的原因:

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

4) get = 0; 没用,你之前不要用 get 再赋值 rand()

5) r = 0; 也没有用,因为你在做 scanf (" %c", &r);

之前没有使用 r

6) srand(time(0)); 必须在程序开始时只执行一次,而不是多次,因为如果你在同一秒内执行两次,rand() 将 return 相同的值。

7) 你会

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

对于每个 do .. while 但是你永远不会回到文件的开头,所以每次你前进并且你不检查文件的结尾。当您到达文件末尾时,fscanf(filer, "%d\n", &num[n]); 什么都不做,num 不变。

你只需要在执行开始时从文件中读取一次数字

8) 你问 rand() 到 return 一个介于 1 和 27 之间的值,所以只有很少的可能性,这可能就是你有 :[=27 的原因=]

numbers in the list are often extracted.


这里建议考虑到备注(除了最后一个关于取值范围),numbers.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

好的,我研究了你的代码,我或多或少地理解了你的程序做了什么以及如何分配变量,但这是我现在的问题: 我将数组的维度设置为五,因为我只想读取 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
    }
}

(added else) 仅适用于数组中的第一个数字,但如果提取一个数字,即数组中的第二个、第三个...(加载到数组中,因为我可以打印它) 程序看不到它。

@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]='[=10=]';
    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]='[=10=]';
    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);
}