C语⾔总结-printfscanf详细总结
⼀、printf()
1、基本⽤法-printf( 格式字符串, 待打印项1, 待打印项2,…);
格式字符串:这些符号被称为转换说明 (conversion specification),它们指定了如何把数据转换成可显⽰的形式。在%和转换字符之间插⼊修饰符可修饰基本的转换说明。
类型匹配:函数打印数据的指令要与待打印数据的类型相匹配。例如, 打印整数时使⽤%d,打印字符时使⽤%c。
待打印项:它们可以是变量、常量,甚 ⾄是在打印之前先要计算的表达式。
define的基本用法
2、转化说明与修饰符(含标记)
转化说明 主要描述了 按什么数据类型输出。如%d 以⼗进制有符号的整数,%u ⼗进制⽆符号整数,%x 以⼗六进制⽆符号整数(字符⼩写,X字符⼤写) %c单个字符,%s字符串 %p 指针输出。(由于使⽤%符号来标识转换说明,因此打印%符号约定使⽤两个%)
修饰符:在%和转换字符之间插⼊修饰符可修饰基本的转换说明,⽤于描述 (1)在打印位置的对齐⽅
式(2)长度不够时前导空格或者0(3)特殊进制添加前缀-#使⽤0开头打印8进制,0x开头打印⼗六进制。(4)最⼩宽度 (5)精度 (6)标识整形长度类型,如h位short长度、hh为char长度类型、l为long长度类型,ll为longlong型长度类型;
修饰符顺序要求: 如果要插⼊多个字符,其书写顺序应该与表 中列出的顺序相同。不是所有的组合都可⾏。
下表列出可作为修饰符的合法字符:
3、转化说明与待打印项不匹配分析
格式字符串中的转换说明⼀定要与后⾯的每个项相匹配,若忘记这个基 本要求会导致严重的后果。
1、数量不匹配问题
printf(“The score was Squids %d, Slugs %d.\n”, score1);
这⾥,第2个%d没有对应任何项。系统不同,导致的结果也不同。出现这种问题最好的状况是得到⽆意义的值,也可能保留系统堆栈中的关键信息,被攻击获取。
2、类型不匹配问题
通常都有多种选 择。例如,如果要打印⼀个int类型的值,可以使⽤%d、%x或%o。打印double类型的值时,可使⽤%f、%e或%g。但是如果不匹配时,如下:
符号不匹配 :⼀个负数按⽆符号输出,输出其负数按补码表⽰的整数。
长度不匹配 :使⽤短类型输出长类型时,会发⽣截断。只读取其最低⼏个和短类型等长的字节(类似除余)。
混淆整型和浮点型:使⽤整形打印浮点数会失败,⽽使⽤浮点数打印整形时,printf会尝试读取按浮点数长度的预期字节长度,如果整形⼩于预期会出现越界读取,打印出来的数据⽆意义。
#define PAGES 336
#define WORDS 65618
int main(void) {
short num = PAGES;
short mnum = -PAGES;
printf("num as short and unsigned short: %hd %hu\n", num,num);
// 输出 num as short and unsigned short: 336 336
printf("-num as short and unsigned short: %hd %hu\n", mnum,mnum);
// 输出 -num as short and unsigned short: -336 65200
/* short ⼤⼩是2字节使⽤⼆进制补码,0~32767代表正数,32768~65535为负数。
65535表⽰-1,65534 表⽰-2,-336表⽰为65200 */
printf("num as int and char: %d %c\n", num, num);
// 输出 num as int and char: 336 P
/*只看储存336的2字节中的后1字节,截断相当于⽤⼀个整数除以256,
只保留其余数是80,对应的ASCII值是字符P */
printf("WORDS as int, short, and char: %d %hd %c\n",WORDS,WORDS,WORDS);
//输出 WORDS as int, short, and char: 65618 82 R
return 0;
}
int main(void) {
float n1 = 3.0;
double n2 = 3.0;
long n3 = 2000000000;
long n4 = 1234567890;
printf("%.1e %.1e %.1e %.1e\n", n1, n2, n3, n4);
/
/ 输出 3.0e+000 3.0e+000 3.1e+046 3.6e+266
/* %e转换说明让printf函数认为待打印的值是double类型8字节。
除了查看n3的4字节外,还会相邻的4字节共8字节单元组合解释成浮点数,
最终得到的结果是⽆意义的值 */
printf("%ld %ld\n", n3, n4);
// 输出 2000000000 1234567890
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
// 输出 0 1074266112 0 1074266112
/* %ld转换说明打印浮点数会失败,在⾥⽤%ld打印long类型的数也失败,
问题出在C如何把信息传递给函数。具体情况因编译器实现⽽异 */
return 0;
}
4、printf的参数传递机制
参数传递机制因编译器实现(⼊栈的顺序关系)⽽异。要特别注意
上⼀个举例中的如printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
int main(void) {
float n1 = 3.0;
double n2 = 3.0;
long n3 = 2000000000;
long n4 = 1234567890;
printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
// 输出 0 1074266112 0 1074266112
/
* %ld转换说明打印浮点数会失败,在⾥⽤%ld打印long类型的数也失败,问题出在C如何把信息传递给函数。
具体情况因编译器实现⽽异 */
return 0;
}
1、程序把传⼊的值放⼊被称为栈(stack)的内存区域。
计算机根据变量类型(不是根据转换说明)把这些值放⼊栈中。常见从右往左顺序⼊栈。因此,n1被 储存在栈中,占8字节(float类型被转换成double类型)。同样,n2也在栈中 占8字节,⽽n3和n4在栈中分别占4字节。
2、控制转到printf()函数读取栈中内容
该函 数根据转换说明(不是根据变量类型)从栈中读取值。%ld转换说明表明 printf()应该读取4字节,所以printf()读取栈中的前4字节作为第1个值。这是 n1的前半部分,将被解释成⼀个long类型的整数。根据下⼀个%ld转换说 明,printf()再读取4字节,这是n1的后半部分,将被解释成第2个long类型的 整数。
类似地,根据第3个和第4个%ld,printf()读取n2的前半部 分和后半部分,并解释成两个long类型的整数。因此,对于n3和n4,虽然⽤ 对了转换说明,但printf()还是读错了字节。
3、printf("%d %d %d %d ",i++,–i,++i,i++)说明
不同编译器产⽣不同的结果,原因主要有1:⼊栈的顺序关系 2、i++和++i操作符和⼊栈之间的顺序关系。
如使⽤vscode中测试gcc编译器:先处理完所有⾃增⾃减之后再最后统⼀⼀次性的⼊栈(并且如果发⽣i++或者i–会先缓存值,对应成员⼊栈时按缓存值计算⼊栈值,如果是–i或者++i则⽆缓存 使⽤最终i结果计算⼊栈值) ,测试如下:
int main(void) {
int i = 8;
printf("%d,%d,%d,%d\n", ++i, --i, i++, i--);
// 输出 8,8,7,8
/*  1、先处理++/--
i++(i--):先缓存i的原始值,然后i⾃增(⾃减)。最后⼊栈时,⽤缓存的i的原始值⼊栈。
++i(--i):i⾃增,不缓存i的原始值。最后⼊栈时,是更新后的最终i。
i--: 缓存8,i=7
i++:缓存7,i=8
--i:i=7
++i:i=8(到这⼀步时i=8,也就是说最后⼊栈之前i的最终值为8,因此++i和--i的⼊栈值都是8)
2、最后⼊栈
第⼀个⼊栈值8(存在缓存),第⼆个⼊栈值7(存在缓存),第三个⼊栈值8(此时的i值),第四个⼊栈8(此时的i值)
所以这时候栈的数据是:8,8,7,8.(从顶到底)。
*/
i = 0;
printf("%d %d %d %d\n", i+1, i++, i+2, ++i);
// 输出 3 1 3 2
/* 先处理++/--:++i:i为1, i++:缓存1,i为2,⼊栈:第⼀个为2(此时i值),第⼆个为3,第三个为缓存1,第四个为3,所以这时候栈的数据是 3 1 3 2 (从顶到底)*/
return 0;
}
⼆、scanf()
1、基本⽤法–scanf( 格式字符串, 地址1, 地址2,…);
通⽤终端格式化输⼊函数,它从标准输⼊设备(键盘) 读取输⼊的信息,给变量赋值。
数据分段:scanf()函数使⽤空⽩(换⾏符、制表符和空格)把输⼊分成多个字段。 在依次把转换说明和字段匹配时跳过空⽩。只要在每个输⼊项之间输⼊⾄少⼀个换⾏ 符、空格或制表符即可,可以在⼀⾏或多⾏输⼊。唯⼀例外的是%c会读取每个字符,包括空⽩。
(1)以试读取⼀个%d举例:
每次读取⼀个字符*跳过所有的 空⽩字符,直⾄遇到第1个⾮空⽩字符才开始读取。因为要读取整数,所以希望发现⼀个数字字符或者⼀个符号(+或-)。如果到⼀个数字或 符号,保存并读取下⼀个,直⾄遇到⾮数字字符,便认为读到了整数的末尾。然后把⾮数字字符放回输⼊。这意味着程序在下⼀次读取输⼊时,⾸先读 到的是上⼀次读取丢弃的⾮数字字符。如果使⽤字段宽度,scanf()会在字段结尾或第1个空⽩字符处停⽌读取 (满⾜两个条件之⼀便停⽌)。
(2)其他类型说明:
读取输⼊和⽤%d 的情况相同。区别在于 scanf()会把更多字符识别成数字的⼀部分。%x转换说明要求scanf()识 别⼗六进制数a~f和A~F。浮点转换说明要求scanf()识别⼩数点、e记数法 (指数记数法)和新增的p记数法(⼗六进制指数记数法)。
%s 转换说明,scanf()会读取除空⽩以外的所有字符。scanf()跳 过空⽩开始读取第 1 个⾮空⽩字符,并保存⾮空⽩字符直到再次遇到空⽩。当scanf()把字符串放进指定数组中时,它会在字符序列的末尾加上’\0。
char str[80];
scanf("%s",str); // 输⼊ test is good<;回车>
printf("%s",str); // 输出 test (遇到第⼀个空⽩字符结束)
解决读取包含空格的字符串问题,使⽤扫描字符集合[],多个字符串的输⼊,并对结束符进⾏⾃定义。
char str[80];
scanf("%[^\n]",str); // 输⼊ test is good
// 使⽤扫描字符集合[],多个字符串的输⼊,并对结束符进⾏⾃定义    printf("%s",str); // 输出 test is good
return 0;
2、转化说明与修饰符(含标记)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。