c - 发送 "cls"到dos会导致异常

标签 c exception command-line console cmd

由于发布问题,我的上一个问题(与此重复)已关闭。

最后提供了背景知识,以便您可以直接解决问题。

我有一个基于文本的程序来帮助学习词汇或其他任何东西(基本上模拟闪存卡,但会闪现您不经常知道的内容)。在我测试它时,它似乎工作得很好,直到我厌倦了屏幕上不断积压的文本,所以我实现了一个(有点不可移植的)清晰屏幕例程。 然后它开始抛出异常,我放入了各种调试代码来尝试跟踪它。

嗯...我设法将其范围缩小到第 445 行的以下命令:

system("cls");

这个命令怎么会导致异常呢?有人知道解决方法吗? 我在 Windows Vista 和 Windows 7 上的命令提示符下运行了此命令,得到了相同的结果。

完整的源代码,以防有人想自己编译或查看:

#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <process.h>
#include <ctype.h>
#include <string.h>

#define DINPUTFILENAME "vtdb.~sv"
#define DOUTPUTFILENAME "vtdb.~sv"
#define MAXINTVALUE 2147483647
#define MAXTEXTLENGTH 256
#define N2LTONORM 5
#define NORMTON2L 3
#define NORMTOKNOWN 5
#define KNOWNTONORM 2
#define KNOWNTOOLD 2
#define OLDTONORM 1

struct vocab
{
    int index; //identifies the entry in the list, allowing it to be selected by use of a random number
    char * question;//pointer to question text
    char * answer;//pointer to the answer text, which is required for the response to be considered correct
    char * info;//pointer to optional extra text giving advice such as to how to format the response
    char * hint;//pointer to optional text giving a clue to the answer
    int right;//indicates whether counter is counting correct or incorrect responses
    int counter;//counts how many times in a row the answer has been correct/incorrect
    int known;//indicates to what level the vocab is known, and thus to which list it belongs (when loading/saving)
    struct vocab * next;//pointer to next in list
};

struct listinfo//struct holds head, tail and the number of entries for the n2l, norm, known and old lists
{
    struct vocab * head;
    int entries;
    struct vocab * tail;
};

struct listinfo n2l, norm, known, old;
int n2l_flag; //Prevents 'need to learn's coming up twice in a row
int maxtextlength = MAXTEXTLENGTH; //allows use of this #define within text strings
FILE * inputfile;
FILE * outputfile;

void getrecordsfromfile(char * inputfilename,char separator);//load
char * readtextfromfile(int maxchars,char separator);//get text field from file
int readnumberfromfile(int maxvalue,char separator);//get integer field from file
struct vocab * addtolist(struct vocab * newentry, struct listinfo * list);//add given (already filled in) vocab record to given list
int removefromlist(struct vocab * entry, struct listinfo * list,int freeup);//remove given entry from given list. Also destroy record if freeup is true
void reindex (struct listinfo * list);//necessary to stop gaps in the numbering system, which could cause random vocab selection to fail
int writeliststofile();//save
void testme();//main code for learning vocab, including options menu
char * gettextfromkeyboard(char * target,int maxchars);//set given string (char pointer) from keyboard, allocating memory if necessary
int getyesorno();//asks for yes or no, returns true (1) if yes
void testrandom();//code keeps causing exceptions, and as it's so random, I'm guessing it's to do with the random numbers

void getrecordsfromfile(char * inputfilename,char separator)
{
    int counter = 0;
    struct vocab * newvocab;
    struct listinfo * newvocablist;
    if (!(inputfile = fopen(inputfilename, "r")))
    {
        printf("Unable to read input file. File does not exist or is in use.\n");
    }    
    else
    {
        printf("Opened input file %s, reading contents...\n",inputfilename);
        while (!feof(inputfile))
        {
            newvocab = (struct vocab *)malloc(sizeof(struct vocab));
            if (!newvocab)
            {
                printf("Memory allocation failed!\n");
                return;
            }
            else
            {
                newvocab->question=readtextfromfile(MAXTEXTLENGTH,separator);
                newvocab->answer=readtextfromfile(MAXTEXTLENGTH,separator);
                newvocab->info=readtextfromfile(MAXTEXTLENGTH,separator);
                newvocab->hint=readtextfromfile(MAXTEXTLENGTH,separator);
                newvocab->right=readnumberfromfile(1,separator);
                newvocab->counter=readnumberfromfile(0,separator);
                newvocab->known=readnumberfromfile(3,separator);

                switch (newvocab->known)
                {
                    case 0: newvocablist = &n2l;break;
                    case 1: newvocablist = &norm;break;
                    case 2: newvocablist = &known;break;
                    case 3: newvocablist = &old;break;
                }

                addtolist(newvocab,newvocablist);
                if (newvocab->question==NULL||newvocab->answer==NULL)
                {
                    printf("Removing empty vocab record created from faulty input file...\n");
                    removefromlist(newvocab,newvocablist,1);
                }
                else counter++;
            }
        }
        fclose(inputfile);
        printf("...finished.\n%i entries read from %s.\n\n",counter,inputfilename);
    }
    return;
}

char * readtextfromfile(int maxchars,char separator)
{
    int i=0;
    char ch;
    char * target = (char *)malloc(maxchars+1); //allocate memory for new string
    if (!target) {printf("Memory allocation failed!\n");return 0;}//return 0 and print error if alloc failed

    ch=getc(inputfile);
    if (ch==separator){free(target);return NULL;}//if field is blank (zero-length), return null pointer
    while (isspace(ch))
    {
        ch = getc(inputfile);//cycle forward until you reach text
        if (ch == separator||ch=='\n'||ch==EOF) {free(target);return NULL;}//if no text found(reached ~ before anything else), return null pointer
    }
    if (ch=='"') //Entry is in quotes (generated by excel when exporting to .csv and field contains a comma)
    {
        ch=getc(inputfile);//move to next character after the quotes
        while (i<maxchars && ch!='"' && ch!='\n')//stop when you reach the end quotes, end of line, or when text too long
        {
            target[i++]=ch;
            ch = getc(inputfile); //copy name from file to target, one char at a time
        }
    }
    else //entry is not in quotes, so char is currently first letter of string
    {
        while (i<maxchars && ch!=separator && ch!='\n')//stop when you reach separator, end of line, or when text too long
        {
            target[i++]=ch;
            ch = getc(inputfile); //copy name from file to target, one char at a time
        }
    }
    target[i] = '\0';//terminate string
    return target;
}

int readnumberfromfile (int maxvalue,char separator)
{
    int number, i=0;
    char ch;
    char * buff = (char *)malloc(11);
    if (!buff) {printf("Memory allocation failed!\n");return 0;}//return 0 and print error if alloc failed
    if (!maxvalue) maxvalue=MAXINTVALUE;

    ch=getc(inputfile);
    while (!isdigit(ch))
    {
        ch = getc(inputfile);//cycle forward until you reach a digit
        if (ch == separator||ch=='\n'||ch==EOF) {printf("Format error in file\n");return 0;}//if no number found(reached ~ before digit), print error and return 0
    }
    while (i<11 && ch!=separator && ch!='\n')//stop when you reach '~', end of line, or when number too long
    {
        buff[i++]=ch;
        ch = getc(inputfile); //copy number from file to buff, one char at a time
    }
    buff[i] = '\0';//terminate string
    number = atoi(buff)<=maxvalue ? atoi(buff) : maxvalue;//convert string to number and make sure it's in range
    free(buff);
    return number;
}

struct vocab * addtolist(struct vocab * newentry, struct listinfo * list)
{
    if (!list->head)//if head is null, there is no list, so create one
    {
        list->head = list->tail = newentry;//this is the new head and tail
        list->entries = newentry->index = 1;
        newentry->next = NULL;// FISH! not sure if this is necessary, but just be sure...
    }
    else//just appending to the list
    {
        list->tail->next = newentry;//adjust current tail to point to new entry
        list->tail = newentry;//make the new entry the new tail
        newentry->index=++list->entries;
        newentry->next = NULL;
    }
    return newentry;
}

int removefromlist(struct vocab * entry, struct listinfo * list,int freeup)
{
    struct vocab * prev;
    if (list->head == entry) //if entry being deleted is first in the list
    {
        if (list->tail == entry) //if entry is only item in the list
        {
            list->head = list->tail = NULL;
        }
        else //if first in list, but not last
        {
            list->head = entry->next;
        }
    }
    else //entry is not first in list, so set prev to point to previous entry
    {
        prev = list->head;
        while (prev->next!=entry)
        {
            prev=prev->next;
            if (!prev)
            {
                printf("Trying to delete an entry from a list it's not in!!\n");
                return 0;
            }
        }
        if (list->tail == entry)//if entry is at the end of the list
        {
            list->tail = prev;
            list->tail->next = NULL;
        }
        else //if entry is somewhere in middle of list
        {
            prev->next=entry->next;
        }
    }//this entry is now not pointed to in any list
    list->entries--;
    /*following line removed because it could theoretically break a list if the entry was removed from a list after it had been added to another
    entry->next = NULL;//and doesn't point to anything either*/
    reindex(list);
    if (freeup) //if freeup is set, this also wipes the record and frees up the memory associated with it
    {
        free(entry->question);
        free(entry->answer);
        free(entry->info);
        free(entry->hint);
        free(entry);
    }
    return 1;
}

void reindex (struct listinfo * list)
{
    int counter = 1;
    struct vocab * workingentry = list->head;
    while (workingentry)
    {
        workingentry->index = counter++;
        workingentry=workingentry->next;
    }
    if (list->entries!=counter-1) printf("Reindexing Error!\n");
}

int writeliststofile()
{
    int i,counter=0;
    struct listinfo * list;
    struct vocab * entry;
    if (!(outputfile = fopen(DOUTPUTFILENAME, "w")))
    {
        printf ("Error accessing output file!\n");
        return 0;
    }
    else
    {
        printf("Saving...\n");
        for (i=0;i<=3;i++)
        {
            switch (i)
            {
                case 0: list = &n2l;break;
                case 1: list = &norm;break;
                case 2: list = &known;break;
                case 3: list = &old;break;
                default: printf("Loop Error!\n");break;
            }
            entry=list->head;
            while (entry!=NULL)
            {
                if (counter) fprintf(outputfile,"\n");
                fprintf(outputfile,"%s~%s~%s~%s~%i~%i~%i",entry->question,entry->answer,entry->info,entry->hint,entry->right,entry->counter,i);
                entry=entry->next;
                counter++;
            }
        }
        fclose(outputfile);
        printf("...finished. %i entries saved.\n",counter);
        return 1;
    }
}

void testme()
{
    int list_selector, entry_selector, bringupmenu = 0, testagain=1;
    char testmenuchoice = '\n';
    char * youranswer = (char *)malloc(MAXTEXTLENGTH+1);
    struct listinfo * currentlist;
    struct vocab * currententry;
    if (!youranswer) {printf("Memory allocation error!\n");return;}

    while (testagain)
    {
        fprintf(stderr,"Start of 'testagain' loop\nClearing screen...\n");
        system("cls");

        //select a list at random, using the percentage probabilities in the if statements. FISH! Can this be done with a switch and ranges?
        fprintf(stderr,"Assigning list selector to random value...");
        list_selector = (((float)rand() / 32768) * 100)+1;
        fprintf(stderr,"assigned list selector value %i\nAssigning list pointer...",list_selector);
        if (list_selector<33) currentlist = &n2l;
        if (list_selector>32&&list_selector<95) {n2l_flag=0;currentlist=&norm;} //use norm list and cancel n2l flag (not cancelled with other lists)
        if (list_selector>94&&list_selector<100) currentlist = &known;
        if (list_selector==100) currentlist = &old;
        fprintf(stderr,"assigned list pointer %x\nModifying pointer...",currentlist);

        //do a little control over random selection
        if (currentlist==&n2l && n2l_flag) {currentlist=&norm; n2l_flag=0;} //if n2l list was used last time as well (flag is set), use entry from the norm list instead
        if (currentlist==&n2l) n2l_flag = 1; //is using n2l this time, set flag so it won't be used next time as well

        if (currentlist->entries==0) currentlist = &norm;//if current list is empty, default to normal list
        if (currentlist->entries==0 && !n2l_flag) currentlist = &n2l;//if normal list is empty, try n2l list if it wasn't used last time
        if (currentlist->entries==0 && list_selector%10==5) currentlist = &old;//if list is still empty, in 10% of cases try old list
        if (currentlist->entries==0) currentlist = &known;//in the other 90% of cases, or if old is empty, use the known list
        if (currentlist->entries==0) currentlist = &old;//if known list is empty, try the old list
        if (currentlist->entries==0) {currentlist = &n2l;n2l_flag=1;}//if old list is empty, use n2l list EVEN if it was used last time
        if (currentlist->entries==0) {printf("No entries in list!");return;} //if list is STILL empty, abort
        fprintf(stderr,"modified list pointer\nAssigning entry selector...");

        //we now have the desired list of words with at least one entry, let's select an entry at random from this list
        entry_selector = (((float)rand() / 32767) * currentlist->entries)+1;
        fprintf(stderr,"assigned entry selector value %i\nAssignig pointer...",entry_selector);
        currententry = currentlist->head;
        fprintf(stderr,"set entry pointer to head, going to loop to it...\n");
        while (currententry->index!=entry_selector)
        {
            currententry = currententry->next;//move through list until index matches the random number
            if (currententry==NULL) {printf("Indexing error!\nCurrent list selector: %i, entries: %i, entry selector: %i\n",list_selector,currentlist->entries,entry_selector);return;}//in case not found in list
        }
        fprintf(stderr,"Looped, testing.\n");

        printf("Translate the following:\n\n\t%s\n\n",currententry->question);
        if (!currententry->info) printf("There is no additional information for this entry.\n");
        else printf("Useful Info: %s\n\n",currententry->info);
        printf("Your Translation:\n\n\t");
        gettextfromkeyboard(youranswer,MAXTEXTLENGTH);
        if (!strcmp(youranswer,currententry->answer))//if you're right
        {
            printf("Yay!\n");

            if(currententry->right) currententry->counter++;
            else currententry->right = currententry->counter = 1;
            if (currententry->counter>2) printf("You answered correctly the last %i times in a row!\n",currententry->counter);

            //make comments based on how well it's known, and move to a higher list if appropriate
            if (currentlist==&n2l && currententry->counter>=N2LTONORM)
            {
                removefromlist(currententry,currentlist,0);
                printf("Looks like you know this one a little better now!\nIt will be brought up less frequently.\n");
                currententry->counter = 0;
                addtolist(currententry,&norm);
            }
            if (currentlist==&norm && currententry->counter>=NORMTOKNOWN)
            {
                removefromlist(currententry,currentlist,0);
                printf("Looks like you know this one now!\nIt will be brought up much less frequently.\n");
                currententry->counter = 0;
                addtolist(currententry,&known);
            }
            if (currentlist==&known && currententry->counter>=KNOWNTOOLD)
            {
                removefromlist(currententry,currentlist,0);
                printf("OK! So this one's well-learnt.\nIt probably won't be brought up much any more.\n");
                currententry->counter = 0;
                addtolist(currententry,&old);
            }
        }

        else //if you're wrong
        {
            printf("\nSorry, the correct answer is:\n\n\t%s\n\n",currententry->answer);

            if(!currententry->right) currententry->counter++;
            else {currententry->right = 0; currententry->counter = 1;}
            if (currententry->counter>1) printf("You've got this one wrong the last %i times.\n",currententry->counter);
            if (currentlist==&norm && currententry->counter>=NORMTON2L)
            {
                removefromlist(currententry,currentlist,0);
                printf("This one could do with some learning...\n");
                currententry->counter = 0;
                addtolist(currententry,&n2l);
            }
            if (currentlist==&known && currententry->counter>=KNOWNTONORM)
            {
                removefromlist(currententry,currentlist,0);
                printf("OK, perhaps you don't know this one as well as you once did...\n");
                currententry->counter = 0;
                addtolist(currententry,&norm);
            }
            if (currentlist==&old && currententry->counter>=OLDTONORM)
            {
                removefromlist(currententry,currentlist,0);
                printf("This old one caught you out, huh? It will be brought up a few more times to help you remember it.\n");
                currententry->counter = 0;
                addtolist(currententry,&norm);
            }
        }

        fprintf(stderr,"Tested, options menu?\n");
        printf("Type 'o' for options or strike enter for another question\n");
        testmenuchoice = getchar();
        fprintf(stderr,"Got choice\n");
        if (tolower(testmenuchoice)=='o') bringupmenu = 1;
        fprintf(stderr,"set menuflag\n");
        if (testmenuchoice!='\n') while (getchar()!='\n')getchar();
        fprintf(stderr,"cleared getchar\n");
        while (bringupmenu)
        {
            system("cls");
            printf("Current Entry:\n\nQuestion: %s\nAnswer: '%s'\n",currententry->question,currententry->answer);
            if (currententry->info) printf("Info: %s\n",currententry->info); else printf("No info.\n");
            if (currententry->hint) printf("Hint: %s\n\n",currententry->hint); else printf("No hint.\n\n");
            printf("Options Menu:\n\nType q to modify the question phrase displayed for translation.\nType a to change the answer phrase you must provide.\nType i to add/modify additional info for this entry.\nType h to add/modify the hint for this entry.\nType p to mark this entry as high priority to learn.\nType d to delete this entry from the database.\nType x to end testing and return to the main menu.\n\n");
            testmenuchoice=getchar();
            while (getchar()!='\n') getchar();
            switch (testmenuchoice)
            {
                case 'q': printf("Enter new question text for this entry (max %i chars):\n",maxtextlength);
                           currententry->question=gettextfromkeyboard(currententry->question,MAXTEXTLENGTH);
                           break;
                case 'a': printf("Enter new answer text for this entry (max %i chars):\n",maxtextlength);
                           currententry->answer=gettextfromkeyboard(currententry->answer,MAXTEXTLENGTH);
                           break;
                case 'i': printf("Enter new info for this entry (max %i chars):\n",maxtextlength);
                           currententry->info=gettextfromkeyboard(currententry->info,MAXTEXTLENGTH);
                           break;
                case 'h': printf("Enter new hint for this entry (max %i chars):\n",maxtextlength);
                           currententry->hint=gettextfromkeyboard(currententry->hint,MAXTEXTLENGTH);
                           break;
                case 'p': if(currentlist=&n2l)printf("Already marked as priority!\n");
                           else
                           {
                               removefromlist(currententry,currentlist,0);
                               currententry->counter = 0;
                               currentlist=&n2l;
                               addtolist(currententry,currentlist);
                               printf("Entry will be brought up more often\n");
                           }
                           break;
                case 'd': printf("Are you sure you want to delete this entry?\nOnce you save, this will be permanent!(y/n)");
                           if (getyesorno()) {removefromlist(currententry,currentlist,1);printf("Entry deleted!\n");bringupmenu=0;}
                           else printf("Entry was NOT deleted.\n");
                           break;
                case 'x': bringupmenu = testagain = 0;
                           break;
                default: printf("Invalid choice.\n");
            }
            if (bringupmenu)
            {
                printf("Select again from the options menu? (y/n)");
                bringupmenu = getyesorno();
            }
            if (!bringupmenu&&testagain)
            {
                printf("Continue testing? (y/n)");
                testagain = getyesorno();
            }
        }
        fprintf(stderr,"End of 'testagain' loop.\n Clearing Screen...");
        system("cls");
    }
    free(youranswer);
//    getchar();
    return;
}

char * gettextfromkeyboard(char * target,int maxchars)
{
    int i =0;
    char ch;
    if (!target)//if no memory already allocated (pointer is NULL), do it now
    {
        target=(char *)malloc(maxchars+1);
        if (!target) {printf("Memory allocation failed!");return NULL;} //return null if failed
    }
    ch = getchar();
    if (ch=='\n') {free(target);return NULL;}//if zero length, free mem and return null pointer
    while (!isalnum(ch))//cycle forward past white space
    {
        ch=getchar();
        if (ch=='\n') {free(target);return NULL;}//if all white space, free mem and return null pointer
    }
    while (ch!='\n' && i<maxchars)
    {
        target[i++]=ch;
        ch=getchar();
    }
    target[i]='\0';
    return target;
}

int getyesorno()
{
    char yesorno = '\n';
    while (toupper(yesorno)!='Y'&&toupper(yesorno)!='N')
    {
        yesorno=getchar();
        if (toupper(yesorno)!='Y'&&toupper(yesorno)!='N') printf("Invalid choice. You must enter Y or N:\n");
    }
    while (getchar()!='\n') getchar();
    if (toupper(yesorno)=='Y') return 1;
    else return 0;
}

void testrandom()
{

    return;
}

int main(int argc, char* argv[])
{
    char * inputfilename = DINPUTFILENAME;
    char * outputfilename = DOUTPUTFILENAME;
    char separator = '~';
    char menuchoice = '\0';
    n2l.entries = norm.entries = known.entries = old.entries = 0;

    srand((unsigned)time(NULL));

    fprintf(stderr,"Start...\n");
    printf("Loading...\nLoad default database? (y/n)");
    if (!getyesorno())
    {
        printf("Default file type is .~sv. Import .csv file instead? (y/n)");
        if (getyesorno())
        {
            separator = ',';
            printf("Enter name of .csv file to import:\n");
        }
        else
        {
            printf("Enter name of .~sv file to load:\n");
        }
        inputfilename = gettextfromkeyboard(inputfilename,256);
    }
    getrecordsfromfile(inputfilename,separator);


    while (menuchoice!='x')
    {
        printf("Welcome to the Vocab Test, version C!\n\nMain menu:\n\n\tt: Test Me!\n\ts: Save\n\tx: Exit\n\n");
        menuchoice = getchar();
        while (getchar()!='\n') getchar();        
        switch (tolower(menuchoice))
        {
            case 'x': break;
            case 't': testme(); break;
            case 's': writeliststofile();break;
            case 'w': testrandom(); break;
            default: printf("Invalid choice. Please try again.\n"); break;
        }
        system("cls");
    }


    system("cls");
    printf("Bye for now!\n\nPress enter to exit.");
    getchar();
    fprintf(stderr,"Successfully closed\n");
    return 0;
}

我尝试在典型运行中添加 stderr 的输出,但这会使正文太长。还尝试将其添加为答案,但是:

Users with less than 100 reputation can't answer their own question for 8 hours after asking. You may self-answer in 7 hours. Until then please use comments, or edit your question instead.

背景:今年早些时候,我第一次涉足编程,并决定从 C 开始,然后再转向 C++、Java,也许还有 Python 和 C#。 为了让我开始使用 C,在强制性的 hello world 之后,我编写了一个小游戏(基于文本,还包括“cls”命令),然后转向这个小词汇测试器,这是为了帮助我在学习印尼语时学习印尼语。在奥地利说德语:-D。我最终对 cls 崩溃感到愤怒,从那以后就没有再编程了。我真的很想重新拾起它,所以我从这个问题开始。

最佳答案

您是否尝试过打印'\f'?这就是“换页”字符。

编辑:我仔细查看了您的代码,发现有些事情我不喜欢。 :-)

例如,在 gettextfromkeyboard 中,如果您仅输入空格,它将释放 target,即使输入时该值非 NULL。

在这一行中:

inputfilename = gettextfromkeyboard(inputfilename,256);

它将指向常量字符串的inputfilename传递给gettextfromkeyboard。试图释放它是一个坏主意。

我对 while (getchar()!='\n') getchar(); 也有疑问。

假设输入是“ABC\n”

条件将消耗并返回'A',循环体将消耗'B',条件将消耗并返回'C' ,循环体将消耗'\n'

关于c - 发送 "cls"到dos会导致异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6987169/

相关文章:

c++ - 为什么内存地址是偶数?

c - 如何重置 c 中的链表?

java - 用于铁路模拟的命令处理程序/解析器

linux - 是否有将随机垃圾字节写入文件的命令?

java - 我如何才能知道在我的操作系统上进行上下文切换需要多长时间?

c# - 如何在 wcf-service 中设置默认的 faultcontract 属性

exception - 在Dart中抛出错误的正确方法

exception - 捕获 "Pixel outside the boundaries"异常?

ruby - 从 Ruby 在 Windows 命令中设置本地 ENV 变量

c++ - 使用 Arduino Mega 和 Simple-H HV Motor Shield 控制直流电机