Я новичок в программировании на 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);
}
Почему free(filer);?
num[5] обращается за пределами массива (индексируется от 0 до 4)
Спасибо, Фил М, счет начинается с 0!





В вашем коде есть несколько проблем
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 уверен, что сможешь
1) я не могу понять, что делает ваш первый цикл while 2) что означает for (;;)? 3) в вашем цикле for я не могу понять, как переменная i получает числа, содержащиеся в файле, и считывается в цикле while (я думаю), и поэтому, как программа знает, какая строка содержит извлеченный номер, если он присутствует в number.txt
@ caesar753 пока в начале считывает все целые числа из файла, в то время как сканф считывает целое число и возвращает 1 (поскольку формат обрабатывает только% d, поэтому 1 элемент). Чтобы быть совместимым с любым количеством чисел в файле, я не использую массив с фиксированным размером, а перераспределяю, чтобы увеличить его размер, роюсь в Интернете о перераспределение
@ caesar753 for(;;) похож на for(;true;) или while(1), он зацикливается без условий, для выхода необходимо выполнить ломать (или, конечно, возврат)
@caesar753 яя> — это просто индекс в массиве, nums[i] — одно из прочитанных чисел. Программа не знает строку, в которой было число, только ранг числа. Как вы видите в моих файлах все числа находятся в одной строке, но использование нескольких строк ничего не меняет ни в коде, ни в исполнении, фсканф обходит символы новой строки
@ caesar753 при необходимости выполните программу в отладчике шаг за шагом, если вы все еще не понимаете, и посмотрите, что происходит / меняются переменные
Хорошо, я изучил ваш код и более или менее понял, что делает ваша программа и как назначаются переменные, но теперь это моя проблема: я установил размерность массива в пять, потому что я хочу прочитать только последнее (или первое, пока) число из пяти в 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 каждое число расположено в новой строке.
Хорошо, моя программа выросла и теперь имеет много новых функций:
и это код (комментарии на итальянском языке)
#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);
}
Вы также можете опубликовать вход
numbers.txt?