字符串copy函数推演过程
最次的字符串拷贝代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *src = "abcd";
char buf[64];
int i = 0;
for (i = 0; *(src + i) != '\0'; i++)
{
buf[i] = *(src + i);
}
buf[i] = '\0';
printf("buf:%s\n", buf);
system("PAUSE");
return 1;
}
第一种风格的拷贝函数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_cpy01(char *from,char *to)
{
int i = 0;
for (i = 0; *from != '\0'; from++,to++)
{
*to = *from;
}
*to = '\0';
}
int main(void)
{
char *src = "abcd";
char buf[64];
str_cpy01(src,buf);
printf("buf:%s\n", buf);
system("PAUSE");
return 1;
}
第二种风格的copy函数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//*操作 和++的操作
//++ 优先级高
void str_cpy02(char *from,char *to)
{
int i = 0;
for (i = 0; *from != '\0';)
{
*to++ = *from++;
}
*to = '\0';
}
int main(void)
{
char *src = "abcd";
char buf[64];
str_cpy02(src, buf);
printf("buf:%s\n", buf);
system("PAUSE");
return 1;
}
第三种风格
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_cpy03(char *from,char *to)
{
while ((*to = *from) != '\0'){
from++;
to++;
}
}
int main(void)
{
char *src = "abcd";
char buf[64];
str_cpy03(src, buf);
printf("buf:%s\n", buf);
system("PAUSE");
return 1;
}
第四种风格
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_cpy04(char *from,char *to)
{
while ((*to++ = *from++) != '\0');
}
int main(void)
{
char *src = "abcd";
char buf[64];
str_cpy04(src, buf);
printf("buf:%s\n", buf);
system("PAUSE");
return 1;
}
第五种风格
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_cpy05(char *from,char *to)
{
while ((*to++ = *from++));
}
int main(void)
{
char *src = "abcd";
char buf[64];
str_cpy05(src, buf);
printf("buf:%s\n", buf);
system("PAUSE");
return 1;
}
第六种风格:避免操作空指针以及引入辅助指针变量增强程序的健壮性
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int str_cpy06(char *from, char *to)
{
char * tmpfrom = from;//避免修改形参
char * tmpto = to;
if (from == NULL || to == NULL)//避免操作空指针
return -1;
while (*tmpto++ = *tmpfrom++);
printf("from:%s\n",from);
return 0;
}
int main(void)
{
char *src = "abcd";
char buf[64];
str_cpy06(src, buf);
printf("buf:%s\n", buf);
system("PAUSE");
return 1;
}
strstr模型
strstr–do-while模型
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int getCountDoWhile(char * p_str, char *sub_str, int *ncount)
{
int ret = 0;
char * src = p_str;
int tmp_count = 0;
if (p_str == NULL || sub_str == NULL || ncount == NULL)//合法性检测
{
ret = -1;
printf("%s is error:(p_str == NULL || sub_str == NULL || ncount == NULL)\n", __FUNCTION__);
return ret;
}
do{
src = strstr(src, sub_str);
if (src != NULL)
{
tmp_count++;
src += strlen(sub_str);
}
else{
break;
}
} while (*src != '\0');
*ncount = tmp_count;
return ret;
}
int main(void)
{
//strstr(str, str2)
int ncount = 0;
int ret = 0;
char *p = "11abcd111122abcd3333322abcd3333322qqq";
ret = getCountDoWhile(p,"abcd",&ncount);
if (ret != 0){
printf("func getCountDoWhile() err:%d \n", ret);
return ret;
}
printf("ncount:%d \n", ncount);
system("PAUSE");
return ret;
}
strstr–while模型
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int getCountWhile(char * p_str, char *sub_str, int *ncount)
{
int ret = 0;
char * src = p_str;
int tmp_count = 0;
if (p_str == NULL || sub_str == NULL || ncount == NULL)//合法性检测
{
ret = -1;
printf("%s is error:(p_str == NULL || sub_str == NULL || ncount == NULL)\n", __FUNCTION__);
return ret;
}
while (src = strstr(src,sub_str)){
tmp_count++;
src += strlen(sub_str);
if (*src == '\0')
break;
}
*ncount = tmp_count;
return ret;
}
int main(void)
{
//strstr(str, str2)
int ncount = 0;
int ret = 0;
char *p = "11abcd111122abcd3333322abcd3333322qqq";
ret = getCountWhile(p, "abcd", &ncount);
if (ret != 0){
printf("func getCountWhile() err:%d \n", ret);
return ret;
}
printf("ncount:%d \n", ncount);
system("PAUSE");
return ret;
}
两头堵模型
求出字符串(首尾有空格)有效字符的长度
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<ctype.h>
int getCount(char * src,int * count)
{
int ret = 0;
char *tmp_p = src;
int tmp_count = 0;
int head = 0,tail = 0;
if(src == NULL || count == NULL)
{
ret = -1;
printf("%s is error!(src == NULL || count == NULL)\n",__FUNCTION__);
return ret;
}
while(*(tmp_p+head) != '\0' && isspace(tmp_p[head])){
head++;
}
tail = strlen(src)-1;
while(*(tmp_p+tail) != '\0' && isspace(tmp_p[tail])){
tail--;
}
tmp_count = tail - head + 1;
*count = tmp_count;
return ret;
}
int main(void)
{
char *p = " abcdefg ";
int ret = 0;
int ncount = 0;
ret = getCount(p,&ncount);
if(ret != 0){
printf("getCount failed!ret : %d\n",ret);
return ret;
}
printf("ncount:%d\n",ncount);
printf("Hello World!\n");
return 0;
}
去除字符串中的空格,输出新字符串
注意:向一个指针指向的内存空间拷贝数据的时候要留心是否可以向该片内存空间写入数据,常量数据区是不可以写入数据的。
int trimeSpace(char *src,char *new_str)
{
int ret = 0;
char *tmp_p = src;
int head = 0,tail=0;
if(src == NULL || new_str == NULL)
{
ret = -1;
printf("%s is error!(src == NULL || count == NULL)\n",__FUNCTION__);
return ret;
}
tail = strlen(src)-1;
while(tmp_p[head] != '\0' && isspace(tmp_p[head])){
head++;
}
while(tmp_p[tail] != '\0' && isspace(tmp_p[tail])){
tail--;
}
strncpy(new_str,tmp_p+head,tail-head+1);
new_str[tail-head+1] = '\0';
return ret;
}
int main(void)
{
char *p = " abcdefg ";
char buf[1024];
int ret = 0;
ret = trimeSpace(p,buf);
if(ret != 0){
printf("getCount failed!ret : %d\n",ret);
return ret;
}
printf("buf:%s\n",buf);
printf("Hello World!\n");
return 0;
}
注意:strncpy函数并不会在目标字符串后面自动添加’\0’
字符串反转模型
类似两头堵模型的字符串反转
int inverse(char *src_str)
{
int ret = 0;
int len = strlen(src_str);
char * head = src_str;
char * tail = src_str + (len-1);
char tmp_c = 0;
if(src_str == NULL){
ret = -1;
printf("%s is error!src_str == NULL\n",__FUNCTION__);
return ret;
}
while(head < tail){
tmp_c = *head;
*head++ = *tail;
*tail-- = tmp_c;
}
return ret;
}
int main(void)
{
//char *p = " abcdefg ";//不能用字符串常量赋值给一个指针进行该实验,因为常量区不可修改数据
char buf[1024]="abcdefg";
int ret = 0;
ret = inverse(buf);
if(ret != 0){
printf("inverse failed!ret : %d\n",ret);
return ret;
}
printf("buf:%s\n",buf);
printf("Hello World!\n");
return 0;
}
利用堆栈的字符串反转(递归)
递归简单的逆序打印不存储结果
int inverse_02(char *src_str)
{
int ret = 0;
char *tmp_p = src_str;
if(src_str == NULL){//合法性检测
ret = -1;
printf("%s is error!src_str == NULL\n",__FUNCTION__);
return ret;
}
if(*tmp_p == '\0')//递归停止的正常条件
{
return 0;
}
inverse_02(tmp_p+1);//将tmp_p指向的内存空间的数据的地址逐个压栈
printf("%c",*tmp_p);
return ret;
}
int main(void)
{
char buf[1024]="abcdefg";
int ret = 0;
printf("buf:");
ret = inverse_02(buf);
if(ret != 0){
printf("inverse_02 failed!ret : %d\n",ret);
return ret;
}
printf("\nHello World!\n");
return 0;
}
递归结合全局变量
char g_buf[1024]={0};
int inverse_03(char *src_str)
{
int ret = 0;
char *tmp_p = src_str;
if(src_str == NULL){//合法性检测
ret = -1;
printf("%s is error!src_str == NULL\n",__FUNCTION__);
return ret;
}
if(*tmp_p == '\0')//递归停止的正常条件
{
return 0;
}
inverse_03(tmp_p+1);//将tmp_p指向的内存空间的数据的地址逐个压栈
strncat(g_buf,tmp_p,1);
return ret;
}
int main(void)
{
char buf[1024]="abcdefg";
int ret = 0;
ret = inverse_03(buf);
if(ret != 0){
printf("inverse_03 failed!ret : %d\n",ret);
return ret;
}
printf("g_buf:%s\n",g_buf);
printf("\nHello World!\n");
return 0;
}
递归结合局部变量(指针做函数参数)
int inverse_04(char *src_str,char *str_out)
{
int ret = 0;
char *tmp_p = src_str;
if(src_str == NULL || str_out == NULL){//合法性检测
ret = -1;
printf("%s is error!(src_str == NULL || str_out == NULL)\n",__FUNCTION__);
return ret;
}
if(*tmp_p == '\0')//递归停止的正常条件
{
return 0;
}
inverse_04(tmp_p+1,str_out);//将tmp_p指向的内存空间的数据的地址逐个压栈
strncat(str_out,tmp_p,1);
return ret;
}
int main(void)
{
char buf[1024]="abcdefg";
char res[1024];
int ret = 0;
memset(res,0,sizeof(res));
ret = inverse_04(buf,res);
if(ret != 0){
printf("inverse_04 failed!ret : %d\n",ret);
return ret;
}
printf("res:%s\n",res);
printf("\nHello World!\n");
return 0;
}
两个小练习
有一个字符串”1a2b3d4z”,;
要求写一个函数实现如下功能,
功能1:把偶数位字符挑选出来,组成一个字符串1。valude;20分
功能2:把奇数位字符挑选出来,组成一个字符串2,valude 20
功能3:把字符串1和字符串2,通过函数参数,传送给main,并打印。
功能4:主函数能测试通过。
int getStr1Str2(char *souce, char *buf1, char *buf2);
int getStr1Str2(char *souce, char *buf1, char *buf2)
{
int ret = 0;
char *src = souce;
if(souce == NULL || buf1 == NULL || buf2 ==NULL){
ret = -1;
printf("%s is error!(source == NULL || buf1 == NULL || buf2 ==NULL)\n",__FUNCTION__);
return ret;
}
while(*src != '\0'){
*buf1 = *src;
src++;
buf1++;
*buf2 = *src;
src++;
buf2++;
}
return ret;
}
int main(void)
{
char src[1024]="1a2b3c4d";
char buf1[512]={0};
char buf2[512]={0};
int ret = 0;
ret = getStr1Str2(src,buf1,buf2);
if(ret != 0){
printf("getStr1Str2 failed!ret : %d\n",ret);
return ret;
}
printf("src:%s\n",src);
printf("偶数位的数据buf2:%s\n",buf2);
printf("奇数位的数据buf1:%s\n",buf1);
printf("\nHello World!\n");
return 0;
}
键值对(”key = valude”)字符串,在开发中经常使用;
要求1:请自己定义一个接口,实现根据key获取valude;40分
要求2:编写测试用例。30分
要求3:键值对中间可能有n多空格,请去除空格30分
注意:键值对字符串格式可能如下:
“key1 = valude1”
“key2 = valude2”
“key3 = valude3”
“key4 = valude4”
“key5 = “
“key6 =“
“key7 = “
int getKeyByValude(char *keyvaluebuf, char *keybuf, char *valuebuf);
int trimeSpace(char *src,char *new_str)
{
int ret = 0;
char *tmp_p = src;
int head = 0,tail=0;
if(src == NULL || new_str == NULL)
{
ret = -1;
printf("%s is error!(src == NULL || count == NULL)\n",__FUNCTION__);
return ret;
}
tail = strlen(src)-1;
while(tmp_p[head] != '\0' && isspace(tmp_p[head])){
head++;
}
while(tmp_p[tail] != '\0' && isspace(tmp_p[tail])){
tail--;
}
strncpy(new_str,tmp_p+head,tail-head+1);
new_str[tail-head+1] = '\0';
return ret;
}
int getKeyByValude(char *keyvaluebuf, char *keybuf, char *valuebuf)
{
int ret = 0;
char * p = keyvaluebuf;//辅助指针变量初始化
if(keyvaluebuf == NULL || keybuf == NULL || valuebuf ==NULL){
ret = -1;
printf("%s is error!(keyvaluebuf == NULL || keybuf == NULL || valuebuf ==NULL)\n",__FUNCTION__);
return ret;
}
//1. 从母串中查找子串
p = strstr(p,keybuf);
if(p == NULL){
ret = -1;
return ret;
}
p += strlen(keybuf);//辅助指针变量再次初始化使得下一次可用
//2. 判断有没有等号
p = strstr(p,"=");
if(p == NULL){
ret = -1;
return ret;
}
p += strlen("=");//辅助指针变量再次初始化使得下一次可用,指向等号后面开始的地方
//3. 去除等号后面的空格
ret = trimeSpace(p,valuebuf);//调用自己封装的函数
if(ret != 0){
printf("getKeyByValude failed \n");
return ret;
}
return ret;
}
int main(void)
{
char *keyandvalue = "key2 = value2 ";
char *key = "key2";
char res_str[1024]={0};
int ret = 0;
ret = getKeyByValude(keyandvalue,key,res_str);
if(ret != 0){
printf("getKeyByValude failed!ret : %d\n",ret);
return ret;
}
printf("res_str:%s\n",res_str);
printf("\nHello World!\n");
return 0;
}
一级指针易错模型分析
字符串作函数参数的时候,记得在被调函数进行合法性检测
对指针变量进行合法性检测主要是检测指针本身的值而不是检测指针变量指向的内存空间的值
比如下面代码就有错误:
void copy_str21(char *from, char *to)
{
if (*NULL = '\0' || *to!=’\0’) //错误判断
{
Printf(“func copy_str21() err\n”);
return;
}
for (; *from!='\0'; from++, to++)
{
*to = *from;
}
*to = '\0';
}
字符串初始化字符数组因为忽略C风格的字符串默认结尾有个’\0’作为结尾符号而发生越界,如:
char buf[3] = "abc";
不断修改指针变量的值会导致指针指向的内存区域发生变化,从而在涉及到free的时候,释放错误的内存块导致程序崩溃,如:
试图将被调函数里分配的内存空间向外传递,但如果被调函数实在栈空间分配的内存则传递是不可能成功的,这点需要格外注意
从主调函数传递一个实参的地址给被调函数,被调函数制图修改该实参的值,需要注意先使用间接访问操作符取出相应地址处的内存数据,再进行修改操作,这里主要是由于*和++操作符的优先级不一样导致的。
mycount++;和(mycount)++;
关于const
int main()
{
const int a;
int const b;
const char *c;
char * const d; char buf[100]
const char * const e ;
return 0;
}
第一个第二个意思一样 代表一个常整形数
第三个 c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
第五个 e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)
合理的利用const可以带来的好处:
指针做函数参数,可以有效的提高代码可读性,减少bug;
清楚的分清参数的输入和输出特性;
结论:
指针变量和它所指向的内存空间变量,是两个不同的概念。。。。。。
看const 是放在*的左边还是右边 放在右边修饰指针变量,放在左边修饰所指向的内存变量;
C语言中的const是个冒牌货,通过指针可以绕过他的只读属性;