神奇的scanf

神奇的scanf

作为标准输入输出函数组中的一个重要的输入的函数,scanf/sscanf/vscanf函数和printf/sprintf/vsprintf有个重要的区别:如果格式参数和后面的参数不匹配,printf系列函数可能会导致打印出的格式或者数据不是自己期望的
,而scanf系列函数如果格式参数和后面的参数不匹配,可能导致有待输入的参数附近的内存发生变化,甚至导致程序崩溃。

以下面的函数为例:

8
#include<stdio.h>

9
#include<string.h>

10

11
int main(int argc, char * argv)

12
{

13
   char names[6];

14
   char c=‘D‘;

15
   short cnt=0x1234;

16

17
   strcpy(names, "Hello");

18

19
   printf("Original Values:\n");

20
   printf("cnt = %d, c = %c, names = %s\n", cnt, c,
names);

21

22
   printf("Please Input your settings:\n");

23
   printf("C = ");

24
   scanf("%c", &c);

25
   getchar();

26
   printf("Names = ");

27
   scanf("%s", names);

28
   getchar();

29
   printf("Cnt = ");

30
   scanf("%ld", &cnt);

31
   getchar();

32

33
   printf("Latest Values:\n");

34
   printf("cnt = %d, c = %c, names = %s\n", cnt, c,
names);

35

36
   return 0;

37
}

程序期望从标准输入中接受字符C,字符串names和short类型counter,然后把接受到的输入打印出来。但如果把这样的程序进行充分的测试就会发现,当输入的cnt为负数或者输入的cnt值比较大,发现最后打印出来的部分值跟输入的值大相径庭,例如下面的情况:

[[email protected]
CStudy]# ./scan

Original
Values:

cnt
= 4660, c = D, names = Hello

Please
Input your settings:

C
= h

Names
= Leifeng

Cnt
= -2

Latest
Values:

cnt
= -2, c = , names = Leifeng

根本原因是scanf系列函数会根据格式字符串中指定的格式往某个指针所对应的内存区域里写入数据,而非根据指针所指向的数据类型写入。这一点常常给一些刚接触C语言的程序员带来麻烦和困惑,甚至某些工作多年的程序员也经常忽视。为此,需要对scanf系列函数的接口要求和格式参数需要清晰的认识。

1.
scanf系列函数的接口要求

scanf函数的格式参数指定了往内存写入数据的类型,包括字节长度、有无符号、整型还是浮点。如果将要往内存写入的数据长度超过对应指针指向的数据类型的长度,就会覆盖那个指针指向数据的相邻内存,具体影响哪些数据,取决于编译器的堆栈类型(满递减、满递增、空递减、空递增)、硬件环境(大尾端和小尾端)。比如在上面的例子中,就是因为格式字符串
”ld“超过了对应数据short
cnt的长度,因此局部变量char
c 被覆盖。
因此,为了避免scanf函数不正确的调用可能覆盖有用的数据和代码,要求格式参数后面的指针参数所指向的数据类型必须严格符合接受输入的数据的类型。

2.scanf系列函数的格式字符串

总体说来,scanf函数的格式字符串和printf系列的格式字符串差不多,下面列出了常用的格式字符串及其含义:

h:
half, 用于d,i,o,u,x,X,n的前面,表示short,
例如%hd,表示short
int

hh:
half half, 用于d,i,o,u,x,X,n的前面char,表示unsigned
char或者signed
char

j:类似h,但是只用来修饰intmax_t,uintmax_t

l:
long,用来修饰d,i,o,u,x,X,表示就按照long
int或者unsigned
long int往下一个指针指向的内存写数据。如果它是用来修饰e,f,g,那么将会按照double而非float格式读入输入的数据

L:
long long, 用来修饰d,i,o,u,x,X,表示就按照long
long int或者unsigned
long long int往下一个指针指向的内存写数据。如果它是用来修饰e,f,g,那么将会按照long
double而非float/double格式读入输入的数据

t:表示ptridiff_t类型,C99引入

z:表示size_t类型,C99引入

d:表示有符号十进制整型,int

i:
如果输入以0x、0X开头,输入的数据按照16进制读入,如果以0开头,按照8进制读入;其他的类型都按照10进制读入

o:无符号8进制

u:无符号10进制

x/X:无符号16进制

f/e/g/E/a:有符号浮点

p:指针类型

s:空间足够的字符串,会往输入的末尾自动加上‘\0‘

c:一段字符序列,默认长度为1

[:指定非空字符串序列中字符的范围

m:字符串,scanf系列函数会根据输入的数据长短自动申请空间,调用者自己注意释放

下面给出了一个充分利用格式字符m和[
]的例子:

char
*p;

int
n;

errno
= 0;

n
= scanf("%m[A-Z]", &p);

if
(n == 1) {

printf("read:
%s\n", p);

free(p);

}
else if (errno != 0) {

perror("scanf");

}
else {

fprintf(stderr,
"No matching characters\n");

}

参考资料:scanf
man手册

时间: 2024-10-13 03:38:09

神奇的scanf的相关文章

AC日记——神奇的幻方 洛谷 P2615(大模拟)

题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. 之后,按如下方式从小到大依次填写每个数K(K=2,3,…,N*N): 1.若(K−1)在第一行但不在最后一列,则将K填在最后一行,(K−1)所在列的右一列: 2.若(K−1)在最后一列但不在第一行,则将K填在第一列,(K−1)所在行的上一行: 3.若(K−1)在第一行最后一列,则将K填在(K−1)

一个神奇的递推公式--转自2108

志远兄发现了一个神奇的递推公式, 某些递推的题目可以看作, 一个个上三角阵, 而问题的解为(1,1) 至 (n,n) 的路径个数, 废话不多说, 上题上代码 以下转自http://www.cnblogs.com/--ZHIYUAN/p/5971367.html 小兔的棋盘 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 9447    A

c语言scanf详解

函数名: scanf 功 能: 执行格式化输入 用 法: int scanf(char *format[,argument,...]);scanf()函数是通用终端格式化输入函数,它从标准输入设备(键盘) 读取输入的信息.可以读入任何固有类型的数据并自动把数值变换成适当的机内格式.其调用格式为:      scanf("<格式化字符串>",<地址表>);scanf()函数返回成功赋值的数据项数,出错时则返回EOF.其控制串由三类字符构成:1.格式化说明符: 2.

神奇的幻方【够造奇数阶的魔方阵】

http://noi.openjudge.cn/ch0108/22/ 总时间限制:  1000ms 内存限制:  65535kB 描述 幻方是一个很神奇的N*N矩阵,它的每行.每列与对角线,加起来的数字和都是相同的.我们可以通过以下方法构建一个幻方.(阶数为奇数)1.第一个数字写在第一行的中间2.下一个数字,都写在上一个数字的右上方:    a.如果该数字在第一行,则下一个数字写在最后一行,列数为该数字的右一列    b.如果该数字在最后一列,则下一个数字写在第一列,行数为该数字的上一行    

2015年 day1.1 神奇的幻方

神奇的幻方 题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. 之后,按如下方式从小到大依次填写每个数K(K=2,3,…,N*N): 1.若(K−1)在第一行但不在最后一列,则将K填在最后一行,(K−1)所在列的右一列: 2.若(K−1)在最后一列但不在第一行,则将K填在第一列,(K−1)所在行的上一行: 3.若(K−1)在第一行最后一列,则将K填

百练2755 神奇的口袋 【深搜】or【动规】or【普通递归】or【递推】

总Time Limit:  10000ms  Memory Limit:  65536kB 有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40.John现在有n个想要得到的物品,每个物品的体积分别是a1,a2--an.John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品.现在的问题是,John有多少种不同的选择物品的方式. Input 输入的第一行是正整数n (1 <= n <= 20),表示不

字典树 - 神奇的字典

Description 度熊手上有一本神奇的字典,你可以在它里面做如下三个操作:   1.insert : 往神奇字典中插入一个单词   2.delete: 在神奇字典中删除所有前缀等于给定字符串的单词   3.search: 查询是否在神奇字典中有一个字符串的前缀等于给定的字符串 Input 这里仅有一组测试数据.第一行输入一个正整数N (1 <= N <= 100000),代表度熊对于字典的操作次数,接下来N行,每行包含两个字符串,中间中用空格隔开.第一个字符串代表了相关的操作(包括: i

连续调用scanf的问题总结

对于非常简单的scanf函数,一直使用,但是却是有很多的知识点没有掌握好,现总结如下: 1.多个scanf之后,后序以 scanf("%c",&c) 当程序连续调用scanf 函数的,前面的获得输入接收的时候,一般结束都是以一个空白字符(空格.enter),比如enter 结束输入:但是,当后面接着是还有一个以 scanf("%c",&a) 的时候,则上面输出 enter 作为结束符,就会被输入到a,从而导致了a 为空. (1)以%d 继续输入的时候

BZOJ 1416: [NOI2006]神奇的口袋( 高精度 )

把x1~xn当成是1~n, 答案是不会变的. 然后直接模拟就行了...... P.S 双倍经验... BZOJ1416 && BZOJ1498 ------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; c