
本篇文章及后面的幾篇文章將會詳細介紹和學習字串操作函式及記憶體操作函式,
文章目錄
- 一、strlen
- 二、strcpy
- 三、strcat
- 四、strcmp
- 五、strncpy
- 六、strncat
- 七、strncmp
- 八、strstr
介紹:C語言中對字符和字串的處理很是頻繁,但是C語言本身是沒有字串型別的,字串通常放在常量字串中或者字符陣列中,(字串常量適用于那些對它不做修改的字串函式.
一、strlen
在之前的學習中,我們會經常使用一個字串函式求字串的長度-- strlen,
strlen求字串長度的演算法分析:strlen接收到字串起始位置的地址時,比較該地址處的內容是否為’\0’,若不為’\0’, 字串的長度 + 1,
圖解如下:

函式介紹:strlen
size_t strlen(const char* str);
頭檔案:string.h
函式名:strlen
函式引數:str,引數型別是const char* ,即需要進行求字串長度的起始地址
函式回傳型別: size_t,size_t是unsigned int的型別重定義,是無符號整型,庫函式使用size_t型別可能考慮的是字串的長度不可能是負數,所以用了無符號型別size_t,
函式功能:計算字串的長度
重點內容:
(1)字串以’\0’作為結束標志,strlen函式回傳的是在字串中’\0’前面出現的字符個數(不包含’\0’),
(2)引數指向的字串必須要以’\0’結束,(如果沒有’\0’,可能會出現隨機值)
(3)注意函式的回傳值為size_t,是無符號的(易錯)(strlen(“abc”) - strlen("abcdef) < 0為假,注意strlen回傳型別)
(4)學會strlen函式的模擬實作
strlen函式的模擬實作三種方法:
①計數器方法
②遞回方法(不創建臨時變數求字串長度)
③指標 - 指標方法
#include<stdio.h>
#include<assert.h>
//1.計數器實作求字串長度函式
int my_strlen1(const char* str)//整個程序不改變指標指向內容,加上const
{
assert(str != NULL);//加上斷言,防止接收空指標
int count = 0;
while (*str != '\0')//也可以直接用while(*str)
{
count++;
str++;
}
return count;
}
//2.遞回實作求字串長度,不用創建臨時變數
int my_strlen2(const char* str)
{
assert(str != NULL);
if (*str != '\0')//也可以直接用if(*str)
{
return 1 + strlen(str + 1);//不能直接使用str++,可以使用++str,建議直接用str+1
}
else
return 0;
}
//3.指標-指標得到中間元素的個數,實作求字串長度
int my_strlen3(const char* str)
{
assert(str != NULL);
const char* tmp = str;//創建臨時指標變數保存str起始值
while (*str != '\0')
{
str++;
}
return str - tmp;
}
int main()
{
char arr[] = "abcdefgh";
int ret1 = my_strlen1(arr);//1.計數器方法
int ret2 = my_strlen2(arr);//2.遞回方法
int ret3 = my_strlen3(arr);//3.指標 - 指標方法
printf("%d\n", ret1);
printf("%d\n", ret2);
printf("%d\n", ret3);
return 0;
}

二、strcpy
如果我們要將一個字串的內容拷貝到另外一個字串空間中時,需要使用字串拷貝- -strcpy函式,在正式介紹strcpy函式之前,我們先看一段代碼:
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdefghik";
char arr2[] = "hello";
//將字串arr2拷貝給arr1
strcpy(arr1, arr2);
//用除錯視窗觀察arr1變化
return 0;
}


strcpy函式進行字串拷貝的演算法分析:

函式介紹:strcpy
char* strcpy(char* destination,const char* source);
頭檔案:string.h
函式名:strcpy
函式引數:
引數1:destination, 型別:char* ,表示將字串拷貝的目的地位置
引數2:source,型別:char* ,表示被拷貝字串拷貝的源地址起始位置,
函式回傳型別: char*, 實際上就是回傳destination(目的地)的起始位置
函式功能:字串拷貝
重點內容:
(1) Copies the C string pointed by source into the array pointed by
destination, including the terminating nulcharacter(and stopping at that point).
(2)源字串必須以‘\0’結束, (源字串如果沒有’\0’, 那么strcpy在拷貝的時候,不知道什么時候停止,可能會造成越界訪問)
(3)會將源字串中的’\0’拷貝到目標空間,
(4)目標空間必須足夠大,以確保能存放源字串, (目標空間不夠大,也會導致越界訪問)
(5)目標空間必須可變,
(要將源字串的內容拷貝到目標空間,目標空間當然要可以變化,才能接收拷貝過來的字符)
(6)學會模擬實作,
模擬實作strcpy
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* dest_start = dest;
//拷貝src指向字串的內容到dest指向的空間,包括'\0'
//方法一:常規思路版
//while (*src != '\0')
//{
// *dest = *src;
// dest++;
// src++;
//}
//*dest = *src;
//方法二:代碼精簡版
while (*dest++ = *src++)
{
;
}
return dest_start;
}
int main()
{
char arr1[] = "abcdefghik";
char arr2[] = "hello";
my_strcpy(arr1, arr2);//模擬實作strcpy函式
printf("%s", arr1);
return 0;
}

三、strcat
如果我們要將一個字串的內容追加到另外一個字串的末尾空間中時,需要使用字串拷貝-- - strcat函式,在正式介紹strcat函式之前,我們先看一段代碼:
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[30] = "hello";
//arr1空間需要足夠大來接收追加過來的內容
//否則會造成越界訪問
char arr2[] = "world";
//將字串arr2內容追加給arr1
strcat(arr1, arr2);
printf("%s", arr1);
return 0;
}

strcat在進行追加的時候是否會將’\0’追加過去?

strcat函式進行字串追加的演算法分析:

函式介紹:strcat
char* strcat(char* destination,const char* source);
頭檔案:string.h
函式名:strcat
函式引數:
引數1:destination, 型別:char* ,表示將字串追加的目的地位置
引數2:source,型別:char* ,表示被追加字串的源地址起始位置,
函式回傳型別: char*,實際上就是回傳destination(目的地)的起始位置
函式功能:字串追加
重點內容:
(1)Appends a copy of the source string to the destination string.The
terminating null character in destination is overwritten by the first character of source, and a null - character is included at the end ofthe new string formed by the concatenation of both in destination.
(2)源字串必須以‘\0’結束,目標空間也必須包含’\0’,以確定追加的起始位置,
(3)目標空間必須有足夠的大,能容納下源字串的內容,
(4)目標空間必須可修改,
(5)字串自己給自己追加,如何 ?
用strcat自己給自己追加會導致程式崩潰,無法實作自己追加自己!(從’\0’開始追加,自己追加自己的時候,’\0‘改成了被追加的首字符,再尋找追加字串結束的’\0’時,找不到’\0’,會導致死回圈)
模擬實作strcat
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* dest_start = dest;
//1.找到目的空間中的'\0'
while (*dest != '\0')//跳過不是'\0'的字符
{
dest++;
}
//2.追加
while (*dest++ = *src++)
{
;
}
return dest_start;
}
int main()
{
char arr1[30] = "hello";
char arr2[] = "world";
my_strcat(arr1, arr2);//模擬實作strcat
printf("%s", arr1);
return 0;
}

思考:strcat為什么不能自己追加自己?
strcat從’\0’開始追加,自己追加自己的時候,’\0‘改成了被追加的首字符,再尋找追加字串結束的’\0’時,找不到’\0’,會導致死回圈
怎么樣才能自己追加自己呢?
使用strncat函式,后面會進行詳細講解
四、strcmp
如果我們要比較兩個字串的是否相等,或者比較字串大小,不能用運算子 == 來直接進行判斷,而是需要用到字串比較函式strcmp
strcmp函式進行字串追加的演算法分析:

函式介紹:strcmp int strcmp(const char* str1,const char* str2);
頭檔案:string.h
函式名:strcmp
函式引數:
引數1:str1, 型別:char* ,表示將進行比較的第一個字串
引數2:引數2:str2, 型別:char* ,表示將進行比較的第二個字串
函式回傳型別: int, 回傳兩個字串比較的結果
函式功能:字串比較
重點內容:
(1)This function starts comparing the first character of each string.lf they are equal to each other, itcontinues with the following pairs until the characters differ or until a terminating null - character isreached.
標準規定︰
(2)第一個字串大于第二個字串,則回傳大于O的數字,
(3)第一個字串等于第二個字串,則回傳0
(4)第一個字串小于第二個字串,則回傳小于0的數字
那么如何判斷兩個字串?
逐個字符進行比較
模擬實作strcmp
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* p1, const char* p2)
{
assert(p1 && p2);
while (*p1 == *p2)
{
if (*p1 == '\0')
{
return 0;
}
p1++;
p2++;
}
//方法一:vs實作的方式
//if (*p1 > *p2)
//{
// return 1;
//}
//else
//{
// return -1;
//}
//方法二:linux下gcc實作方式
return *p1 - *p2;
}
int main()
{
char* p1 = "abcdef";
char* p2 = "abqwt";
if (my_strcmp(p1, p2) > 0)
{
printf("%s > %s\n", p1, p2);
}
else if (my_strcmp(p1, p2) == 0)
{
printf("%s = %s\n", p1, p2);
}
else
{
printf("%s < %s\n", p1, p2);
}
return 0;
}

五、strncpy
strncpy與strcpy相比較多了一個字母n,這個n代表的是需要拷貝字符的個數,也就是說strncpy需要關注拷貝字符的個數,而不是像strcpy那樣關注’\0’,
舉例:
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[10] = "abcdefg";
char arr2[] = "1234";
int len = 0;//len是拷貝位元組的個數
scanf("%d", &len);
strncpy(arr1, arr2, len);
printf("%s", arr1);
return 0;
}
演算法圖解分析:

char* strncpy(char* destination,const char* source,size_t num);
頭檔案:string.h
函式名:strncpy
函式引數:
【引數1】destination,型別:char*,拷貝字符的目的地位置,即接收字符的起始位置
【引數2】source,型別:char* ,拷貝字符的源地址,即拷貝字串的開始位置,
【引數3】num,型別size_t,拷貝字符的個數,用來控制拷貝字符的長度,
函式回傳型別:char* ,回傳接收字符的起始位置,
函式功能:指定個數的字串拷貝
重點內容
(1)Copies the first num characters of source to destination.lf the end of the source C string(which issignaled by a null - character) is found before num characters have been copied, destination is paddedwith zeros until a total of num characters have been written to it.
(2)拷貝num個字符從源字串到目標空間,
(3)如果源字串的長度小于num,則拷貝完源字串之后,在目標的后邊追加0,直到num個,
(4)如果拷貝的字符長度大于目的地空間的容量,則會破壞dest字串后面的\0,要避免這種情況發生
模擬實作strncpy
#include<stdio.h>
#include<assert.h>
//模擬實作strncpy
//方法一
//char* my_strncpy(char* dest, const char* src, size_t n)
//{
// char* dest_start = dest;
// while ((n > 0) && (*src != '\0'))
// {
// *dest = *src;
// dest++;
// src++;
// n--;
// }
// while (n > 0)
// {
// *dest = '\0';
// dest++;
// n--;
// }
// return dest_start;
//}
//方法二
char* my_strncpy(char* dest, const char* src, size_t count)
//count比n更有實際意義
{
assert(dest != NULL);//參考斷言
assert(src != NULL);
char* start = dest;
while (count && (*dest++ = *src++) != '\0')
{
count--;
}
if (count)
{
while (count--)
{
*dest++ = '\0';
}
}
return start;
}
int main()
{
char arr1[10] = "abcdefg";
char arr2[] = "1234";
size_t len = 0;
scanf("%d", &len);
my_strncpy(arr1, arr2, len);
printf("%s", arr1);
return 0;
}

六、strncat
strcat函式是字串追加,在使用的時候以src的’\0’作為追加結束標志,因此在使用strcat來追加一個字串陣列本身的時候,會因\0被提前覆寫而無法追加成功,

strncat與strcat相比多了一個字母n,這個n代表的是需要追加字符的個數,也就是說strncat需要關注追加字符的個數,而不是像strcat那樣關注’\0’,

strncat在追加字串的時候,會自動在末尾處添加字串結束標志’\0’,(這也是我們在追加的時候,不用關注原dest, src中’\0’,僅需關注追加字符的個數的原因)
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[15] = "12345\0xxxxxxx";
char arr2[] = "abcd";
size_t count = 0;
scanf("%d", &count);
strncat(arr1, arr2, count);
printf("%s", arr1);
return 0;
}

演算法圖解分析:

情況二程式演示效果:

char* strncat(char* destination,const char* source,size_t num);
頭檔案:string.h
函式名:strncat
函式引數:
【引數1】destination,型別:char*,被追加字符的目的地位置,即接收追加字符的起始位置
【引數2】source,型別:char* ,追加字符的源地址,即追加字串的開始位置,
【引數3】num,型別size_t,追加字符的個數,用來控制追加字符的長度,
函式回傳型別:char* ,回傳接收字符的起始位置,
函式功能:指定個數的字串追加
重點內容:
(1)Appends the first num characters of source to destination, plus a terminating null - character.
(2)lf the length of the C string in source is less than num, only the content up to the terminating null - character is copied.
(3)如果追加的字符長度大于目的地空間的剩余容量,則出現越界訪問,要避免這種情況發生,
模擬實作strncpy
#include<stdio.h>
#include<assert.h>
//方法一
//char* my_strncat(char* dest, const char* src, size_t count)
//{
// char* start = dest;
// //dest找到\0的位置
// while (*dest!='\0')
// {
// dest++;
// }
// while (count)
// {
// //將src中的字符追加給dest
// *dest = *src;
// dest++;
// src++;
// //如果src以及指向\0的位置,提前結束回圈
// if (*src == '\0')
// {
// break;
// }
// count--;
// }
// *dest = '\0';//字符個數追加完畢后,再單獨追加'\0'
// return start;
//}
//方法二
char* my_strncat(char* dest, const char* src, size_t count)
{
assert(dest != NULL && src != NULL);
char* start = dest;
while (*dest++)
;
dest--;
while (count--)
if ((*dest++ = *src++) == '\0')
return start;
*dest = '\0';
return start;
}
int main()
{
char arr1[15] = "12345\0xxxxxxx";
char arr2[] = "abcd";
size_t count = 0;
scanf("%d", &count);
my_strncat(arr1, arr2, count);
printf("%s", arr1);
return 0;
}

七、strncmp
strcmp用來比較兩個字串的大小,其演算法思想如下:

而strncmp與strcmp相比多了一個字母n,這個n代表的是需要比較字符的個數,也就是說strncmp需要關注比較字符的個數,而不是像strcmy那樣僅關注’\0’,strncmp的回傳結果與strcmp一樣,回傳 > 0, == 0, < 0 的整數,
以下面代碼為例:
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcd12";
int ret = 0;
size_t count = 0;
scanf("%d", &count);
ret = strncmp(arr1, arr2, count);
if (ret > 0)
{
printf("arr1 > arr2\n");
}
else if (ret == 0)
{
printf("arr1 = arr2\n");
}
else
{
printf("arr1 < arr2\n");
}
return 0;
}
演算法圖解分析:

int strncmp(const char* p1,const char* p2,size_t count); 頭檔案:string.h
函式名:strcmp
函式引數:
【引數1】p1,char* 型別,表示用來比較的其中一個字串,
【引數2】p2,char* 型別,表示用來比較的另一個字串,
【引數3】count,size_t型別,表示參與比較的字符個數,
函式回傳型別:int型別,根據比較的具體結果回傳相應的數值
Exit :
returns <0 if str1 < str2
returns 0 if str1 == str2
returns >0 if str1 > str2
函式功能:
Compares two strings for ordinal order.The comparison stops after :
(1) a difference between the strings is found,
(2) the end of the strings is reached, or
(3) count characters have been compared.
重點內容:
(1)比較到出現另個字符不一樣或者一個字串結束或者num個字符全部比較完,

庫函式源代碼參考:
int __cdecl strncmp
(
const char* first,
const char* last,
size_t count
)
{
size_t x = 0;
if (!count)
{
return 0;
}
/*
* This explicit guard needed to deal correctly with boundary
* cases: strings shorter than 4 bytes and strings longer than
* UINT_MAX-4 bytes .
*/
if (count >= 4)
{
/* unroll by four */
for (; x < count - 4; x += 4)
{
first += 4;
last += 4;
if (*(first - 4) == 0 || *(first - 4) != *(last - 4))
{
return(*(unsigned char*)(first - 4) - *(unsigned char*)(last - 4));
}
if (*(first - 3) == 0 || *(first - 3) != *(last - 3))
{
return(*(unsigned char*)(first - 3) - *(unsigned char*)(last - 3));
}
if (*(first - 2) == 0 || *(first - 2) != *(last - 2))
{
return(*(unsigned char*)(first - 2) - *(unsigned char*)(last - 2));
}
if (*(first - 1) == 0 || *(first - 1) != *(last - 1))
{
return(*(unsigned char*)(first - 1) - *(unsigned char*)(last - 1));
}
}
}
/* residual loop */
for (; x < count; x++)
{
if (*first == 0 || *first != *last)
{
return(*(unsigned char*)first - *(unsigned char*)last);
}
first += 1;
last += 1;
}
return 0;
}
八、strstr
查找字串,找子字串,
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abbbcdefbcd";
char arr2[] = "bcd";
char* ret = strstr(arr1, arr2);
if (ret == NULL)
{
printf("Can not find!\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}

演算法圖解分析:

char* strstr(const char* str1, const char* str2);
頭檔案:string.h
函式名:strstr
函式引數:
【引數1】str1,char* ,用于查找字串的母串,
【引數2】str2,char*,待查找字串的子串,
函式回傳型別:char* ,回傳查找到的地址
函式功能:查找字串 / 找子字串
重點內容:
(1)如果在str1中查找了字串str2,則回傳str1中字串str2的起始地址
(2)如果查找不到,回傳空指標NULL
(3)如果str1中含有多個str2字串內容,回傳的是第一個被查找到的str2起始地址
模擬實作strstr:
#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
char* cur = (char*)str1;
char* p1 = NULL;
char* p2 = NULL;
if (*str2 == '\0')
return (char*)str1;
while (*cur != '\0')
{
p1 = cur;
p2 = str2;
while ((*p1 == *p2) && (*p1 != '\0') && (*p2 != '\0'))
{
p1++;
p2++;
}
if (*p2 == '\0')
{
return cur;//找到字串的情況
}
cur++;
}
return NULL;//找不到字符的情況
}
其實庫函式的實作邏輯跟我們實作的方式是一樣的,兩者有所區別的是代碼風格,庫函式的代碼風格更加簡練,需要有一定的編程功底!
庫函式源代碼參考:
char* __cdecl strstr(
const char* str1,
const char* str2
)
{
char* cp = (char*)str1;
char* s1, * s2;
if (!*str2)
return((char*)str1);
while (*cp)
{
s1 = cp;
s2 = (char*)str2;
while (*s1 && *s2 && !(*s1 - *s2))
s1++, s2++;
if (!*s2)
return(cp);
cp++;
}
return(NULL);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/287650.html
標籤:其他
上一篇:C++ C++實戰筆記
下一篇:面試必學!!
