萌新的学习笔记,写错了恳请斧正。
目录
printf/fprintf/sprintf/snprintf
scanf系列的边界检查函数(scanf_s/fscanf_s/sscanf_s)
printf系列的边界检查函数(printf_s/fprintf_s/sprintf_s/snprintf_s)
#include <stdio.h>
int scanf( const char* format, ... );
int fscanf( FILE* stream, const char* format, ... );
int sscanf( const char* buffer, const char* format, ... );
这三个函数从各种资源读取数据,按照格式控制字符串(format)转译,并将结果存储到指定位置
这三个函数返回成功赋值的接受参数的数量(可以为零),出错则返回EOF。
他们的区别在于:
scanf和fscanf已经具体的讲过了
sscanf的特性与fscanf等同,抵达字符串尾('\0')等价于fscanf抵达文件尾的条件
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a = 0;
char* arr = (int*)malloc(100 * sizeof(int));
char str[100] = { 0 };
sscanf(" 114514 afjoiweaj jiojl", "%d%s%s", &a, arr, str);
printf("%d - %s - %s", a, arr, str);
return 0;
}
#include <stdio.h>
int printf( const char* format, ... );
int fprintf( FILE* stream, const char* format, ... );
int sprintf( char* buffer, const char* format, ... );
int snprintf( char* buffer, int bufsz, const char* format, ... );
前三个函数从给定位置加载数据,按一定要求转换为字符串等价物,并写结果到各种地方
第四个函数snprintf就是增加了长度限制的sprintf,其他没有区别
对比四个函数:
#include <stdio.h>
int main()
{
char a[10] = "1919810";
sprintf(a, "%s", "114514");
printf(a);
return 0;
}
#include <stdio.h>
int main()
{
char str[100];
snprintf(str, 100, "Hello %s", "World!");
printf(str);
return 0;
}
首先声明一点,这些为了安全创造的带有下划线s结尾的边界检查函数是在C11标准中定义的可选函数,所以并不是每一个编译器都能编译这个函数,只有在使用的编译器实现定义“__STDC_LIB_EXT1__”而且在包含<stdio.h>前定义“__STDC_WANT_LIB_EXT1__”为整数常量1时才能保证这些函数一定可以使用。当然,不少编译器都默认开启了这些函数,比如在Visual Studio中这些函数就是默认开启的,甚至如果使用不安全的不带边界检查的这些函数还会警告并中断编译(在文件前定义“_CRT_SECURE_NO_WARNINGS”为整数常量1可以忽略不安全函数警告)
//就是加上这一行以禁用不安全函数警告 #define _CRT_SECURE_NO_WARNINGS 1 //后面的1可以省略,默认定义值为1
这些函数相比于传统函数,多了边界检查,可以防止缓冲区溢出
#include <stdio.h>
int scanf_s(const char* format, ...);
int fscanf_s(FILE* stream, const char* format, ...);
int sscanf_s(const char* buffer, const char* format, ...);
机制与传统函数相同,除了:
1.?如果在格式限定中使用了%c 、?%s 或?%[,后面可变参数列表中与其对应的参数变成两个,第一个仍为传统函数的参数,第二个则是类型为rsize_t的一个值,用于限定这个格式匹配符最多能匹配多少个字节的内容。比方说:
#include <stdio.h>
int main()
{
int n = 0, m = 0;
char arr[10] = { 0 };
scanf_s("%d%x%s", &n, &m, arr, (rsize_t)sizeof arr);
printf("n=%d m=%d arr=%s\n", n, m, arr);
return 0;
}
这段代码中,scanf_s中%s对应的参数包括arr和(rsize_t)sizeof arr两个arr是读取后保存的位置,后面则是限定最多读取10个字节(空终止符'\0'是算在内的,所以实际最多读9个)
2.?在运行时额外检测以下违规行为,如果出现违规也返回EOF:
其使用条件与优越性与scanf系列的边界检查函数相同
#include <stdio.h>
int printf_s(const char* format, ...);
int fprintf_s(FILE* stream, const char* format, ...);
int sprintf_s(char* buffer, rsize_t bufsz, const char* format, ...);
int snprintf_s(char* buffer, rsize_t bufsz, const char* format, ...);
其机制与传统函数相同,除了在运行时检测如下违规行为:
关于返回值:
当我们不确定输出字符串的大小时,我们可以利用以零为bufsz和以空指针为buffer的snprintf来决定缓冲区大小。为什么呢?
当bufsz为零、buffer为空指针时,根据上面的介绍,我们知道这只会输出忽略bufsz限制情况下本应写入的字符数,而且不会产生其他影响。而这个输出值,不就是我们需要的缓冲区大小值吗?
下面是一个例子:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
const char* format = "sqrt(2) = %f";
int sz = snprintf(NULL, 0, format, sqrt(2));
char* buf = (char*)malloc(sz + 1); // +1 for '\0'
snprintf(buf, sz + 1, format, sqrt(2));
printf("%s\n", buf);
return 0;
}