目录
字符串做函数参数
我们来做一个简单的字符串拷贝的操作,下面就是这个逐个元素复制的过程,只需要注意最后的\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;
}