001_安全函数_C_safe_library

本文详细介绍了微软实现的一些字符串安全函数,如memset_s用于初始化内存,strncpy_s和strcat_s提供长度限制,以及memcpy_s和scanf_s的安全版本,强调了它们在防止内存溢出和保证程序安全的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于操作字符串的安全函数,这些并非是C标准中的,是微软实现的,所以可能你在没有引入实现的情况下去运用,就会提示没有发现此定义类似提示,如下:

所以要把实现的函数引入,再用,如下:

 将实现函数及头文件引入,然后进行使用,下面我们开始:

【函数1: memset_s】

【格式】

memset_s(dest, sizeof(dest), int n, sizeof(dest))

【功能】

初始化某一块指定长度(一般情况下为字符数组)的内存为n值,或者理解为:初始化内存内容为某一数值,也可以的

【入参】

dest:内存块地址
sizeof(dest):内存块总大小
n:要被初始化的值,一般情况下是0
sizeof(dest):要被初始化的内存长度

【返回值】

这里我们对这个返回值一般不关心,因为我们主要是初始化内存块,获得返回值对于我们来说也并没有什么太大的用,是作为返回给系统进行校验的,这个有兴趣的可以去查询下关于size_t,errno_t, rsize_t类型的定义和含义,我们这里主要介绍函数功能和使用

【test_code】

 【总结】

这个memset_s函数比我们的void * memset(void *s, int c, size_t n);函数多了一个内存总长度的入参,在初始化时候,编译器会检验是否超过内存总长度,所以相对而言比较安全

【函数2: strncpy_s】

【格式】

char* strncpy_s(char *dest, sizeof(dest),char *src, strlen(src));

【功能】

向目的内存拷贝一定长度的字符串

【入参】

dest:目的地址
sizeof(dest):可拷贝大小
src:待拷贝的字符串
strlen(src):待拷贝的字符串有效长度

【test_code】

 【总结】

我们可以看下:

char *strcpy(char *dest, const char *src);

char *strncpy(char *dest, const char *src, int n);

char* strncpy_s(char *dest, sizeof(dest),char *src, strlen(src));

会发现其实后来的cp函数比原来的在长度和安全区做了更多的限定,所以才"安全"!

【函数3: strcat_s】

【格式】

errno_t strcat_s(char *strDest, size_t destMax, const char *strSrc);

【功能】

在一个字符串后面追加上另外一个字符串

【入参】

strDest:目的地址
destMax:目的地址最大容量
char:待操作的字符串

【返回值】

返回一个整数,0表示复制成功,返回非0值代表复制不成功,不同的值表示不同的错误,具体内容可以查阅MSDN手册

【test_code】

输出结果:

【总结】

 char *strcat(char *dest, const char *src);

char *strncat(char *dest, const char *src, size_t n);

 errno_t strcat_s(char *strDest, size_t destMax, const char *strSrc);

可以看出多了一个参数,长度限定

#include <stdio.h>
#include <string.h>
#include "./head/log.h"


int main()
{
        char src[MAXS] = "I am okay!";
        char dest[MAXS];
        char *p = "hello";

        memset(dest, 0, sizeof(dest));
        //dest[MAXS] = "hello";只有在初始化时才可以这样赋值,后面只能用copy函数赋值
        strncpy(dest, p, sizeof(char)*6);
        LOGS(dest);
        LOG();
        strcat(dest, src);
        LOGS(dest);

        strncat(dest, src, sizeof(char)*10);
        LOG();
        LOGS(dest);
        return SUCCESS;
}
输出:
hello
===============
helloI am okay!
===============
helloI am okay!I am okay!

/*strcat_s验证*/
void cats()
{
    char dest[200] = "I am ...";
    char src[] = "hello day";

    LOGS(dest);//I am ...
    strcat_s(dest, sizeof(dest), src);
    LOG();
    LOGS(dest);//I am ...hello day
}
int main()
{
    char dest[MAXS];
    char *str = "it is not a good idea in life!";


    memset_s(dest, sizeof(dest), 0, sizeof(dest));
    LOGS("str", str);
    LOGS("dest", dest);
    LOG();
    strcat(dest, str);
    LOGS("strcat_dest", dest);
    memset(dest, 0, sizeof(dest));
    strcat_s(dest, sizeof(dest), str);
    LOG();
    LOGS("strcat_s__dest", dest);

    return SUCCESS;
}

【函数4: memcpy_s】

【格式】

errno_t memcpy_s(char *dest , size_t big , const char *src , size_t count);

【功能】

拷贝一定长度的字符串到目的内存dest

【入参】

dest:拷贝完成之后的字符串地址
big: dest的大小,一般都是sizeof(dest)
src:需要拷贝的字符串
count:需要拷贝的字符串长度

注意:big必须大于等于count,否则拷贝将出现中断。

【返回值】

返回一个整数,0表示复制成功,返回非0值代表复制不成功,不同的值表示不同的错误,具体内容可以查阅MSDN手册

【test_code】

 【总结】

void *memcpy(void *destin, void *source, unsigned n);

errno_t memcpy_s(char *dest , size_t big , const char *src , size_t count);

还是多了个范围限定

【函数4: sscanf_s】

【格式】

int sscanf(const char *str, const char *format, ...);

int sscanf_s(const char *buffer, const char *format, ...)

【功能】

将字符或者数值赋予某个变量,这只是简单的一种用法而已

【入参】

buffer:原始数据
format:不定参数,以某种格式去把原始数据操作存储或者赋予新变量中,以备使用

【返回值】

整形,一般不用来做检验,没多大意义

【test_code】

char input[] = "42";
int number;
sscanf_s(input, "%d", &number, sizeof(number));

char input[] = "42";
int number;
sscanf(input, "%d", &number);

【总结】

可以看出sscanf_s比sscanf多了一个sizeof校验参数,对于操作的变量,所以更安全了哦

【函数4: snprintf_s

【格式】

int snprintf(char *str, size_t size, const char *format, ...);

int snprintf_s(char *str, size_t size, size_t size-1, const char *format, ...);

【功能】

将字符串和数值组合成一个命令

【入参】

str:待操作的地址块
size:待操作的地址块的大小
size-1:允许被操作的地址块大小
format:不定参数,以某种格式去把数据赋值给地址块

【返回值】

整形,可以用了做检验,均可

#include <stdio.h>

int main() {
    char buffer[20];
    int num = 42;

    // Format the data into the buffer
    int num_chars_written = snprintf(buffer, sizeof(buffer), "The number is %d", num);

    // Check if the operation was successful and print the result
    if (num_chars_written < sizeof(buffer)) {
        printf("Formatted string: %s\n", buffer);
    } else {
        printf("Buffer size too small to store the formatted string.\n");
    }

    return 0;
}

【test_code】

char buffer[20];
    int num = 42;

    // Format the data into the buffer
    int num_chars_written = snprintf(buffer, sizeof(buffer), "The number is %d", num);

snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "The number is %d", num);

【总结】

可以看出snprintf_s比snprintf多了一个sizeof校验参数,对于操作的变量,所以更安全了哦

【函数5: fscnaf_s】

这个函数目前用的不多,所以就没有举例演示

【注意】

我们这里用到了多个文件编译,所以需要用Makefile,如下:

这里关于这个不做赘述,我的主页有关于这个的文章,大家有兴趣的可以去看下,就知道怎么会回事了。

### memset_s 函数介绍 `memset_s` 是 C11 标准引入的一个安全版本的 `memset`,旨在解决传统 `memset` 可能带来的安全隐患。它的设计目的是为了防止缓冲区溢出以及提供更可靠的内存初始化方式。 #### 函数原型 以下是 `memset_s` 的函数定义: ```c errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n); ``` - **`s`**: 指向要设置的目标内存区域的指针。 - **`smax`**: 目标内存区域的最大大小(以字节为单位),用于防止缓冲区溢出。 - **`c`**: 用来填充目标内存的值,通常是一个字节形式的整数。 - **`n`**: 需要被设置为目标值的字节数。 如果 `n` 超过了 `smax`,或者传入了非法参数,则会触发错误处理逻辑并返回相应的错误码[^1]。 --- #### 功能特点 1. **安全性增强** - `memset_s` 强制要求指定目标缓冲区的最大尺寸 (`smax`),从而避免因误操作导致的越界写入问题。 - 即使编译器尝试优化掉某些看似无意义的操作(如清理敏感数据),`memset_s` 也会确保实际执行内存写入动作[^1]。 2. **错误检测能力** - 当发生任何异常状况时(比如无效指针、不合理大小等),`memset_s` 不仅可以阻止危险行为的发生,还会通过返回特定的错误代码通知调用方发生了什么问题[^4]。 3. **兼容性考虑** - 对于那些希望保持向前兼容性的应用程序来说,采用 `memset_s` 能够更好地适应未来可能出现的新平台或工具链需求[^1]。 --- #### 使用示例 下面给出一段简单的程序演示如何利用 `memset_s` 来初始化一块动态分配出来的空间: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> int main(){ size_t length = 10; char* dataBlock = malloc(length * sizeof(char)); if(dataBlock){ // Safe initialization using memset_s if(0 == memset_s(dataBlock ,length*sizeof(char),'A', length)){ printf("Data block successfully initialized with 'A'.\n"); for(size_t i=0;i<length;i++) putchar(dataBlock[i]); free(dataBlock); } else{ fprintf(stderr,"Failed to initialize memory safely!\n"); exit(EXIT_FAILURE); } } else{ perror("Memory allocation failed:"); return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` 在此实例中,我们先申请了一片连续的存储单元,接着运用 `memset_s` 将其全部置位成字符 `'A'` 。最后释放资源结束流程。 --- ### 注意事项 尽管 `memset_s` 提供了许多额外保护措施,但在实际应用过程中仍需留意以下几点: - 确认所提供的各个参数均处于合理范围内; - 若目标对象在其生命周期剩余部分不会再被访问到的话,请勿依赖常规方法清除之——改用专门针对此类场景设计的服务例程,例如 FreeBSD 上面提到过的 `explicit_bzero()` 或 Windows 下可用的 `SecureZeroMemory()` [^1]; --- ### 总结 综上所述,相较于普通的 `memset` ,`memset_s` 增加了一个额外的参数来限定最大可操作范围,并且具备更加完善的自我防护机制,在当今追求高效的同时也愈发注重稳定可靠的大环境下显得尤为重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值