字符串复制与赋值,字符串strcpy、strncpy

                                   

                                             字符串复制与赋值

复制:另造一份的意思,地址不同;赋值:起个别名的意思,地址相同。

  • 复制函数:char *strcpy(char* dest, const char *src);

头文件:#include <string.h> 和 #include <stdio.h>

功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间

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

返回指向dest的指针。

实现代码:

char* strcpy(char* des,const char* source)
{
    char* r=des;
    assert((des != NULL) && (source != NULL));
    while((*r++ = *source++)!='\0');

    return des;
}

 注意点:

  1. 入参不能为NULL;
  2. dest所指区域要足够大,否则就会覆盖某些其他未知数据,造成未知错误;
  3. source字符串以复制到'\0'为标记结束,也即source所指向的字符串是以'\0'结尾的,这当中存在风险!

假如source所指向的数据段是一段协议数据,而协议数据中间包含了一些数据0,这时复制就会出现错误,会出现复制不完整的现象。比如char source[10] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x00, 0x47, 0x48, 0x49, 0x4a};

正常复制:

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

int main(){
	
	char* dest = NULL;
	char source[10] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a};
	
	dest = (char *)malloc(strlen(source)+1);
	strcpy(dest, source);
	
	printf("source address is %p, dest address is %p.\n", &source, dest);
	printf("source lenth is %d, source is %s.\n", strlen(source), source);
	printf("dest lenth is %d, dest is %s.\n", strlen(dest), dest);
	
	free(dest);

	return 0;
}

运行环境:ubuntu14.04,平台自带gcc编译器

运行结果:

root@ubuntu:/home/jack# make
gcc -o test test.c -lpthread
root@ubuntu:/home/jack# ./test
source address is 0xbfe65902, dest address is 0xa026008.
source lenth is 10, source is ABCDEFGHIJ.
dest lenth is 10, dest is ABCDEFGHIJ.
root@ubuntu:/home/jack# 

测试程序下载地址:https://download.csdn.net/download/sleeping_sunshine/11596997

没毛病!再来一段:

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

int main(){
	
	char* dest = NULL;
	char source[10] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x00, 0x47, 0x48, 0x49, 0x4a};
	
	dest = (char *)malloc(strlen(source)+1);
	strcpy(dest, source);
	
	printf("source address is %p, dest address is %p.\n", &source, dest);
	printf("source lenth is %d, source is %s.\n", strlen(source), source);
	printf("dest lenth is %d, dest is %s.\n", strlen(dest), dest);
	
	free(dest);

	return 0;
}

运行环境:ubuntu14.04,平台自带gcc编译器

运行结果:

root@ubuntu:/home/jack# make
gcc -o test test.c -lpthread
root@ubuntu:/home/jack# ./test
source address is 0xbf8ae7a2, dest address is 0x939e008.
source lenth is 5, source is ABCDE.
dest lenth is 5, dest is ABCDE.
root@ubuntu:/home/jack# 

看出问题没?

这在工程项目中是很大的问题,工作设备牵涉大量的外围部件,他们与主机通过约定的协议进行数据的传送,往往一串数据中包含很多位的数字 0 ,也就是 结束符 '\0'('\0'的ASCII码是0),所以复制时经常会提前结束;

测试程序下载地址:https://download.csdn.net/download/sleeping_sunshine/11603540

 

  • 赋值
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main(){
	
	char* source = "hello boy!";
	char* dest = NULL;
	
	dest = source;
	
	printf("source address is %p, dest address is %p.\n", source, dest);
	printf("source lenth is %d, source is %s\n", strlen(source), source);
	printf("dest lenth is %d, dest is %s\n", strlen(dest), dest);
	
	return 0;
}

运行环境:ubuntu14.04,平台自带gcc编译器

运行结果:

root@ubuntu:/home/jack# make
gcc -o test test.c -lpthread
root@ubuntu:/home/jack# ./test
source address is 0x8048570, dest address is 0x8048570.
source lenth is 10, source is hello boy!
dest lenth is 10, dest is hello boy!
root@ubuntu:/home/jack# 

可以看出赋值后的目标地址不变的。

测试程序下载地址:https://download.csdn.net/download/sleeping_sunshine/11597057

 

  • 复制函数:char *strncpy(char *destinin, char *source, int maxlen);

destinin:表示复制的目标字符数组;

source:表示复制的源字符数组;

maxlen:表示复制的字符串长度。

一般实现代码:

char *strncpy(char *dest, const char *src, size_t len)
{
	assert(dest!= NULL && src != NULL);

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

	return res;
}

我们在复制之前一般都会知道源字符串或者说是数组的长度,进而进行小于等于源数据长度的复制,但是有没有可能存在这种情况:源字符串长度为5,而要复制10的长度的字符。通常来说只需在assert语句中加一个判断即可避免这种情况,即:

assert((dest!= NULL) && (src != NULL) && (len <= strlen(src)));

那假如我就想随意一点,复制长度任意 ,代码里面该怎么处理。

首先要明确一点:

  1. 如果strlen(src) >= len,那么只有len个字符被复制到dst中,此时如果是复制字符串的话,是没有'\0'结束符的;
  2. 如果strlen(src) < len,dest数组就用额外的NULL字节填充到len长度。

char *strncpy1(char *dest, const char *src, size_t len)
{
	assert(dest != NULL && src != NULL);

	char *res = dest;
	int nullSet = 0;

	if (strlen(src) < len){//src长度小于len
		nullSet = len - strlen(src);
		len = strlen(src);
	}
 
	while (len--){
		*dest++ = *src++;
	}

	if( 0 == nullSet ){
		*dest = '\0';
	}

	while (nullSet--){
		*dest++ = '\0';
	}

	return res;
}

举个例子:

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


char *strncpy1(char *dest, const char *src, size_t len)
{
	assert(dest != NULL && src != NULL);

	char *res = dest;
	int nullSet = 0;

	if (strlen(src) < len){//src长度小于len
		nullSet = len - strlen(src);
		len = strlen(src);
	}
 
	while (len--){
		*dest++ = *src++;
	}

	if( 0 == nullSet ){
		*dest = '\0';
	}

	while (nullSet--){
		*dest++ = '\0';
	}

	return res;
}

int main(){
	
	char* source1 = "hello-boy!";
	char source2[] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a};
	
	char* dest1 = (char *)malloc(5);
	char* dest2 = (char *)malloc(20);
	char* dest3 = (char *)malloc(5);
	char* dest4 = (char *)malloc(20);
	strncpy1(dest1, source1, 5);
	strncpy1(dest2, source1, 20);
	strncpy1(dest3, source2, 5);
	strncpy1(dest4, source2, 20);
	
	printf("source1 address is %p, source1 is %s.\n", source1, source1);
	printf("source2 address is %p, source2 is %s.\n", source2, source2);
	printf("dest1 address is %p, dest1 is %s.\n", dest1, dest1);
	printf("dest2 address is %p, dest2 is %s.\n", dest2, dest2);
	printf("dest3 address is %p, dest3 is %s.\n", dest3, dest3);
	printf("dest4 address is %p, dest4 is %s.\n", dest4, dest4);

	free(dest1);
	free(dest2);
	free(dest3);
	free(dest4);

	return 0;
}

运行环境:ubuntu14.04,平台自带gcc编译器

运行结果:

root@ubuntu:/home/jack# make
gcc -o test test.c -lpthread
root@ubuntu:/home/jack# ./test
source1 address is 0x80488c2, source1 is hello-boy!.
source2 address is 0xbf8d36c2, source2 is ABCDEFGHIJ.
dest1 address is 0x9b05008, dest1 is hello.
dest2 address is 0x9b05018, dest2 is hello-boy!.
dest3 address is 0x9b05030, dest3 is ABCDE.
dest4 address is 0x9b05040, dest4 is ABCDEFGHIJ.
root@ubuntu:/home/jack# 

可以看出,即使复制的长度大于src字符串的长度,也可以正确复制。(用库函数strcpy()当然也可以完成此功能,此处书写的strcpy1()只为了说明len大于strlen(src)情况下的处理)。

测试程序下载地址:https://download.csdn.net/download/sleeping_sunshine/11600165

既然指定了复制的字节数,那么复制的前len个字节中包含有0x00,会不会提前终止呢?

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


int main(){
	
	char* source1 = "hello-boy!";
	char source2[] = {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x00, 0x49, 0x4a};
	
	char* dest1 = (char *)malloc(5);
	char* dest2 = (char *)malloc(20);
	char* dest3 = (char *)malloc(5);
	char* dest4 = (char *)malloc(20);
	strncpy(dest1, source1, 5);
	strncpy(dest2, source1, 20);
	strncpy(dest3, source2, 5);
	strncpy(dest4, source2, 20);
	
	printf("source1 address is %p, source1 is %s.\n", source1, source1);
	printf("source2 address is %p, source2 is %s.\n", source2, source2);
	printf("dest1 address is %p, dest1 is %s.\n", dest1, dest1);
	printf("dest2 address is %p, dest2 is %s.\n", dest2, dest2);
	printf("dest3 address is %p, dest3 is %s.\n", dest3, dest3);
	printf("dest4 address is %p, dest4 is %s.\n", dest4, dest4);

	free(dest1);
	free(dest2);
	free(dest3);
	free(dest4);

	return 0;
}

运行环境:ubuntu14.04,平台自带gcc编译器

运行结果:

root@ubuntu:/home/jack# make
gcc -o test test.c -lpthread
root@ubuntu:/home/jack# ./test
source1 address is 0x8048780, source1 is hello-boy!.
source2 address is 0xbfc3d9e2, source2 is ABCDEFG.
dest1 address is 0x9e53008, dest1 is hello.
dest2 address is 0x9e53018, dest2 is hello-boy!.
dest3 address is 0x9e53030, dest3 is ABCDE.
dest4 address is 0x9e53040, dest4 is ABCDEFG.

果然发生了截断,也就是说:对于字节数组(可以看成一种特殊字符串)的复制,当其中包含0x00时,不可使用strcpy()或者strncpy()函数,比较好的解决办法就是利用for循环来确认复制的位数。在工程实践中,外围部件按协议发来的数据往往就是一串16进程数,对其进行解析时尤其要注意这点!

测试程序下载地址:https://download.csdn.net/download/sleeping_sunshine/11600176

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值