我正在使用C语言和Cygwin终端编写打字游戏。
我已经从.txt文件中读取了1000个单词,然后打印了一个随机单词。我需要在二维数组框“游戏板”中打印此随机词]
Link to: Image of current output. Need to move word from outside of box to inside of box.
我如何在方框内打印随机单词?
单词需要出现在框的第一行中的任意水平位置。
注意:当我说盒子时,我是指由短划线和星号组成的20(高)乘80(宽)的盒子。
任何帮助将不胜感激。预先非常感谢。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
void main(){
int g, h; //index for use in for loop
//creates box
const int boxLength = 20;
const int boxWidth = 75;
char box[boxLength][boxWidth];
for(int g = 0; g < boxLength; g++){
for(int h = 0; h < boxWidth; h++){
if(g == 0 || g == boxLength - 1)
box[g][h] = '-';
else if(h == 0 || h == boxWidth - 1)
box[g][h] = '|';
else
box[g][h] = ' ';
}
}
FILE *myFilePointer2 = fopen("wordList.txt", "r");
srand(time(0));
int size = 1000;
if(myFilePointer2 == NULL){
printf("Unable to open file wordList.txt");
exit(0);
}
char** words = (char**)malloc(sizeof(char**)*size); //2d pointer array, dynamically allocated, to store words from text file
char wordBankArray[1050];//wordBankArray
int wordQuantity = 0;
while(fgets(wordBankArray, 1050, myFilePointer2) != NULL){// read data from myFilePointer line by line and store it into words array
words[wordQuantity] = (char*)malloc(sizeof(char)*(strlen(wordBankArray)+1)); //dynamically allocates memory for words array
strcpy(words[wordQuantity], wordBankArray); //copying words from text file to wordBankArray
wordQuantity++;
}
printf("Randomly generated word from .txt file: ");
int index = rand()%wordQuantity; // psuedo randomly generates an index in range of 0 to wordQuantity)
printf("%s\n", words[index]); //prints randomly generated word from index
for(int g = 0; g < boxLength; g++){ //prints 2d box
for(int h = 0; h < boxWidth; h++){
printf("%c", box[g][h]);
}
printf("\n");
fclose(myFilePointer2); //close file for reading
}
}
[您有许多小错误,如果您正在读取具有1000个单词的文件,但仅分配100个指针,则将调用未定义的行为,尝试写入不存在的指针。
fclose()
循环内不需要for(int g = 0; g < boxLength; g++){
(关闭文件boxlength
的次数)。
如果定义const int ...
,那么您正在创建2D VLA(可变长度数组),这很好,但是如果在编译之前就知道边界,则改为使用#define
声明常量,并避免使用VLA而不是在C89 / 90中存在,在C99中引入,并在C11中成为可选功能。
您还应该在编译字符串中添加-Wshadow
,然后对变量进行阴影处理:
int g, h; //index for use in for loop
声明循环变量时:
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 Program startup(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
)当使用C中的malloc
,calloc
或realloc
动态分配内存时,不需要强制转换malloc
的返回值,这是不必要的。参见:Do I cast the result of malloc?。如果使用取消引用的指针来设置类型大小,那么您永远不会出错。您可以使用以下方法为words
分配指针:
您必须验证每个分配)if (!(words = malloc (nptrs * sizeof *words))) { /* allocate/validate */ perror ("malloc-words"); return 1; }
([note:
[注意,您只分配了初始WRDSZ
(100
)指针。如果文件有1000个字,则必须跟踪填充的指针数(wordQuantity
),还必须跟踪分配的指针数(例如nptrs
)。当wordQuantity == nptrs
时,您必须realloc
通过words
可用的指针数,然后再尝试使用另一个指针(通常将当前分配的数字加倍是合理的增长方案)。添加其他测试和重新分配,您的读取循环将变为:
您只需要调用一次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)不再需要它时,可以将其freed。必须使用内存错误检查程序,以确保您不尝试访问内存或不要在分配的块的边界之外/之外写,尝试读取或基于未初始化的值进行条件跳转,最后,确认您释放了已分配的所有内存。
对于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)
始终确认已释放已分配的所有内存,并且没有内存错误。
仔细检查,如果还有其他问题,请告诉我。
您可以尝试这样的事情: