У вас есть несколько мелких ошибок, и если вы читаете файл с 1000 словами, но выделяете только 100 указателей - вы вызываете неопределенное поведение, пытаясь записать указатели, которые не существуют.
Вы не можете Вам не нужно fclose()
внутри вашего for(int g = 0; g < boxLength; g++){
l oop (который закрывает файл boxlength
количество раз).
Если вы определяете const int ...
, то вы создаете 2D VLA (массив переменной длины), что хорошо, но если границы известны до времени компиляции, вместо этого объявите константы с #define
и избегайте VLA, который не существует в C89 / 90, был представлен в C99 и стал дополнительной функцией в C11.
Вы также должны добавить -Wshadow
к вашей строке компиляции, вы скрываете переменные:
int g, h; //index for use in for loop
Когда вы объявляете переменные l oop:
for(int g = 0; g < boxLength; g++){
for(int h = 0; h < boxWidth; h++){
...
(будьте осторожны с объявлениями переменных - здесь вы позже не будете использовать g
или h
, но в других теневые переменные могут иметь тяжелые последствия)
Имея это в виду, вы можете определить необходимые константы как:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#define BXLEN 20 /* if you need a constant, #define one (or more) */
#define BXWDT 75
#define WRDSZ 100
#define ARRSZ 1050
(вы можете выбрать любые имена, которые вам нравятся)
Если вы не работаете в автономной среде (без операционной системы), ваше объявление void main()
неверно. В реализации, соответствующей стандартам, допустимыми декларациями для main
для являются int main (void)
и int main (int argc, char *argv[])
(которые вы увидите написанными с эквивалентом char **argv
). См .: C11 Standard - §5.1.2.2.1 Запуск программы (p1) .
Аргументы int argc, and char **argv
позволяют передавать информацию в вашу программу в командной строке. Не кодируйте имена файлов жестко. (если вы находитесь во встроенной системе - это исключение, поскольку у вас может не быть возможности передать имя файла в командной строке) В противном случае вы можете сделать:
int main (int argc, char **argv)
{
int nptrs = WRDSZ, index, wordQuantity = 0; /* declare/initialize vars */
char box[BXLEN][BXWDT] = {{0}},
**words = NULL,
wordBankArray[ARRSZ] = "";
FILE *fp = NULL;
if (argc < 2 ) { /* validate 1 argument given for filename */
fprintf (stderr, "error: insufficient input,\n"
"usage: %s filename\n", argv[0]);
return 1;
}
/* open file/validate file open for reading */
if ((fp = fopen (argv[1], "r")) == NULL) {
perror ("fopen-argv[1]");
return 1;
}
( note: Я сократил myFilePointer2
до fp
)
Когда вы динамически выделяете память с malloc
, calloc
или realloc
в C, нет необходимости приводить к возврату malloc
, это не нужно. Смотрите: Я разыгрываю результат Малло c? . Если вы используете разыменованный указатель для установки размера - вы никогда не ошибетесь. Вы можете назначить указатели для words
с помощью:
if (!(words = malloc (nptrs * sizeof *words))) { /* allocate/validate */
perror ("malloc-words");
return 1;
}
( примечание: вы должны проверить каждое выделение)
Примечание выше, вы только выделяете начальный WRDSZ
(100
) указатели. Если ваш файл содержит 1000 слов, вы должны отслеживать количество заполненных указателей (ваш wordQuantity
) и отслеживать количество назначенных указателей (скажем, с nptrs
). Когда wordQuantity == nptrs
, вы должны realloc
количество указателей, доступных через words
, прежде чем пытаться использовать другой указатель (обычно удвоение текущего выделенного числа является разумной схемой роста). При добавлении дополнительного теста и перераспределения ваше чтение l oop станет:
while (fgets (wordBankArray, ARRSZ, fp) != NULL) { /* read each line in file */
size_t len; /* save length, then memcpy */
if (wordQuantity == nptrs) { /* check if all pointers used - realloc */
/* always realloc using a temporary pointer -- not the pointer itself */
void *tmp = realloc (words, 2 * nptrs * sizeof *words);
if (!tmp) { /* validate realloc succeeds */
perror ("realloc-words");
break; /* don't exit, original words pointer still valid */
}
words = tmp; /* assign reallocated block to original pointer */
nptrs *= 2; /* update number of pointers allocated */
}
if (!(words[wordQuantity] = malloc ((len = strlen (wordBankArray)) + 1))) {
perror ("malloc-words[wordQuantity]");
return 1;
}
memcpy (words[wordQuantity], wordBankArray, len + 1);
wordQuantity++;
}
fclose (fp);
( note: , вам нужно всего лишь один раз вызвать strlen()
, сохранить размер и затем скопировать строка с memcpy()
. Если вы звоните strcpy()
, вы просто сканируете конец строки, который у вас уже есть, от вашего звонка до strlen()
)
После того, как вы закончили чтение, затем позвоните fclose()
в этот момент. Также обратите внимание, что sizeof (char)
равно 1
и должно быть опущено при умножении вашего размера. Остальная часть того, что у вас есть, напечатает коробку и выведет случайную строку, но обратите внимание, что в выходных данных нет преобразования, нет необходимости вызывать printf
. puts
или fputs
подойдет (хороший компилятор сделает это за вас за кулисами)
То, что вы упускаете, освобождает выделенную вами память. Для этого вы можете сделать:
for (int i = 0; i < wordQuantity; i++) /* free allocated strings */
free (words[i]);
free (words); /* free pointers */
Если вы поставите это в целом, вы можете сделать:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#define BXLEN 20 /* if you need a constant, #define one (or more) */
#define BXWDT 75
#define WRDSZ 100
#define ARRSZ 1050
int main (int argc, char **argv)
{
int nptrs = WRDSZ, index, wordQuantity = 0; /* declare/initialize vars */
char box[BXLEN][BXWDT] = {{0}},
**words = NULL,
wordBankArray[ARRSZ] = "";
FILE *fp = NULL;
if (argc < 2 ) { /* validate 1 argument given for filename */
fprintf (stderr, "error: insufficient input,\n"
"usage: %s filename\n", argv[0]);
return 1;
}
for (int g = 0; g < BXLEN; g++) { /* initialize box */
for (int h = 0; h < BXWDT; h++) {
if (g == 0 || g == BXLEN - 1)
box[g][h] = '-';
else if (h == 0 || h == BXWDT - 1)
box[g][h] = '|';
else
box[g][h] = ' ';
}
}
/* open file/validate file open for reading */
if ((fp = fopen (argv[1], "r")) == NULL) {
perror ("fopen-argv[1]");
return 1;
}
srand (time (NULL)); /* seed rand generator */
if (!(words = malloc (nptrs * sizeof *words))) { /* allocate/validate */
perror ("malloc-words");
return 1;
}
while (fgets (wordBankArray, ARRSZ, fp) != NULL) { /* read each line in file */
size_t len; /* save length, then memcpy */
if (wordQuantity == nptrs) { /* check if all pointers used - realloc */
/* always realloc using a temporary pointer -- not the pointer itself */
void *tmp = realloc (words, 2 * nptrs * sizeof *words);
if (!tmp) { /* validate realloc succeeds */
perror ("realloc-words");
break; /* don't exit, original words pointer still valid */
}
words = tmp; /* assign reallocated block to original pointer */
nptrs *= 2; /* update number of pointers allocated */
}
if (!(words[wordQuantity] = malloc ((len = strlen (wordBankArray)) + 1))) {
perror ("malloc-words[wordQuantity]");
return 1;
}
memcpy (words[wordQuantity], wordBankArray, len + 1);
wordQuantity++;
}
fclose (fp);
fputs ("Randomly generated string from list : ", stdout);
index = rand() % wordQuantity;
printf ("%s\n", words[index]);
for (int g = 0; g < BXLEN; g++) {
for (int h = 0; h < BXWDT; h++) {
printf ("%c", box[g][h]);
}
putchar ('\n');
}
for (int i = 0; i < wordQuantity; i++) /* free allocated strings */
free (words[i]);
free (words); /* free pointers */
}
Пример использования / Вывод
Для файла данных dat/1kfnames.txt
я просто перенаправил 1000 имен файлов в файл, чтобы они служили словами:
$ ./bin/wordinbox dat/1kfnames.txt
Randomly generated string from list : str_printf_null.c
---------------------------------------------------------------------------
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
---------------------------------------------------------------------------
Проверка использования памяти / ошибок
В любом написанном вами коде, который динамически распределяет память, у вас есть 2 обязанностей относительно любого выделенного блока памяти: (1) всегда сохраняет указатель на начальный адрес для блока памяти, таким образом, (2) он может быть освобожден , когда он больше не нужен.
Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы гарантировать, что вы не пытаетесь получить доступ к памяти или написать за пределами / за пределами выделенного блока, попытаться прочитать или основать условный переход на неинициализированном значении и, наконец, подтвердить, что вы освобождаете всю выделенную память.
Для Linux valgrind
это нормальный выбор. Есть похожие проверки памяти для каждой платформы. Все они просты в использовании, просто запустите вашу программу через нее.
$ valgrind ./bin/wordinbox dat/1kfnames.txt
==28127== Memcheck, a memory error detector
==28127== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==28127== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==28127== Command: ./bin/wordinbox dat/1kfnames.txt
==28127==
Randomly generated string from list : tor.c
---------------------------------------------------------------------------
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
---------------------------------------------------------------------------
==28127==
==28127== HEAP SUMMARY:
==28127== in use at exit: 0 bytes in 0 blocks
==28127== total heap usage: 1,008 allocs, 1,008 frees, 45,566 bytes allocated
==28127==
==28127== All heap blocks were freed -- no leaks are possible
==28127==
==28127== For counts of detected and suppressed errors, rerun with: -v
==28127== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.