使用scanf – > ANSI C的最大字符串长度
我有:
#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)
…
有任何想法吗?
我对这些解决方案都不满意,所以我进一步研究,发现了GNU GCC宏字符串化
可以用作:
#define XSTR(A) STR(A) #define STR(A) #A #define MAX_STR_LEN 100 scanf("%"XSTR(MAX_STR_LEN)"[^\n]s", sometext)
也许VS2010提供类似的东西?
我只是想避免缓冲区溢出
然后不要使用scanf()
。 完全没有。
如果要扫描文本行,请不要#define MAX_STR
。 您可以在
隐藏LINE_MAX
(如果您的目标是POSIX兼容系统):
char buf[LINE_MAX]; fgets(buf, sizeof(buf), stdin);
应该做的伎俩。
几乎每个人都说,最好使用fgets(..., stdin)
来处理这个问题。
在下面的链接中,我提出了一种安全且正确的技术,可以通过一个更安全的方法,通过实体宏替换scanf()
:
一个安全替换scanf()的宏
我提出的宏(使用兼容的C99编译器)是safe_scanf()
,如以下程序所示:
#include #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\tf == %g", x, f); return 0; }
您必须根据需要调整 MAXBUFF
的值…
虽然宏safe_scanf()
非常可靠,
使用宏方法有一些弱点:
缺少对参数的类型检查,缺少“返回”值(几乎与“true” scanf()
函数不同,它返回一个int ,带有用于错误检查的有价值信息),等等。
所有问题都有解决方案,但它是另一个主题的一部分……
也许,最精确的解决方案是通过调用stdarg.h
库来定义具有可变数量参数的函数my_scanf()
,联合fgets()
和vsscanf()
。 在这里你有代码:
#include #include 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:\nx == %d\nz == %g\n scanfret == %d", x, z, scanf_ret); getchar(); return 0; }
函数my_scanf()具有原型
int my_scanf(const char* fmt, const int maxbuff, ...);
它接受一个格式字符串fmt
,其行为方式与其他scanf()
类似。
第二个参数是从标准输入(键盘)有效接受的最大字符数。
返回值是一个int ,如果maxbuff
没有意义,则为EOF
,或者发生一些输入错误。 如果返回非负值,则与标准函数sscanf()
或vsscanf()
返回的值相同。
在函数内部, maxbuff
在1中递增,因为fgets()
为一个额外的'\ 0'字符腾出了一些空间。
立即丢弃maxbuff
非正值。
fgets()
将读取从stdin
(键盘)读取的字符串,其中包含最多maxbuff
字符的空间,包括'\ n'。
如果用户输入了一个非常长的字符串,那么它将被截断,并且需要某种“刷新”机制以便将所有字符丢弃到下一个'\ n'( ENTER )。 如果没有,下一个键盘读取可能有旧字符,根本不需要。
“刷新”的条件是在读取stdin
后fgets()
没有达到'\ n'。
如果且仅当buffer[maxbuff - 1]
不等于'\ 0'或'\ n'时才会出现这种情况。
( 检查一下! )
最后,使用stdarg.h
宏和函数vsscanf()
的适当组合来处理变量参数列表。
推荐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
不太可能按照你的想法进行。
怎么样
scanf("%.*[^\n]s", MAX_STR_LEN, sometext)