網站首頁 編程語言 正文
使用函數實現字符串部分復制
本題要求編寫函數,將輸入字符串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[] ); /* óé2??Dêμ??£???è¥2?±í */ 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; } /* ??μ?′ú????±????ú?aà? */ 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 useful -
dest
(目標字符數組):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.
原文鏈接:https://blog.csdn.net/tianty1121/article/details/107725636
相關推薦
- 2022-11-05 Flutter實現一個支持漸變背景的Button示例詳解_Android
- 2022-04-24 .NET?CORE?鑒權的實現示例_實用技巧
- 2022-09-02 useEffect中不能使用async原理詳解_React
- 2022-05-23 C#中using語句的用法_C#教程
- 2022-06-29 python人工智能tensorflow優化器Optimizer算法匯總_python
- 2022-09-01 MongoDB實現查詢、分頁和排序操作以及游標的使用_MongoDB
- 2022-09-24 Python?tkinter?多選按鈕控件?Checkbutton方法_python
- 2021-11-08 深入解析golang中的標準庫flag_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支