我们知道在字符串函数中,strcpy,strcat,strerror,strtok,strncpy,strncat,strlen存在字符串溢出的问题,从而引发安全性问题,本篇介绍相当的安全替代函数。strcpy_s,strcat_s,strerror_s,strtok_s,strncpy_s,strncat_s,memcpy_s,memmove_s下面看具体的示例
实验环境:window11 Qt 5.12.9
strcpy / strcpy_s example
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <inttypes.h>
int main(void)
{
char dst[4];
char dst1[4];
char dst2[10];
char src[] = "Too long!";
char *ds = strcpy(dst1, src);
printf("dst1 = \"%s\", ds = %s\n", dst1, ds);
printf("dst1 = \"%p\", ds = %p\n", dst1, ds);
//根据返回值来判断字符复制是否正确 0:表示正确,非0表示错误
//strcpy_s(char *_Dst, rsize_t _SizeInBytes, const char *_Src);
int r = strcpy_s(dst, sizeof dst, src);
printf("dst = \"%s\", r = %d\n", dst, r);
if(r != 0)
{
printf("error:%d %s\n", r, strerror(errno));
}
int r2 = strcpy_s(dst2, sizeof dst2, src);
printf("dst2 = \"%s\", r2 = %d\n", dst2, r2);
printf("src = \"%s\", src = %p\n", src, src);
printf("Hello World!\n");
return 0;
}
运行结果:
参考: https://zh.cppreference.com/w/c/string/byte/strcpy
strcat / strcat_s example
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <inttypes.h>
int main(void)
{
char dst[4];
char dst1[4];
char dst2[10];
char src[] = "Too long!";
char *ds = strcat(dst1, src);
printf("dst1 = \"%s\", ds = %s\n", dst1, ds);
printf("dst1 = \"%p\", ds = %p\n", dst1, ds);
//根据返回值来判断字符连接是否正确 0:表示正确,非0表示错误
//strcat_s(char *_Dst, rsize_t _SizeInBytes, const char * _Src);
int r = strcat_s(dst, sizeof dst, src);
printf("dst = \"%s\", r = %d\n", dst, r);
if(r != 0)
{
printf("error:%d %s\n", r, strerror(errno));
}
int r2 = strcat_s(dst2, sizeof dst2, src);
printf("dst2 = \"%s\", r2 = %d\n", dst2, r2);
printf("src = \"%s\", src = %p\n", src, src);
printf("strcat Hello World!\n");
return 0;
}
运行结果:
参考:
https://zh.cppreference.com/w/c/string/byte/strcat
strerror / strerror_s example
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <inttypes.h>
int main(void)
{
FILE *fp = fopen(tmpnam((char[L_tmpnam]){0}), "r");
if(fp==NULL) {
printf("File opening error: %s\n", strerror(errno));
setlocale(LC_ALL, "de_DE.utf8");
char *msg = strerror(errno);
printf("Now in German: %s len=%lld\n", msg, strlen(msg));
setlocale(LC_ALL, "zh_CN.utf8"); // printf 需要多字节输出的 CTYPE
size_t errmsglen = strlen(msg) + 1;
char errmsg[errmsglen];
//strerror_s(char *_Buf,size_t _SizeInBytes,int _ErrNum);
int ret = strerror_s(errmsg, errmsglen, errno);
printf("Now in Chinese: %s ret = %d\n", errmsg, ret);
}
printf("strerror_s Hello World!\n");
return 0;
}
运行结果:
参考:
https://zh.cppreference.com/w/c/string/byte/strerror
strtok_s / strtok example
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <inttypes.h>
int main(void)
{
char input[] = "A bird came down the walk";
printf("Parsing the input string '%s'\n", input);
char* token = strtok(input, " ");
while (token) {
puts(token);
token = strtok(NULL, " ");
}
printf("Contents of the input string now: '");
for (size_t n = 0; n < sizeof input; ++n)
input[n] ? putchar(input[n]) : fputs("\\0", stdout);
puts("'");
char str[] = "A bird came down the walk";
rsize_t strmax = sizeof str;
const char* delim = " ";
char* next_token;
printf("Parsing the input string '%s'\n", str);
//token = strtok_s(str, &strmax, delim, &next_token);
token = strtok_s(str, delim, &next_token);
//char *_Str,const char *_Delim, char **_Context
//_Str - 是要分割的字符串; 指向要记号化的空终止字节字符串的指针
//delim - 是分隔符; 指向标识分隔符的空终止字节字符串的指针
//_Context - 是保存分割状态的指针; 指向 char* 类型对象的指针,strtok_s 以之存储其内部状态
//在第一次调用strok_s函数时,需要将str传递给它,并在后续的调用中将context设置为NULL。每次调用strok_s函数后,
//它会返回分隔后的字符串部分,并将context指向下一个部分的位置,直到字符串被完全分割为止。
while (token) {
puts(token);
//token = strtok_s(NULL, &strmax, delim, &next_token);
printf("next_token============= '%s' token====%s\n", next_token, token);
token = strtok_s(NULL, delim, &next_token);
}
printf("Contents of the input string now: '");
for (size_t n = 0; n < sizeof str; ++n)
str[n] ? putchar(str[n]) : fputs("\\0", stdout);
puts("'");
printf("Hello World!\n");
return 0;
}
参考:
https://zh.cppreference.com/w/c/string/byte/strtok
strncpy_s / strncpy example
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <inttypes.h>
int main(void)
{
char src[] = "hi";
char dest[6] = "abcdef"; // 无空字符
strncpy(dest, src, 5); // 写入五个字符 'h', 'i', '\0', '\0', '\0' 到 dest
printf("strncpy(dest, src, 5) to a 6-byte dest gives : ");
for (size_t n = 0; n < sizeof dest; ++n) {
char c = dest[n];
c ? printf("'%c' ", c) : printf("'\\0' ");
}
printf("\nstrncpy(dest2, src, 2) to a 2-byte dst gives : ");
char dest2[2];
strncpy(dest2, src, 2); // 截断:写入二个字符 'h', 'i', 到 dest2
for (size_t n = 0; n < sizeof dest2; ++n) {
char c = dest2[n];
c ? printf("'%c' ", c) : printf("'\\0' ");
}
printf("\n");
char dst1[6], src1[100] = "hello";
int r1 = strncpy_s(dst1, 6, src1, 100); // 写入 0 到 r1,6 个字符到 dst1
printf("dst1 = \"%s\", r1 = %d\n", dst1, r1); // 'h','e','l','l','o','\0' 到 dst1
//strncpy_s(char *_Dst, size_t _DstSizeInChars, const char *_Src, size_t _MaxCount);
char dst2[5], src2[7] = { 'g','o','o','d','b','y','e' };
int r2 = strncpy_s(dst2, 5, src2, 7); // 复制溢出目标数组
printf("dst2 = \"%s\", r2 = %d\n", dst2, r2); // 写入非零到 r2,'\0' 到 dst2[0]
char dst3[5];
int r3 = strncpy_s(dst3, 5, src2, 4); // 写入 0 到 r3,5 个字符到 dst3
printf("dst3 = \"%s\", r3 = %d\n", dst3, r3); // 'g', 'o', 'o', 'd', '\0' 到 dst3
return 0;
}
运行结果:
参考:
https://zh.cppreference.com/w/c/string/byte/strncpy
strncat_s / strncat example
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <inttypes.h>
int main(void)
{
char str[50] = "Hello ";
char str2[50] = "World!";
strcat(str, str2);
strncat(str, " Goodbye World!", 3);
puts(str);
char s1[100] = "good";
char s5[1000] = "bye";
//strncat_s(char *_Dst,size_t _DstSizeInChars,const char *_Src,size_t _MaxCount);
int r1 = strncat_s(s1, 100, s5, 1000); // r1 为 0,s1 保有 "goodbye\0"
printf("s1 = %s, r1 = %d\n", s1, r1);
char s2[6] = "hello";
int r2 = strncat_s(s2, 6, "", 1); // r2 为 0,s2 保有 "hello\0"
printf("s2 = %s, r2 = %d\n", s2, r2);
char s3[6] = "hello";
int r3 = strncat_s(s3, 6, "X", 2); // r3 非零,s3 保有 "\0"
printf("s3 = %s, r3 = %d\n", s3, r3);
// strncat_s 截断惯用法:
char s4[7] = "abc";
int r4 = strncat_s(s4, 7, "defghijklmn", 3); // r4 为 0,s4 保有 "abcdef\0"
printf("s4 = %s, r4 = %d\n", s4, r4);
return 0;
}
运行结果:
参考:
https://zh.cppreference.com/w/c/string/byte/strncat
memcpy_s / memcpy example
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <inttypes.h>
int main(void)
{
// 简单用法
char source[] = "once upon a midnight dreary...", dest[4];
memcpy(dest, source, sizeof dest);
for(size_t n = 0; n < sizeof dest; ++n)
putchar(dest[n]);
// 设置分配的内存的有效类型为 int
int *p = malloc(3*sizeof(int)); // 分配的内存无有效类型
int arr[3] = {1,2,3};
memcpy(p,arr,3*sizeof(int)); // 分配的内存现在拥有有效类型
// reinterpreting data
double d = 0.1;
// int64_t n = *(int64_t*)(&d); // 严格别名使用违规
long long n;
memcpy(&n, &d, sizeof d); // OK
printf("\n%a is %" PRIx64 " as an int64_t\n", d, n);
char src[] = "aaaaaaaaaa";
char dst[] = "xyxyxyxyxy";
//errno_t __cdecl memcpy_s (void *_dest,size_t _numberOfElements,const void *_src,size_t _count);
int r = memcpy_s(dst,sizeof dst,src,5);
printf("dst = \"%s\", r = %d\n", dst,r);
r = memcpy_s(dst,5,src,10); // count 大于 destsz
printf("dst = \"");
for(size_t ndx=0; ndx<sizeof dst; ++ndx) {
char c = dst[ndx];
c ? printf("%c", c) : printf("\\0");
}
printf("\", r = %d\n", r); //r = 0表示成功,非0表示失败
return 0;
}
运行结果:
参考:
https://zh.cppreference.com/w/c/string/byte/memcpy
memmove_s / memmove example
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <locale.h>
#include <inttypes.h>
int main(void)
{
char str[] = "1234567890";
puts(str);
memmove(str+4, str+3, 3); // 从 [4,5,6] 复制到 [5,6,7]
puts(str);
// 设置分配的内存的有效类型为 int
int *p = malloc(3*sizeof(int)); // 分配的内存无有效类型
int arr[3] = {1,2,3};
memmove(p,arr,3*sizeof(int)); // 分配的内存现在拥有有效类型
// 转译数据
double d = 0.1;
// int64_t n = *(int64_t*)(&d); // 严格别名使用违规
int64_t n;
memmove(&n, &d, sizeof d); // OK
printf("%a is %" PRIx64 " as an int64_t\n", d, n);
char src[] = "aaaaaaaaaa";
char dst[] = "xyxyxyxyxy";
//errno_t __cdecl memmove_s(void *_dest,size_t _numberOfElements,const void *_src,size_t _count);
int r = memmove_s(dst,sizeof dst,src,5);//返回值r=0表示正常,非0表示失败
printf("dst = \"%s\", r = %d\n", dst,r);
r = memmove_s(dst,5,src,10); // count 大于 destsz
printf("dst = \"");
for(size_t ndx=0; ndx<sizeof dst; ++ndx) {
char c = dst[ndx];
c ? printf("%c", c) : printf("\\0");
}
printf("\", r = %d\n", r);
return 0;
}
运行结果:
参考:
https://zh.cppreference.com/w/c/string/byte/memmove