一 遇到%n
昨天在写scanf 的输入异常处理时遇到了一个从未见过的格式说明符:%n
sscanf(str, "%d %n", &v, &c)
从运行结果来看,c的值是str的长度。
二 Stack Overflow 上关于%n 的QA
于是我在stack overflow上找到了关于这个格式说明符的QA。
What is the use of the %n format specifier in C?
第一个回答是:
Nothing printed. The argument must be a pointer to a signed int, where the number of characters written so far is stored.
意思是,不会打印任何东西。 这个参数必须是一个有符号整数的指针,它存储它出现之前打印的所有字符数。
并给了一个例子:
#include <stdio.h>
int main()
{
int val;
printf("blah %n blah\n", &val);
printf("val = %d\n", val);
return 0;
}
结果是:
blah blah
val = 5
第二个答案给了另一个例子:
int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");
结果是:
hello: Foo
Bar
三 我的运行异常和调试情况
我决定手动运行一下。
我的程序如下:
#include <stdio.h>
int main(void)
{
/* test 1*/
int val;
printf("blah %n blah\n", &val);
printf("val = %d\n", val);
/* test 2*/
int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");
return 0;
}
我先用MinGW的gcc进行编译运行:
gcc -g %n.c -o %n
结果运行时,一直在打印空格,控制台旁边的滑块迅速向下滑行,过了好一会才有字符出现。
我试着用gdb进行调试:
7 printf("blah %n blah\n", &val);
(gdb) p val
$1 = 4194432
(gdb) n
warning: Invalid parameter passed to C runtime function.
blah 8 printf("val = %d\n", val);
(gdb) p val
$2 = 4194432
发现声明变量时和运行printf("blah %n blah\n", &val)
后,val的值并没有发生改变,而且抛出了一个warning:Invalid parameter passed to C runtime function. 对于test 2也是同样的情况。
接着我试着用Developer Command Prompt for VS 2017对该程序进行编译运行:
cl %n.c
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.10.25019 版
版权所有(C) Microsoft Corporation。保留所有权利。
%n.c
Microsoft (R) Incremental Linker Version 14.10.25019.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:%n.exe
%n.obj
运行:%n
,结果:
四 用在线IDE进行测试
为什么我这里运行异常呢?
我另外找了两个在线IDE测试。以下是截图:
1、http://ide.geeksforgeeks.org/
结果都是正常。
五 如何解决“Invalid parameter passed to C runtime function”?
最后我的注意力转移到如何解决“ Invalid parameter passed to C runtime function.”这个问题上来了。
于是,我又跑到Stack Overflow上寻找答案了。how-to-debug-invalid-parameter-passed-to-c-runtime-function
第一个答案建议用gcc 配合选项进行设置调试命令。我试了一个用c99标准和c11标准,竟然都可以成功运行!
gcc -g %n.c -o %n_c99 -std=c99
%n_c99
blah blah
val = 5
hello: Foo
Bar
gcc -g %n.c -o %n_c11 -std=c11
%n_c11
blah blah
val = 5
hello: Foo
Bar
但是单纯的不加选项,就不能成功运行。
接着我找到了:warning-invalid-parameter-passed-to-c
按照里面的方式,我设置了断点并run:
(gdb) b OutputDebugStringA
(gdb) r
Starting program: C:\Users\\Desktop/%n_.exe
[New Thread 2748.0x26d8]
[New Thread 2748.0x167c]
Breakpoint 1, 0x773cb540 in OutputDebugStringA () from C:\WINDOWS\SysWOW64\KernelBase.dll
(gdb) bt
#0 0x773cb540 in OutputDebugStringA () from C:\WINDOWS\SysWOW64\KernelBase.dll
#1 0x7634811f in msvcrt!_invalid_parameter () from C:\WINDOWS\SysWOW64\msvcrt.dll
#2 0x762f5a40 in wctype () from C:\WINDOWS\SysWOW64\msvcrt.dll
#3 0x00010001 in ?? ()
#4 0x7636a9b7 in vswprintf () from C:\WINDOWS\SysWOW64\msvcrt.dll
#5 0x7636419b in printf () from C:\WINDOWS\SysWOW64\msvcrt.dll
#6 0x763a2628 in msvcrt!_iob () from C:\WINDOWS\SysWOW64\msvcrt.dll
#7 0x00405064 in __register_frame_info ()
#8 0x00401482 in main () at %n.c:7
看来问题比想象中复杂。我的初步猜想是Window环境下C语言在某些版本中,不允许 在printf中使用%n对于有符号整数的赋值。把这个问题放在这里,希望以后随着能力的增长,能够解决。