C语言使用函数实现字符串部分复制问题
作者:率真的瓜子
使用函数实现字符串部分复制
本题要求编写函数,将输入字符串t中从第m个字符开始的全部字符复制到字符串s中。
函数接口定义
void strmcpy( char *t, int m, char *s );
函数strmcpy将输入字符串char *t中从第m个字符开始的全部字符复制到字符串char *s中。若m超过输入字符串的长度,则结果字符串应为空串。
裁判测试程序样例
#include <stdio.h> #define MAXN 20 void strmcpy( char *t, int m, char *s ); void ReadString( char s[] ); /* 由裁判实现,略去不表 */ int main() { char t[MAXN], s[MAXN]; int m; scanf("%d\n", &m); ReadString(t); strmcpy( t, m, s ); printf("%s\n", s); return 0; } /* 你的代码将被嵌在这里 */
输入样例:
7
happy new year
输出样例:
new year
由于这道题题目中没有把string.h头文件include进来,
所以自己写一个计算长度的函数
int strlen(char *t) { int i=0; while(*t) { t++; i++; } return i; }
首先很直观地就可以把代码写出来:
void strmcpy( char *t, int m, char *s ) { int n=strlen(t); for(int i=0;i<=n-m;i++) { s[i]=t[m+i-1]; printf("s[%d]=%c\n",i,s[i]); } }
但是这种做法导致最后出来会有多余的空格,因为数组s没有添加结束标记。
没有添加结束标记时,一次性输出数组将导致格式错误。因此还需添加结束标记:
void strmcpy( char *t, int m, char *s ) { int n=strlen(t); for(int i=0;i<=n-m+1;i++) /*把n-m改成了n-m+1,为添加标记创造条件*/ { if(t[m+i-1]=='\0') s[i]='\0'; /*当扫描时发现目标数组中出现结束标记时,为本字符串添加结束标记*/ s[i]=t[m+i-1]; /*扫描*/ } }
来一遍完整代码:
#include <stdio.h> #define MAXN 20 void strmcpy( char *t, int m, char *s ); void ReadString( char s[] ); /* ÓɲÃÅÐʵÏÖ£¬ÂÔÈ¥²»±í */ int main() { char t[MAXN], s[MAXN]; int m; scanf("%d\n", &m); ReadString(t); strmcpy( t, m, s ); printf("%s\n", s); return 0; } /* ÄãµÄ´úÂ뽫±»Ç¶ÔÚÕâÀï */ int strlen(char *t) { int i=0; while(*t) { t++; i++; } return i; } void ReadString( char s[] ) { gets(s); } void strmcpy( char *t, int m, char *s ) { int n=strlen(t); for(int i=0;i<=n-m+1;i++) { if(t[m+i-1]=='\0') s[i]='\0'; s[i]=t[m+i-1]; } }
复制字符串及复制函数汇总(strcpy()/memcpy()/strncpy()/memmove())
我们首先来考虑一个简单的问题,我们定义了一个字符串,然后想要复制这个字符串,在C语言中,我们可以用for循环和指针来实现,假如我们用指针来操作
#include <stdio.h> char str1[20]= "Zxiaoxuan"; char str2[20]; char * pts1 = str1 ; char * pts2 = str2; int main () { pts2=pts1; return 0; }
这样pts2只是复制字符串str1的地址,而不是复制整个字符串。
那么如何进行整个字符串的复制呢
1. 我们可以采用数组的方式来进行
#include<stdio.h> void copy_string(char str1[],char str2[]) { int i = 0; while(str2[i] != '\0') { str1[i] = str2[i]; i++; } str1[i] = '\0'; } int main() { char a[100]="zxiaoxuan"; char b[100]=" "; copy_string(b,a); printf("%s\n",b); return 0; }
输出:
2. 可以采用指针的方式来进行
#include<stdio.h> void copy_string(char *p1,char *p2) { while(*p2 != '\0') { *p1 = *p2; *p1++; *p2++; } *p1 = '\0'; } int main() { char a[100]="zxiaoxuan"; char b[100]=" "; copy_string(b,a); printf("%s\n",b); return 0; }
输出:
除了上面两种,C语言有没有内置的函数来进行拷贝复制呢,当然是有的,下面我们来逐一介绍。
strcpy()
使用头文件:#include <string.h>
定义:char *strcpy(char *dest, const char *src);
参数:
destinin
:目标字符数组;source
:源字符数组;
函数说明:strcpy()会将参数src 字符串拷贝至参数dest 所指的地址。 用于对字符串进行复制,识别到字符串的结束符号‘\0’自动停止
返回值:返回参数dest 的字符串起始地址。
注意:
- 参数 dest 的内存空间要足够大,否则拷贝可能会造成缓冲溢出。
- strcpy() 在复制结束后会添加结束符\0,这点和strncpy()不同
strcpy()的参数是两个字符串指针,其中 *src源字符串可以是指针,数组名,或者字符串常量,但是*dest目标字符串必须位一个确定的数据对象(字符数组),而且应该已经开辟好了存储空间(已经做好初始化)
举例:
#include <stdio.h> #include <string.h> int main () { char str1[]= "Zxiaoxuan"; char str2[20]; char str3[20]; strcpy (str2,str1); strcpy (str3, "copy successful"); printf ( "str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3); return 0; }
输出:
memcpy()
使用头文件:C语言:#include <string.h> C++:#include<cstring>
定义:void memcpy(void *dest, const void *src, size_t n);
参数:
destinin
:目标地址;source
:源地址;n
:复制的字节长度。
函数说明:memcpy()复制 src 所指的内存数据的 n 个字节到 dest所指的内存地址上。也就是从源地址复制n 个字节到目标地址
第一个和第二个指针都是void型且第二个指针不能被修改,第三个参数是需要拷贝的内存长度按字节记。
返回值:返回指向 dest 的指针。返回的指针类型是void。
注意:
- memcpy()并不限制被复制的数据类型,只是逐字节地进行复制,任何数据类型都可以进行复制,例如字符数组、整型、结构体、类等
- memcpy() 会完整的复制 num个字节,不会遇到‘\0’而结束,这点与 strcpy() 不同
- dest 和 src所指的内存空间地址不能重叠
- 参数 dest 的内存空间要足够大,起码要大于等于 num个字节
- 通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
举例:
#include <string.h> #include <stdio.h> #include <stdlib.h> #define N (20) int main() { char *p1 = "zxiaoxuan"; char *p2 = (char *)malloc(sizeof(char) * N); memcpy(p2, p1, N); printf("p2 = %s\n", p2); system("pause"); return 0; }
strncpy()
使用头文件:#include <string.h>
定义:char *strncpy(char *dest, const char *src, size_t len);
参数:
destinin
:目标字符数组;source
:源字符数组;len
:复制的字符串长度。
函数说明:strncpy()复制字符串 src 的前 len 个字节到 dest所指的内存地址上。
返回值:返回字符串dest
注意:
- strncpy()在复制结束后不会向dest结尾添加’\0’结束符 这个是很重要的一个点,要记住
- 如果source(源字符数组)的长度>复制的字符串数len,则只复制source(源字符数组)的前len个字符,不会自动添加结束符\0
- 如果source(源字符数组)的长度<复制的字符串数len,则以NULL填充dest(目标字符数组),直到复制完n个字节
- 参数 dest 的内存空间要足够大,起码要大于等于 num个字节
- 在使用strncpy()的时候,拷贝长度最好为strlen(src)+1,以保证最后的结束符\0也能被复制
举例:
#include <stdio.h> #include <string.h> int main () { char str1[]= "Z Xiao Xuan"; char str2[40]; char str3[40]; /* 拷贝到缓冲区: */ strncpy ( str2, str1, sizeof(str1)+1); //拷贝长度为 str1+1,将结束符\0也进行拷贝 /* 拷贝 5 个字符: */ strncpy ( str3, str2, 5 ); str3[5] = '\0'; /* 手动加上终止符 */ puts (str1); puts (str2); puts (str3); system("pause"); return 0; }
memmove()
使用头文件:#include <string.h>
定义:void *memmove( void* dest, const void* src, size_t count );
参数:
destinin
:目标地址;source
:源地址;count
:复制的字节长度。
函数说明:memmove()复制 src 所指的内存数据的 n 个字节到 dest所指的内存地址上。也就是从源地址复制n 个字节到目标地址。
如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
缓冲区重叠这个需要讲解一下:
根据dest(目标字符数组)内存区域和src(源字符数组)内存区域可分为三种情况:
src内存区域和dest内存区域完全不重叠
src(源字符数组)内存区域和dest(目标字符数组)内存区域存在重叠 且dest所在区域在src所在区域前
如上图,dest(目标字符数组)和src(源字符数组)存在三个字节的内存区域重叠
但是在复制的时候,先把src的前三个字节复制到了dest的前三个内存区域内,再继续复制到重叠区域时,就算被覆盖,也不会有数据错误 所以这样可以正常复制
src(源字符数组)内存区域和dest(目标字符数组)内存区域存在重叠,且在dst所在区域在src所在区域后面
这时候如果使用memcpy()进行复制,会把三个重叠内存字节覆盖为src的前三个字节内容,导致复制到重叠部分的时候出现错误
如果使用memmove(),会先将src(源字符数组)中的内容复制到缓冲区,然后再复制到dest(目标字符数组)中去,有效的避免了数据重叠。
举例:
下面举一个例子:
首先定义一个字符串str:memmove can be very useful......
然后把字符串的第15个字节~第25个字节的11个字节数据,复制到第20个字节~第30个字节中去
src
(源字符数组):very usefuldest
(目标字符数组):useful.....
重叠部分: 第20个字节~第25个字节
#include <stdio.h> #include <stdlib.h> #include <string.h> int main () { char str[] = "memmove can be very useful......"; memmove (str+20,str+15,11); puts (str); system("pause"); return 0; }
先将11个字节的src(源字符数组)数据(very useful)内容复制到缓冲区中,再用缓冲区中的内容覆盖dest(目标字符数组)指向的内存(第20个字节~第30个字节),这样就避免了第20个字节~第25个字节的重叠
最后就变成了memmove can be very very useful.
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。