C语言scanf()用法大全(非常详细)

scanf 是 scan format 的缩写,意思是格式化扫描,也就是从键盘获得用户输入,和 printf 的功能正好相反。

严格来说,scanf() 是从标准输入文件中读取数据,而这个标准输入文件通常都是“黑底白字”的控制台,或者说是键盘。

C语言 scanf() 位于 头文件中,它的原型为:

int scanf ( const char * format, argument... );

format 为格式字符串,由格式说明符和普通字符构成。其中:

格式说明符以%开头,比如 %d、%s、%c 等,表示要读取什么样的数据;

普通字符按照原样输入,比如英文、数字、逗号、空格等。

argument 为参数列表,或者变量列表,多个参数以,分隔。每个参数都是一个指针,用来指明将数据存储在哪里。参数的个数和类型,要与格式说明符一一对应。

注意:argument 指向的位置必须已被分配内存,并且允许写入。

scanf() 会根据 format 中的格式说明符来读取数据,并将读取到的数据放到 argument 指定的位置。

int 表示 scanf() 的返回值类型,也即处理结果的数据类型:

如果读取成功,scanf() 将返回成功匹配并赋值的个数;

如果读取失败,或者达到文件末尾,或者遇到输入结束的条件,则返回 EOF。

EOF 是在 stdio.h 中定义的宏,它的值在不同的平台或者不同的编译器中可能不同,但通常都是 -1。

format 中的格式说明符

format 中的格式说明符比较复杂,它的标准写法如下:

%[*][width][length]specifier

末尾的 specifier 不能省略,其它由[ ]包围的部分可以省略。

specifier

specifier 是格式字符,它最重要,指明了要读取的数据的类型和形式。

specifier 的用法及说明

specifier

匹配的字符

参数类型

i

整数,前面可以带正号+和负号-。

默认为十进制,带上前缀0表示八进制,带上前缀0x表示十六进制。

int *

d

u

十进制整数,d 表示有符号整数,u 表示无符号整数。

int *

unsigned int *

o

八进制整数(无符号)。

unsigned int *

x

十六进制整数(无符号),可以带有0x或者0X前缀。

unsigned int *

f, e, g

浮点数/小数,前面可以带正号+和负号-,接受普通形式(比如 3.1415)以及科学计数法(比如 5.23e4)。

float *

a

c

单个字符。如果指定的宽度 width 不是 1,那么 scanf() 会读取 width 个字符,并将它们连续存储到参数所指定的位置(末尾不追加任何字符)。

char *

s

字符串,不包含空白符(空格、换行、制表符等)。读取连续的字符,直到遇见第一个空白符就结束读取。读取结束后,scanf() 会自动在末尾追加空字符\0,用以表示字符串的结束。

char *

p

指针/地址。在不同的平台和不同的编译器中,指针的格式可能有所区别,但它始终和在 printf() 中使用%p输出的格式相同。

void **

[characters]

允许读取的字符集合。只有出现在[ ]中的字符会被读取,遇到第一个不符合的字符就结束读取,比如[abcABC]表示读取字母 abc,并且不区分大小写。

注意:这里不强调字符的顺序,只要出现在[ ]中的字符,不管先后,都能匹配成功。

为了简化字符集合的写法,scanf() 支持使用连字符-来表示一个范围内的字符,例如 %[a-z]、%[0-9] 等。

连字符左边的字符对应一个 ASCII 码,连字符右边的字符也对应一个 ASCII 码,位于这两个 ASCII 码范围以内的字符就是要读取的字符。

注意:连字符左边的 ASCII 码要小于右边的,如果反过来,那么它的行为是未定义的。

常用的连字符举例:

%[a-z]表示读取 abc...xyz 范围内的字符,也即小写字母;

%[A-Z]表示读取 ABC...XYZ 范围内的字符,也即大写字母;

%[0-9]表示读取 012...789 范围内的字符,也即十进制数字。

你也可以将它们合并起来,例如:

%[a-zA-Z]表示读取大写字母和小写字母,也即所有英文字母;

%[a-z-A-Z0-9]表示读取所有的英文字母和十进制数字;

%[0-9a-f]表示读取十六进制数字。

char *

[^characters]

不允许读取的字符合集。出现在[ ]中的字符不会被读取。

char *

n

不读取任何字符,只计算截止到目前读取的字符的个数,并将它存储到对应参数指定的位置。

int *

%

% 后面再跟一个 %,表示读取一个 %,类似于 % 的转义形式。

char *

*(星号)

* 表示将读取到的字符丢弃,或者忽略,也即不进行存储。因为没有任何字符需要存储,所以它没有对应的参数。

width

width 表示允许读取的最大字符个数。超过 width 的字符即使符合要求,也不会被读取。

length

length 是 specifier 的子说明符,用来修改对应参数的数据类型,它只能是 hh、h、l、ll、j、z、t、L 其中之一。

length 的用法及说明

specifier

length

d i

u o x

f e g a

c s [] [^]

p

n

默认(不指明length)

int *

unsigned int *

float *

char *

void **

int *

hh

signed char *

unsigned char *

signed char *

h

short int *

unsigned short int *

short int *

l

long int *

unsigned long int *

double *

wchar_t *

long int *

ll

long long int *

unsigned long long int *

long long int *

j

intmax_t *

uintmax_t *

intmax_t *

z

size_t *

size_t *

size_t *

t

ptrdiff_t *

ptrdiff_t *

ptrdiff_t *

L

long double *

上面淡黄色背景的行,为 C99 标准引入的说明符或者子说明符。

C语言 scanf() 用法举例

为了方便读者理解,这里给出几个有代表性的例子。

简单的综合示例

#include

int main()

{

char str[31];

int i;

printf("Enter your name: ");

scanf("%30s", str);

printf("Enter your age: ");

scanf("%d", &i);

printf("Hello %s, you are %d years old.\n", str, i);

printf("Enter a hexadecimal number: ");

scanf("%x", &i);

printf("You have entered %#x(%d).\n", i, i);

return 0;

}

输入示例:

Enter your name: Tom↙

Enter your age: 18↙

Hello Tom, you are 18 years old.

Enter a hexadecimal number: 5e↙

You have entered 0x5e(94).

使用 width 指定读取长度

#include

int main() {

int n;

float f;

char url[23];

scanf("%2d", &n);

scanf("%*[^\n]"); scanf("%*c"); //清空缓冲区

scanf("%5f", &f);

scanf("%*[^\n]"); scanf("%*c"); //清空缓冲区

scanf("%21s", url);

printf("Result: n=%d, f=%g, str=%s\n", n, f, url);

return 0;

}

输入示例①:

52↙

3.1415↙

https://54benniao.com↙

Result: n=52, f=3.141, str=https://54benniao.com

输入示例②:

5201314↙

3.1415926↙

https://www.54benniao.com↙

Result: n=52, f=3.141, str=https://www.54benniao

为了避免受到缓冲区中遗留数据的影响,每次读取结束我们都使用scanf("%*[^\n]"); scanf("%*c");来清空缓冲区。

限制读取数据的长度在实际开发中非常有用,最典型的一个例子就是读取字符串:我们为字符串分配的内存是有限的,用户输入的字符串过长就存放不了了,就会冲刷掉其它的数据,从而导致程序出错甚至崩溃;如果被黑客发现了这个漏洞,就可以构造栈溢出攻击,改变程序的执行流程,甚至执行自己的恶意代码,这对服务器来说简直是灭顶之灾。

匹配特定的字符

%s 说明符会匹配除空白符以外的所有字符,它有两个缺点:

%s 不能读取指定字符,比如只想读取小写字母,或者十进制数字等,%s 就无能为力;

%s 读取到的字符串中不能包含空白符,有些情况会比较尴尬,例如,无法将多个单词存放到一个字符串中,因为单词之间就是以空格为分隔的,%s 遇到空格就读取结束了。

使用 %[xxx] 就可以解决以上问题,请看下面的例子:

#include

int main() {

char str1[30];

char str2[30];

scanf("%[abcd]", str1); //只读取abcd字母

scanf("%*[^\n]"); scanf("%*c"); //清空缓冲区

scanf("%[a-zA-Z]", str2); //只读取小写和大写的英文字母

printf("str1: %s\nstr2: %s", str1, str2);

return 0;

}

输入示例:

baccdaxyz↙

abcXYZ123↙

str1: baccda

str2: abcXYZ

再比如,读取一行不能包含十进制数字的字符串,并且长度不能超过 30:

#include

int main() {

char str[31];

scanf("%30[^0-9\n]", str);

printf("str: %s", str);

return 0;

}

输入示例:

I have been programming for 8 years now↙

str: I have been programming for

Copyright © 2088 斯诺克世界杯_世界杯排名榜 - zhaoxiaotian.com All Rights Reserved.
友情链接