使用scanf的最大字符串长度 - > ANSI C

时间:2013-06-26 08:09:21

标签: c visual-studio-2010 scanf

我有:

#define MAX_STR_LEN 100

我希望放入scanf模式以便控制字符串长度:

scanf("%100[^\n]s",sometext)

我试过了:

scanf("%MAX_STR_LEN[^\n]s",sometext)
scanf("%"MAX_STR_LEN"[^\n]s",sometext)
scanf("%",MAX_STR_LEN,"[^\n]s",sometext)

它不起作用。我只想避免缓冲区溢出,因为“sometext”分配了malloc(MAX_STR_LEN) ...

有什么想法吗?

5 个答案:

答案 0 :(得分:10)

我对这些解决方案都不满意,所以我进一步研究,发现了GNU GCC宏stringification

可以用作:

#define XSTR(A) STR(A)
#define STR(A) #A
#define MAX_STR_LEN 100
scanf("%"XSTR(MAX_STR_LEN)"[^\n]s", sometext)

也许VS2010提供类似的东西?

答案 1 :(得分:7)

  

我只想避免缓冲区溢出

然后不要使用scanf()。完全没有。

如果您正在扫描文本行,请不要#define MAX_STR。您可以在LINE_MAX中隐藏<limits.h>(如果您的目标是POSIX兼容系统):

char buf[LINE_MAX];
fgets(buf, sizeof(buf), stdin);

应该这样做。

答案 2 :(得分:4)

正如几乎每个人都说的那样,最好使用fgets(..., stdin)来解决这个问题。

在以下链接中,我提出了一种安全且正确的技术,可让您通过更加安全的方法替换scanf(),通过实体

A macro that safely replaces scanf()

我建议的宏(使用兼容的 C99 编译器)是safe_scanf(),如以下程序所示:

#include <stdio.h>
#define safe_scanf(fmt, maxb, ...) { \
    char buffer[maxb+1] = { [maxb - 1] = '\0' }; \
    fgets(buffer, maxb+1, stdin); \
    if ((buffer[maxb - 1] != '\0') && (buffer[maxb - 1] != '\n')) \
        while(getchar() != '\n') \
           ; \
    sscanf(buffer, fmt, __VA_ARGS__); \
  }

#define MAXBUFF 20     

int main(void) {
   int x; float f;      
   safe_scanf("%d %g", MAXBUFF+1, &x, &f);
   printf("Your input was: x == %d\t\t f == %g",  x, f);
   return 0;
}  

您必须根据自己的需要调整 MAXBUFF的值...
虽然宏safe_scanf()非常稳固,但是 使用宏观方法存在一些弱点:
缺少对参数的类型检查,缺少“返回”值(几乎与“true”scanf()函数不同,它返回 int ,带有错误检查的有价值信息),等等上。
所有这些问题都有解决方案,但它是另一个主题的一部分......

也许,最精确的解决方案是通过调用my_scanf()库来定义具有可变数量参数的函数stdarg.h,并联合fgets()和{{1}的组合}。这里有代码:

vsscanf()

函数my_scanf()具有原型

#include <stdio.h>
#include <stdarg.h>

int my_scanf(const char* fmt, const unsigned int maxbuff, ...) {
    va_list ptr;
    int ret;

    if (maxbuff <= 0)
       return EOF; /* Bad size for buffer[] */

    char buffer[maxbuff+1];
    buffer[maxbuff-1] = '\0';  /* Quick buffer cleaning... */

    if (fgets(buffer, maxbuff+1, stdin) == NULL)
       return EOF; /* Error detected */
    else {
        if ((buffer[maxbuff-1] != '\n') && (buffer[maxbuff-1] != '\0'))
            /* Condition logically equivalent to:
                   fgets() has not reached an '\n'
            */
            while (getchar() != '\n')
               ; /* "Flushing" stdin... */

        va_start(ptr, maxbuff);
        ret = vsscanf(buffer, fmt, ptr);
        va_end(ptr);
        return ret;
    }    
}

#define MAXBUFF 20
int main(void) {
   int x; 
   float z;
   int scanf_ret = my_scanf("%d %g", MAXBUFF, &x, &z);
   printf("\nTest:\n x == %d\n z == %g\n scanfret == %d", x, z, scanf_ret);
   getchar();   
   return 0;   
}

它接受格式字符串int my_scanf(const char* fmt, const int maxbuff, ...); ,其行为方式与任何其他fmt相同 - 就像这样。
第二个参数是将从标准输入(键盘)有效接受的字符的最大数量。
返回值是 int ,如果scanf()没有意义,或者发生了一些输入错误,则为EOF。如果返回非负值,则与标准函数maxbuffsscanf()返回的值相同。

在函数内部,vsscanf()增加1,因为maxbuff为额外的'\ 0'字符留出了一些空间。
立即丢弃fgets()的非正值 maxbuff将读取从fgets()(键盘)读取的字符串,其中包含最多stdin个字符,包括'\ n'。 如果用户输入了一个非常长的字符串,那么它将被截断,并且需要某种“刷新”机制以便将所有字符丢弃到下一个'\ n'( ENTER ) 。如果没有,下一个键盘读数可能会有旧字符,根本不需要。
“刷新”的条件是maxbuff在阅读fgets()后未达到'\ n'。 只有在stdin不等于'\ 0'或'\ n'时才会出现这种情况。
检查!
最后,使用buffer[maxbuff - 1] 和函数stdarg.h的适当组合来处理变量参数列表。

答案 3 :(得分:0)

推荐fgets(buffer, sizeof(buffer), stdin)方法。

如果仍然想要使用scanf(),您可以在运行时创建其格式。

#define MAX_STR_LEN 100
char format[2 + sizeof(size_t)*3 + 4 + 1];  // Ugly magic #
sprintf(format, " %%%zu[^\n]", (size_t) MAX_STR_LEN);
scanf(format, sometext);

或将MAX_STR_LEN重新定义为字符串

#define MAX_STR_LEN "100"
scanf(" %" MAX_STR_LEN "[^\n]", sometext);

仍然推荐fgets() 注意fgets()会在您的缓冲区中放置前导空格和尾随\n,而" %[^\n]"则不会。 顺便说一句:格式中的尾随s不太可能按照您的想法进行。

答案 4 :(得分:-3)

怎么样

scanf("%.*[^\n]s", MAX_STR_LEN, sometext)