C/C++:strcpy、strlcpy、strncpy、strcpy_s、memcpy

一、C语言中的空字符串、空指针

       str族函数必须保证传入的字符串以 '\0' 结尾,不能是空指针,否则会报错。

1、空指针

char *str1 = nullptr;                    // 空指针
printf("%d\n", strlen(str1));            // 报错,strlen(nullptr)

2、空字符串

//char str2[1] = { '\0' };                 
char *str2 = "";
printf("%d\n", strlen(str2));            // 0

 

二、C语言中的三种 - 字符串拷贝函数

1、strcpy

       strcpy是一种C语言的标准库函数,strcpy把含有'\0'结束符的字符串复制到另一个地址空间,返回指向dest的指针

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

       说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。

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

    assert((dest != NULL) && (src != NULL));

    char* p = dest;
    while((*p++ = *src++) != '\0');

    return dest;
}

2、strlcpy,给定缓冲区大小,截断式拷贝,自动加'\0'

       strlcpy,是更加安全版本的strcpy函数,在已知目的地址空间大小为 size 的情况下,把从 src 地址开始且含有 '\0' 结束符的字符串复制到以 dest 开始的地址空间,并不会造成缓冲区溢出,返回实际拷贝的 src 字符串的大小。

size_t strlcpy(char *dest, const char *src, size_t size)

       说明:保证尾部加 '\0',采用截断方式,strlcpy 并不属于 ANSI C,至今也还不是标准。

#include <stdio.h>
#include <string.h>
#include <assert.h>

/*把字符串src拷贝到dest,dest缓冲区大小为size,截断方式拷贝,返回拷贝的实际大小*/
size_t strlcpy(char *dest, const char *src, size_t size){
	
	assert(dest != NULL && src != NULL);           //
	
	if(size == 0 || size == 1) return 0;     //缓冲区太小,至少为2,可放入至少一个有效字符和一个'\0'
	size_t len = strlen(src);
	if(len == 0) return 0;                   //空串
	
	int n = len <= size-1 ? len : size-1;                //留下一个放'\0'
	for(int i=0; i<n; i++) *dest++ = *src++;
	*dest = '\0';
	
	return n;
}

int main()
{
    char buf[5];
    char src[10] = "12345678";
    strlcpy(buf, src, sizeof(buf));
    printf("%s\n",buf);                   // 输出1234
    return 0;
}

3、strncpy,指定要拷贝多少个字符,不自动加'\0'

       strncpy,从字符串 src 中拷贝指定的 n 个字符 到 字符数组dest 中。

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

      说明:复制字符串 src 中的内容到字符串 dest 中,复制多少由 n 的值决定。如果 src 的前 n 个字符不含 NULL 字符,则结果不会以NULL 字符结束。如果 n <= src 的长度,只是将 src 的前 n 个字符复制到 dest 的前 n 个字符,不自动添加 '\0',也就是结果 dest 不包括 '\0'。如果 n > src 的长度,则以 NULL 填充 dest 直到复制完 n 个字节。src 和 dest 所指内存区域不可以重叠且 dest 必须有足够的空间来容纳 src 的字符长度 + '\0'。

 

/*把字符串中src的前n个字符复制到数组dest中,默认dest是有效地址,其内存大小无从得知,默认src是有效字符串*/
char *strncpy(char *dest, const char *src, int n){
	
	assert(dest != 0 && src != 0);
	
	int len = strlen(src);
	char *p = dest;
	
	if(n <= len){             // 拷贝n个有效字符
		while(n--){
			*p++ = *src++;
		}
	}
	else{                     // 拷贝有效字符,不足n则补上'\0'
		int r = n - len;
		while(len--){
			*p++ = *src++;
		}
		while(r--){
			*p++ = '\0';
		}
	}

	return dest;
}

int main()
{
    char buf[5];
    char src[10] = "12345678";

    strlcpy(buf, src, 5);          // 12345, 无'\0'

    return 0;
}

三、其他拷贝函数

1、strcpy_s,给定缓冲区大小,完整串拷贝,包括原字符串的'\0'

      errno_t strcpy_s(char *dest, size_t size, const char *src);

      必须保证 size >= strlen(src) + 1,即需要考虑拷贝 '\0'。

//需要可以放下包括'\0'在内的大小
void strcpy_s1(char *dest, size_t size, const char *src) {
	
	size_t len;
	
	assert(dest != NULL && src != NULL);
	len = strlen(src) + 1;                  //len包含'\0'
	assert(len <= size);

	while (len--) {
		*dest++ = *src++;
	}
}

2、memcpy,内存拷贝,而非串拷贝,指定要拷贝的字节数

       即可用于字符串拷贝(代替strncpy),也可以用于非字符串类型或其数组的拷贝。两者的区别是字符串拷贝要以 '\0' 来判别字符串的长度,而其他类型的或者其数组没有结尾的 '\0'。

void *memcpy1(void *dest, const void * src, size_t size) {
	if (dest == NULL || src == NULL) { return dest; }

	char *d = (char *)dest;
	char *s = (char *)src;

	while (size--) {
		*d++ = *s++;
	}
	return dest;
}

int main()
{
	int a[4];
	int b[4] = { 1,2,3,4 };

	memcpy(a, b, sizeof(b));    // 1,2,3,4

	return 0;
}

四、用法总结

1、如果 src 是字符串,要拷贝整个字符串,可以使用下列方式。

       字符串可以是指针引用方式,也可以是数组保存方式,两者都可以以 strlen(p)+1 来计算整个串长。

// 默认的拷贝整个串
strcpy(dest, src);


// 指定缓冲区大小
strlcpy(dest, src, sizeof(dest));   // 截断方式,放不下就截断,现在在<string.h>中找不到

strcpy_s(dest, sizeof(dest), src);  // 完整拷贝,放不下就报错


// 指定要拷贝的字节数
strncpy(dest, src, strlen(src)+1);  // 放的超长就补'\0',效率低,现在在<string.h>中找不到

memcpy(dest, src, strlen(src)+1);

2、通常的做法

(1)如果要拷贝整个字符串,strncpy、strlcpy 现在在<string.h>中找不到,strcpy 编译器经常会警告。因此推荐使用的是

strcpy_s(dest, sizeof(dest), src);  // 完整拷贝,放不下就报错

memcpy(dest, src, strlen(src)+1);   // 完整拷贝字符串

(2)如果要拷贝非字符串类型,如 int、class

memcpy(dest, src, sizeof(src)); 

3、strcpy 和 strcpy_s 在安全方面的比较

       当有缓冲区溢出的问题时, 使用 strcpy_s 函数则会抛出一个异常。而使用 strcpy 函数的结果则未定,因为它错误地改变了程序中其他部分的内存的数据,可能不会抛出异常但导致程序数据错误,也可能由于非法内存访问抛出异常。

       使用新的增强安全的CRT函数有什么好处呢?简单地说,新的函数加强了对参数合法性的检查以及缓冲区边界的检查,如果发现错误,会返回 errno 或抛出异常。老版本的这些CRT函数则没有那么严格的检查与校验,如果错误地传输了参数或者缓冲区溢出,那么错误并不能被立刻发现,对于定位程序错误也带来更大困难。

       简单的说,如果存在缓冲区溢出,strcpy_s 在编译期就做了检查,可以检查出错误,而 strcpy 在编译时可能检查不出来,在运行期间溢出时才可能被监测到。

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
strcpystrncpystrncpy_s都是C语言中用于字符串复制的函数。 strcpy函数用于将一个字符串复制到另一个字符串中。它的原型是: char *strcpy(char *dest, const char *src) strncpy函数也用于将一个字符串复制到另一个字符串中,但它还可以指定要复制的最大字符数。它的原型是: char *strncpy(char *dest, const char *src, size_t n) strncpy_s函数是C11标准引入的安全版本的strncpy函数。它提供了更强的安全性,可以预防缓冲区溢出。它的原型是: errno_t strncpy_s(char * str2, rsize_t size2, const char * str1, rsize_t size1) 在这些函数中,dest表示目标字符串,src或strSource表示源字符串,n或size表示要复制的字符数或缓冲区的大小。这些函数都返回指向目标字符串的指针。 需要注意的是,strncpystrncpy_s函数在复制过程中可能不会自动添加字符串结束符'\0',因此在使用这两个函数时,需要手动添加结束符,以确保字符串的正确结束。 总结起来,strcpy是将一个字符串复制到另一个字符串中;strncpy是在指定的字符数内将一个字符串复制到另一个字符串中;strncpy_s是C11标准中引入的安全版本的strncpy函数,可以预防缓冲区溢出。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [strcpystrcpy_s、strncpystrncpy_s 字符串拷贝用法](https://blog.csdn.net/weixin_44084447/article/details/122244567)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值