string.h安全函数学习之strcpy_s,strcat_s,strerror_s,strtok_s,strncpy_s,strncat_s,memcpy_s,memmove_s

我们知道在字符串函数中,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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值