C++ strtok(), strtok_s() 字符串分割,strncpy(),strncpy_s() 字符串复制

今天发现在 VS2017上正常运行的代码到 ubuntu 下编译出错,gcc 提示如下错误:

error: ‘strncpy_s’ was not declared in this scope strncpy_s(buf, strIp, 16)

error: ‘strtok_s’ was not declared in this scope

遂查找资料后记录。

 //strtok()函数原型
 _Check_return_ _CRT_INSECURE_DEPRECATE(strtok_s) _CRTIMP char *  __cdecl strtok(_Inout_opt_z_ char * _Str, _In_z_ const char * _Delim);

当strtok()在参数_Str的字符串中发现参数_Delim中包涵的分割字符时,则会将该字符改为\0 字符。

在第一次调用时,strtok()必需给予参数_Str字符串,往后的调用则将参数_Str设置成NULL。每次调用成功则返回指向被分割出片段的指针。

需要注意的是,使用该函数进行字符串分割时,会破坏被分解字符串的完整,调用前和调用后的s已经不一样了。

第一次分割之后,原字符串str是分割完成之后的第一个字符串,剩余的字符串存储在一个静态变量中。

//将字符串"ab,cde,fghi"按照","分割。char str[] = "ab,cde,fghi";
char *p[4];
p[0]= strtok(str, ",");
int i = 0;
while (p[i] != NULL)
{
    
    
    i++;
    p[i]= strtok(NULL, ",");
}
p[i] = "\0";
for (int i = 0; i < 4; i++)
{
    
    
    cout << p[i] << endl;
}

在这里插入图片描述

//strtok_s()函数原型
_Check_return_ _CRTIMP_ALTERNATIVE char *  __cdecl strtok_s(_Inout_opt_z_ char * _Str, _In_z_ const char * _Delim, _Inout_ _Deref_prepost_opt_z_ char ** _Context);

strtok_s 仍然会破坏原字符串的完整性。但是函数将剩余的字符串存储在_Context变量中,而不是静态变量中,从而保证了安全性。

char ** _Context参数是一个指向char *的指针变量,用来在strtok_s内部保存切分时的上下文,以应对连续调用分解相同源字符串。

strtok函数在提取字符串时使用了静态缓冲区,因此,线程不安全。如果要顾及到线程的安全性,应该使用strtok_s。

strtok_s实际上就是将strtok内部隐式保存的this指针,以参数的形式与函数外部进行交互。由调用者进行传递、保存甚至是修改。

需要调用者在连续切分相同源字符串时,除了将str参数赋值为NULL,还要传递上次切分时保存下的_Context。

strtok_s和strtok_r有相同的实现形式和功能,不同的是strtok_s是windows环境下的实现形式,是从VS2005开始推出的版本,而strtok_r是Linux下的实现形式,因此之前我的代码在linux下会报错。

对于strtok_s和strtok_r,我们可以对其进行封装成更加单的实现形式:


#if defined __GNUC__
#define STRTOK(savP, delm)     strtok_r(nullptr, delm, &savP)
#elif defined _MSC_VER
#define STRTOK(savP, delm)     strtok_s(nullptr, delm, &savP)
#endif    
//将字符串"hello world,my name is congcong."按照","分割。char str[] = "hello world,my name is congcong.";
char *p[3];
char *buf;
p[0]=strtok_s(str, ",",&buf);
int i = 0;
while (p[i])
{
    
    
    i++;
    p[i] = strtok_s(NULL, ",", &buf);
}
p[i] = "\0";
for (int i = 0; i < 3; i++)
{
    
    
    cout << p[i] << endl;
}

在这里插入图片描述

//strcpy()函数原型
char *strcpy(char *dst,const char *src)

将以src为首地址的字符串复制到以dst为首地址的字符串,包括 ‘\0’ 结束符,返回dst地址。要求:src和dst所指内存区域不可以重叠且dst必须有足够的空间来容纳src的字符串,若dst空间不足,编译时并不会报错,但执行时因系统不同会出现不同的错误信息。

strcpy() 是依据源字符串中的 '\0’作为结束判断的,不检查copy 前的 Buffer 的大小,如果目标空间不够,就会有 BufferOverflow 问题。也就是说会连着 src 的 '\0’结束符也 copy,因此假如 src 大小是4,则 dst 的大小最少是 5。

//strncpy()函数原型
char *strncpy(char *dst,const char *src,size_t len)

从以src为首地址的字符串中之多复制len个字符到以dst为首地址的字符串。如果在[0,len]之间没有 ‘\0’ 结束符,则dst中没有结束符。如果len大于src的长度,则dst中多余位置自动置为null。

如果len小于src的长度,则dst中没有结束符,dst输出时会出现乱码,直至碰到’\0’结束符为止。

//strcpy_s()函数原型
strcpy_s( char *dst,   size_t num,   const char *src )

该函数是VS2005之后的VS提供的,并非C标准函数,而加_s版本之所以安全,是因为他们在接口增加了一个参数numElems来表明dest中的字节数。

同strcpy()函数功能相同,不同之处在于参数中多了个size_t类型的参数,该参数为字符串dst的长度,当存在缓存区溢出的问题时(即src的长度大于dst的长度),strcpy_s()会抛出异常;而strcpy()结果则未定,因为它错误地改变了程序中其他部分的内存的数据,可能不会抛出异常但导致程序数据错误,也可能由于非法内存访问抛出异常。

注意,以上所有的带 _s 版本的并不是标准库,所以,不推荐使用。

猜你喜欢

转载自blog.csdn.net/u012534831/article/details/109576512
今日推荐