【问题标题】:getc() "stores" the input and reuses it with scanf(), not allowing the user to inputgetc()“存储”输入并用scanf()重用它,不允许用户输入
【发布时间】:2016-08-14 00:23:40
【问题描述】:

我有以下代码。程序所做的是向用户询问 2 个字符串。对于第一个,我尝试使用带有 malloc() 的内存分配字符串并使用 getc() 来处理来自用户的输入。对于第二个字符串,我使用了一个指定大小的字符数组和 scanf()。我遇到的问题是 scanf 从 getc() 获取超出的值之前使用了一些代码行。我怎样才能阻止这种行为? enaString[ctr] = '\0';diaxwristiki[LINESIZE-1] = '\0' 也被认为是未定义的行为吗?或者这是在字符串中添加空终止字符的正确方法?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main(int argc, char* argv[])
{
    int LINESIZE = 15; //maximum length of array of characters
    int ctr = 0;
    char * enaString = NULL, xaraktiras, diaxwristiki[LINESIZE];

    enaString = (char*)malloc(sizeof(char) * LINESIZE + 1);

    if(enaString == NULL)//check if the memory has free blocks
        {
            printf("error to initillize memory");
            exit(1);
        }
    printf("Eisagete xaraktira mikous %d :\n", LINESIZE);
    do{
        xaraktiras = getc(stdin); 
        enaString[ctr] = xaraktiras;
        ctr++;
        if (ctr == LINESIZE -1)  
        {
            break;
        }

    }while(xaraktiras != '\n' );

    enaString[ctr] = '\0'; //is this considered undefined behaviour?
    enaString = NULL;
    free(enaString);

    printf("eisagete mia leksi diaxwrismou :\n");//ask the user for another word.fails cause it keeps the getc() value from before
    scanf(" %15s",diaxwristiki);
    diaxwristiki[LINESIZE-1] = '\0';//is this undefined behaviour?

    printf("timi diaxwrismou %s\n", diaxwristiki);




}

【问题讨论】:

  • ctr = 0。然后enaString[ctr] = xaraktiras -> enaString[0] = xaraktiras。然后ctr++(ctr 现在是 1)。然后enaString[ctr + 1] = '\0' -> enaString[2] = '\0',所以 enaString[0] 是有效的(getc 的结果)。 enaString[2] 为 NUL。 enaString[1] 是??????。然后将 enaString 设置为 NULL,泄漏分配的内存,然后 free(NULL);首先清理这些问题,内存泄漏可能会导致其他地方出现奇怪的行为。
  • 你说得对,我已经在我的代码中修复了这个问题,enaString[ctr + 1] = '\0' 更改为enaString[ctr] = '\0'
  • @Tibrogargan 设置一个指向NULL 的指针然后释放分配的空间不好吗?我应该先释放内存,然后将指针设置为NULL
  • 非常如此。 malloc 返回一个内存地址。您需要将相同的内存地址传递给free。更改 enaString 的值会丢失地址。 free(NULL) 的结果永远不会是好的。释放内存。将 enaString 设置为 NULL 是没有意义的,除非您测试 enaSting(即if (NULL != enaString)
  • 我说怎么做:static inline void gobble_eol() { int c; while ((c = getchar()) != EOF &amp;&amp; c != '\n') ; },当你需要清除当前输入行的任何残留数据时,调用gobble_eol();

标签: c


【解决方案1】:

编译隐藏在 OP 帖子下方的 cmets 中的信息和我自己的 2 美分:

#include <stdio.h>
#include <stdlib.h>
// you don't use anything from string.h in your version
//#include <string.h>

// Put simple constants here, for the preprocessor to process
#define LINESIZE 15

// you don't use the arguments, no need to put them here
int main( /*int argc, char *argv[] */ )
{
  // such constants are better put into a preprocessor directive
  //int LINESIZE = 15; //maximum length of array of characters
  int ctr = 0;

  // Sorted into three lines (three different types) better to read

  // No need to initialize enaString to NULL for malloc/calloc
  // It is a good idea to do for realloc(), safes you
  // the initial malloc() but you do not use realloc() here
  char *enaString;
  // (f)getc() and scanf() return an int
  int xaraktiras, ret_scanf;
  char diaxwristiki[LINESIZE];

  // no casting of malloc() in C
  enaString = /*(char*) */ malloc(sizeof(char) * LINESIZE + 1);
  if (enaString == NULL)        //check if the memory has free blocks
  {                             
    // use stderr stream for error output
    // (sderr might not be available but worth a try)
    // UX-tip: use the same language for errors that you
    // use for user interaction elsewhere
    fprintf(stderr, "error to initillize memory");
    // Use the macros from stdlib.h, the return values
    // are OS dependent and might not be 0 and 1 respectively
    exit(EXIT_FAILURE);
  }
  // you ask for a word of a certain size or only for a word?
  // (My Greek is not very good and Google is of not much help here)
  printf("Eisagete xaraktira mikous %d :\n", LINESIZE);
  do {
    // please be aware the getc() is in most cases implemented
    // as a macro, use fgetc() if you are not sure if that is
    // a problem (it is not here) because macros might get evaluated
    // more than once
    xaraktiras = getc(stdin);
    // you need to check for EOF somewhere. Here would be a good place
    if (xaraktiras == EOF) {
      // try it by pressing CTRL+D instead of feeding characters to getc()
      fprintf(stderr, "EOF found in getc() loop\n");
      // EOF might also indicate an error, see the handling of scanf() below
      // We don't bother with it now, we just exit
      exit(EXIT_FAILURE);
    }
    // no need for a cast here
    enaString[ctr] = xaraktiras;
    // put it after the check, otherwise you have an undefined
    // character at enaString[ctr]
    // ctr++; 
    if (ctr == LINESIZE - 1) {
      // You offered LINESIZE, have allocated LINESIZE+1, but only
      // allow LINESIZE-1
      // The user might be disappointed
      break;
    }
    ctr++;
    // No casting needed, because the type of a char constant is int
    // (yes, that means that things like "char c='STOP'" once worked and you
    //  were able to look for 0x53544f50 in the memory dump. Some compilers might
    //  still allow for it but it is not recommended)
  } while (xaraktiras != '\n');

  // slurp the rest up if there were more characters given
  // (check for EOF ommitted here but should be added, of course)
  if(xaraktiras != '\n'){
     while ((xaraktiras = getc(stdin)) != '\n');
  }

  // you go up to LINESIZE-1 now, so, together with the replacement of ctr++, it is OK
  enaString[ctr + 1] = '\0';    //is this considered undefined behavior?

  // don't just dump the painfully gathered characters, print them at least.
  // That way you'll find out that you included the '\n', too, which
  // might or might not have been your intent
  printf("enaString = \"%s\"\n",enaString);

  // To free the memory free() needs to know where it is and
  // the pointer enaString points to that memory. If you set
  // enaString to NULL free() does not know which memory to free
  // (worse: free(NULL) is allowed) and the memory
  // is left alone, crying, and is unreachable until the program ends,
  // a so called "memory leak"

  // enaString = NULL;
  // free(enaString);
  free(enaString);
  // I don't know who told you so, but it is indeed a good idea to set
  // the pointer to the free'd memory to NULL. Won't do anything here
  // but might safe you from a lot of headaches in large programs
  enaString = NULL;

  printf("eisagete mia leksi diaxwrismou :\n"); //ask the user for another word.

  // the variable "diaxwristiki" can hold 15 characters , "%15s" allows for 16, because
  // scanf() includes `\0` (EOS, NUL, nul, or whatever the kids call it today), too!

  // scanf() returns the number of elements (not characters!) read or EOF.

// for strerror()
#include <string.h>
// for errno
#include <errno.h>
  // reset errno, just in case
  errno = 0;
  if ((ret_scanf = scanf(" %14s", diaxwristiki)) == EOF) {
    // It also returns EOF in case of an error, so check for it
    if (errno != 0) {
      fprintf(stderr, "error in scanf: %s\n", strerror(errno));
      exit(EXIT_FAILURE);
    }
    // try it by pressing CTRL+D instead of feeding characters to scanf()
    fprintf(stderr, "EOF triggered by scanf()\n");
    // diaxwristiki might contain rubbish at this point, clear it
    diaxwristiki[0] = '\0';
  }
  // no need for adding EOS, scanf() already added it
  // diaxwristiki[LINESIZE-1] = '\0';//is this undefined behaviour?
  printf("timi diaxwrismou %s\n", diaxwristiki);
  // it's "int main()", so return something.
  exit(EXIT_SUCCESS);
}

【讨论】:

  • 虽然我非常感谢您帮助我并向我展示了我在代码中犯的一些错误,这对我来说非常有用,因为我想更好地掌握 C 语言,但我的问题没有得到解决。让我更具体一点。假设我想输入以下字符串:kjasldjalsjdlaskjdlasjdl。它只会保留表示enaString = "kjasldjalsjdlas" 的 15 个字母。然后程序进行到第二个输入。在 scanf 中,它不是询问用户另一个输入,而是存储这个 kjdlasjdl 而不是第一个输入中剩余的字母!我该如何避免呢?
  • 并要求用户输入而不是仅仅保存之前的那些字符?
  • 啊啊!是的,我只是忘记了。想想,我不能再忽视它了,我现在真的非常需要一个漫长的假期。希腊的天气怎么样?
  • 感谢伙伴按预期工作。非常感谢您的回答,因为它帮助了我很多!希腊这里的天气很好。虽然我自己不喜欢炎热的天气,但我建议您访问我们。这将是您一生中最美好的时光,也是您度过的最美好的假期。这里的人和岛屿是您不容错过的难忘经历。祝你好运
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-17
  • 2021-02-08
  • 1970-01-01
  • 2010-11-17
相关资源
最近更新 更多