首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >既然我们有snprintf,为什么我们没有snscanf呢?

既然我们有snprintf,为什么我们没有snscanf呢?
EN

Stack Overflow用户
提问于 2013-08-21 22:23:29
回答 5查看 19K关注 0票数 21

我有snprintf,它可以避免缓冲区溢出,但是为什么没有名为snscanf的函数

代码:

代码语言:javascript
运行
复制
int main()
{
     char * src = "helloeveryone";
     char buf1[5];
     sscanf(src,"%s",buf1); // here is a  array out of bounds

}

因此,我认为snscanf也是必要的。为什么我们只有snprintf

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2013-08-21 22:49:07

C11中有争议的(和可选的)附件K添加了一个sscanf_s函数,该函数在指针参数之后增加一个rsize_t类型的参数(也在附件K中定义),指定指向数组的大小。无论是好是坏,这些功能没有得到广泛的支持。通过将大小放入转换说明符中,您可以获得相同的结果。

代码语言:javascript
运行
复制
char out[20];
sscanf(in, "%19s", out);

但是,如果目标对象的大小在运行时可能发生变化(您必须使用snprintf以编程方式构造转换说明符),这是很尴尬和容易出错的。请注意,转换说明符中的字段宽度是要读取的输入字符的最大数量,sscanf还为%s转换写入一个终止空字节,因此您传递的字段宽度必须严格小于目标对象的大小。

票数 11
EN

Stack Overflow用户

发布于 2013-08-21 22:43:30

sscanf(s, format, ...)中,扫描的字符数组是const char *。没有写信给s。当s[i]为NUL时,扫描停止。几乎不需要n参数作为扫描的辅助限制。

sprintf(s, format, ...)中,数组s是目标。snprintf(s, n, format, ...)确保数据不会对s[n]和其他方面产生影响。

有用的是sscanf()转换说明符的标志扩展,这样在编译时就可以很容易地指定限制。(现在可以以一种繁琐的方式完成,下面是动态格式或sscanf(src,"%4s",buf1)。)

代码语言:javascript
运行
复制
// This is a proposed idea for C. - Not valid code today.
sscanf(src, "%!s", sizeof(buf1), buf)

在这里,!将告诉sscanf()读取一个size_t变量,以限制即将出现的字符串的大小。也许在C17?

今天起作用的累赘方法。

代码语言:javascript
运行
复制
char * src = "helloeveryone";
char buf1[5];
char format[1+20+1+1];
sprintf(format, "%%" "%zu" "s", sizeof(buf1) - 1);
sscanf(src, format, buf1);
票数 3
EN

Stack Overflow用户

发布于 2013-08-22 03:58:54

为什么不试试fgets() (使用标准输入文件stdin)?

fgets()允许您指定缓冲区的最大大小。

(在以下内容中,我将使用标准的ISO C99兼容语法。)

因此,您可以编写以下代码:

代码语言:javascript
运行
复制
#include <stdio.h>
#define MAXBUFF 20 /* Small just for testing... */
int main(void) {
  char buffer[MAXBUFF+1]; /* Add 1 byte since fgets() inserts '\0' at end */
  fgets(buffer, MAXBUFF+1, stdin);
  printf("Your input was: %s\n", buffer);
  return 0;
}

fgets()最多从stdin读取MAXBUFF字符,

它是标准输入(即:键盘)。

结果保存在数组buffer中。

如果找到'\n‘字符,则读取停止,'\n’也保存在buffer中(作为最后一个字符)。此外,总是在buffer末尾添加一个'\0‘,因此需要足够的存储空间。

您可以使用fgets()sscanf()的组合来处理字符串:

代码语言:javascript
运行
复制
  char buffer[MAXBUFF+1];
  fgets(buffer, MAXBUFF+1, stdin); /* Plain read */
  int x; float f;
  sscanf(buffer, "%d %g", &x, &f); /* Specialized read */

因此,您有一个“安全”的scanf()-like方法。

注:这种方法有一个很大的问题。如果fgets()在获得行尾字符'\n‘之前到达MAXBUFF字符,则其馀的输入将不会被丢弃,并将作为下一个键盘读取的一部分。

因此,必须添加一个刷新机制,这实际上非常简单:

代码语言:javascript
运行
复制
while(getchar()!'\n') 
    ; /* Flushing stdin... */

但是:如果您只是在fgets()行之后添加最后一段代码,

用户将被迫在每次输入少于MAXBUFF字符的时候按两次输入。最糟糕的是:这是最典型的情况!

要解决这个新问题,请注意一个简单的逻辑条件完全等价于字符'\n‘未到达这一事实,如下所示:

(buffer[MAXBUFF - 1] != '\0') && (buffer[MAXBUFF - 1] != '\n')

(证明!)

因此,我们写道:

代码语言:javascript
运行
复制
fgets(buffer, maxb+1, stdin);
if ((buffer[MAXBUFF - 1] != '\0') && (buffer[MAXBUFF - 1] != '\n'))
     while(getchar() != '\n')
       ;

最后一次触摸是必要的:因为数组缓冲区可以有the,

似乎需要某种初始化。

但是,我们要注意的是,只有[MAXBUFF - 1]位置需要清理:

char buffer[MAXBUFF + 1] = { [MAXBUFF - 1] = '\0' }; /* ISO C99 syntax */

最后,我们可以在一个快速的宏中收集所有的事实,就像这个程序所显示的:

代码语言:javascript
运行
复制
#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;
}

在宏中使用了变量数的机制,

根据 C99规范:变元宏

__VA_ARGS__替换参数的变量列表。

(我们需要可变数量的参数来模拟scanf()-like行为。)

注释:宏体被封装在一个带有{ }的块中.这并不完全令人满意,而且很容易改进,但它是另一个主题的一部分.

特别是,宏safe_scanf()不“返回”一个值(它不是表达式,而是块语句)。

备注:宏中的声明了一个数组buffer,该数组在输入该块时创建,然后在该块退出时被销毁。buffer的范围仅限于宏的块。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18368712

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档