Record13—字符串copy的技术推演

目录

字符串做函数参数

字符串拷贝的技术推演

总体代码


字符串做函数参数

我们来做一个简单的字符串拷贝的操作,下面就是这个逐个元素复制的过程,只需要注意最后的\0需要专门的赋值才和原来的一样。如果把这一步给漏了,就只能说b是一个数组,不是一个C风格的字符串。

void main71()
{
	char a[] = "i am a student";
	char b[64];
	int  i = 0;

	for (i=0; *(a+i) != '\0'; i++)
	{
		*(b+i) = *(a+i);
	}

	//0没有copy到b的buf中.
	b[i] = '\0'; //重要
	printf("a:%s \n", a);
	printf("b:%s \n", b);

	system("pause");
	return ;
}

运行如下,

如果b后面不加入"\0",运行如下:

 就出现了这样的乱码。

以上的完成拷贝功能的实现,只是C语言初学者的水平,如果就业的各位还是以上的写法,就应该自我反思了!

字符串拷贝的技术推演

 通过这个拷贝过程,将字符串与指针的常用操作给推演一遍。我准备完成一个字符串拷贝工作,在这个工作中,打算让业务模型和测试案例进行分开。所以,首先要定义号接口,下面的被调函数的接口是把字符串从from内存空间拷贝到to内存空间,

职业当记:看到函数能够立马调用起来,才是程序员能力的体现!

void copy_str21(char *from, char *to)

 定义好接口后,应该考虑分配内存:在主调函数中,

int main111()
{
	char *from = "abcd";
	char buf2[100]; 
	copy_str21(from, buf2);
	printf("buf2:%s \n", buf2);

	system("pause");
	return;
}

 这样就相当于把函数给调用起来了。要想把函数给调用起来,最主要的是,主调函数要分配好内存空间。

下面要考虑这个被调函数的内部实现了:

{
	for (; *from!='\0'; from++, to++)
	{
		 *to = *from;
	}
	*to = '\0';

	return ;
}

注意,形参from,to在不断的变化。

思路:(被调)定义接口—》(主调)分配内存—》(被调)填写函数功能。 

内存调用图:

在这个过程中,在主函数中的from,与在被调函数中的from是两码事,但是,他们直接互相传的值是相同的。同理主调和被调中的buf2也是如此,互相传值。只不过是from是主调给被调传值,buf2是被调给主调传值。(出现在被调函数定义中的和被调函数体中的是形参,出现在函数主调函数里的是实参

这个地方,有个小迷糊,为啥实参传的是:from和buf2,但在被调函数中的声明处却是*from 和 *buf2呢?咋不是一个类型的变量呢?这是因为我傻了,人家被调函数中的"char *"是数据类型,数据变量还是"from"和"to"而已。

在以上基础上,继续做优化,把“++”部分给移到for语句当中去,做一下改进:

这种写法,和之前的效果是一样的,"++"的优先级高于"*"的优先级。但是,由于是后缀的"++",所以,是先执行的"*from",再执行"++"即,先执行*to = *from;  再执行from++, to++ 操作。

//*操作 和++的操作
//++ 优先级高 
void copy_str22(char *from, char *to)
{
	for (; *from!='\0';)
	{
		*to++ = *from++;  //  先 *to = *from;  再from++, to++ 
	}
	*to = '\0'; //

	return ;
}

 继续做优化,以上写法,还需要手工的去补"\0",很麻烦,所以把原来的for循环,换成while循环:

void copy_str23(char *from, char *to)
{
	while( (*to = *from) != '\0' )
	{
		from ++; 
		to ++;
	}
}

while语句执行顺序:先将*from赋值给*to,再判定*to的值是否和'\0'相等,如果相等就跳出循环结束操作,如果不相等就进入循环体。

当执行到最后,总会有将"\0"赋值给*to,那么肯定是不满足!='\0'这个条件的,就跳出了while循环,那么自然的就完成了复制操作。于是就不需要手工加"\0"了。

还可以再接着优化,

void copy_str24(char *from , char *to)
{
	while ( (*to++ = *from++) != '\0')
	{
		;
	}
}

 直接就把循环体的工作在循环判定中全给干了!

是的,没错,当然还可以再继续优化,直接把判断"\0"给去掉,如下面的,利用当表达式为真的时候,和当表达式为假的时候怎么怎么样进行判断:

void copy_str25(char *from , char *to)
{
	//*(0) = 'a';
	while ( (*to++ = *from++) )
	{
		;
	}
}

 职业当记:在C/C++领域里,就那么40几套经典程序,只要把这几十套程序掌握了,就很快的能达到一个很高的层次,而这个就是一个经典的案例之一。

一看到这个被调函数的函数名称,就应该明白,这个是指针的输入,主调函数中分配内存,供被调用函数中使用,怎么看出来的?因为*to是一个指向复制完毕的地址,如果不是在主调函数中分配的内存,那被调函数运行完一释放,还有怎么保存复制完毕的内容呢?所以,只能是主调函数中分配的内存,同理,传入的内存空间也肯定是主调提供的。我感觉,要同时知晓被调函数的功能何被调函数的接口,才能判断出是指针的输入还是输出。

 如下 是一个错误案例,错在NULL空间不能被修改:

	{
		//错误案例
		char *myto = NULL;  //要分配内存
		//copy_str25(from,myto );
	}

 那要是真遇到主调函数这种情况,被调函数如何避免呢?被调函数如下写法,可以应对上面的情况,兼容这种错误:

int copy_str26_good(char *from , char *to)
{
	//*(0) = 'a';
	char *tmpfrom = from;
	char *tmpto = to;
	if ( from == NULL || to == NULL)
	{
		return -1;
	}


	while ( *tmpto++ = *tmpfrom++ ) ;  //空语句

	printf("from:%s \n", from);
		
}

int main111()
{
	int ret = 0;
	{
		char *myto = NULL;  //要分配内存
		
		ret = copy_str26_good(from, myto);
		if (ret != 0)
		{
			printf("func copy_str26_good() err:%d  ", ret);
			return ret;
		}
	}
	system("pause");
	return ret;
}

一般情况下,不要改变形参,如果需要改变的话,做一个辅助指针变量,给接过来。 上面代码中:

	char *tmpfrom = from;
	char *tmpto = to;

就是要引入个辅助指针变量。 如果不这样,就会造成from的值打印不出来,比如下面这个函数:

void copy_str25_err(char *from , char *to)
{
	//*(0) = 'a';
	while ( (*to++ = *from++) )
	{
		;
	}

	printf("from:%s \n", from);
	
}

运行出来就是: 

这是因为from的值一直在改变,直到改变成"\0",打印的时候,什么都没有。克服这种状况的方法就是像copy_str26_good中,设置两个辅助指针变量来进行解决。正确运行出来:

总体代码

dm07_字符串做函数参数.c

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

void main71()
{
	char a[] = "i am a student";
	char b[64];
	int  i = 0;

	for (i=0; *(a+i) != '\0'; i++)
	{
		*(b+i) = *(a+i);
	}

	//0没有copy到b的buf中.

	b[i] = '\0'; //重要
	printf("a:%s \n", a);
	printf("b:%s \n", b);

	system("pause");
	return ;
}

//字符串copy函数技术推演

//字符串copy函数

//form形参 形参to 的值 不停的在变化....
//不断的修改了from和to的指向
void copy_str21(char *from, char *to)
{
	for (; *from!='\0'; from++, to++)
	{
		 *to = *from;
	}
	*to = '\0';

	return ;
}

//*操作 和++的操作
//++ 优先级高 
void copy_str22(char *from, char *to)
{
	for (; *from!='\0';)
	{
		*to++ = *from++;  //  先 *to = *from;  再from++, to++ 
	}
	*to = '\0'; //

	return ;
}

void copy_str23(char *from, char *to)
{
	while( (*to = *from) != '\0' )
	{
		from ++; 
		to ++;
	}
}

void copy_str24(char *from , char *to)
{
	while ( (*to++ = *from++) != '\0')
	{
		;
	}
}


void copy_str25(char *from , char *to)
{
	//*(0) = 'a';
	while ( (*to++ = *from++) )
	{
		;
	}
}


void copy_str25_err(char *from , char *to)
{
	//*(0) = 'a';
	while ( (*to++ = *from++) )
	{
		;
	}

	printf("from:%s \n", from);
	
}


//不要轻易改变形参的值, 要引入一个辅助的指针变量. 把形参给接过来.....
int copy_str26_good(char *from , char *to)
{
	//*(0) = 'a';
	char *tmpfrom = from;
	char *tmpto = to;
	if ( from == NULL || to == NULL)
	{
		return -1;
	}


	while ( *tmpto++ = *tmpfrom++ ) ;  //空语句

	printf("from:%s \n", from);
		
}

int main111()
{
	int ret = 0;
	char *from = "abcd";
	char buf2[100]; 
	//copy_str21(from, buf2);
	//copy_str22(from,buf2);
	//copy_str23(from, buf2);
	//copy_str24(from, buf2);
	//copy_str25(from ,buf2);
	//printf("buf2:%s \n", buf2);

	{
		//错误案例
		char *myto = NULL;  //要分配内存
		//copy_str25(from,myto );
	}
	{
		char *myto = NULL;  //要分配内存
		
		ret = copy_str26_good(from, myto);
		if (ret != 0)
		{
			printf("func copy_str26_good() err:%d  ", ret);
			return ret;
		}
	}
	system("pause");
	return ret;
}


int main777()
{
	int ret = 0;
	char *from = "abcd";
	char buf2[100]; 

	printf("copy_str25_err begin\n");
	copy_str25_err(from, buf2);
	copy_str26_good(from, buf2);
	printf("copy_str25_err end\n");
	return 0;
}
发布了140 篇原创文章 · 获赞 6 · 访问量 4873

猜你喜欢

转载自blog.csdn.net/qq_17846375/article/details/103682409