字符串是以'\0'作為結(jié)束符,所以字符串函數(shù)夜市根據(jù)這一特性編寫的,這就造成字符串函數(shù)無法處理字符串內(nèi)部有'\0'的數(shù)據(jù),這時可以使用內(nèi)存操作函數(shù),頭文件依舊是<string.h>
memcpy
void *memcpy( void *to, const void *from, size_t count );
功能:函數(shù)從from中復(fù)制count 個字符到to中,并返回to指針。 如果to 和 from 重疊,則函數(shù)行為不確定。
{
char str[] = "welcom\0 to Beijing";
char ch[] = "aaaaaaaaaaaaaaaaaa";
char c[] = "aaaaaaaaaaaaaaaaaa";
memcpy(ch,str,sizeof(str));
strcpy(c,str);
//打印數(shù)據(jù)查看有無復(fù)制成功
printf("ch[8] = %c\n",ch[8]);
printf("c[8] = %c\n",c[8]);
return 0;
}
結(jié)果
ch[8] = t
c[8] = a
可以看到strcpy函數(shù)并沒有復(fù)制成功,只是復(fù)制到'\0'.就結(jié)束,看到memcpy函數(shù)已經(jīng)復(fù)制成功了后面的字符數(shù)據(jù)內(nèi)容發(fā)生改變。所以,如果字符串內(nèi)部有'\0',要使用內(nèi)存操作函數(shù),不然會操作字符串失敗,導(dǎo)致運行出錯。這個內(nèi)存操作函數(shù)也可以用于數(shù)組復(fù)制。
const int count = 10;
char ch[] = "0123456789";
char c[] = "0123456789";
char str[8];
//memcpy不檢查源能存放的數(shù)據(jù)大小
memcpy(str,ch,count*sizeof(char));
printf("%s\n",str);
printf("%s\n",c);
結(jié)果
012345678923456789
8923456789
可以看到,memcpy函數(shù)不檢查源的數(shù)據(jù)大小和目標(biāo)的數(shù)據(jù)大小,造成其他變量的值發(fā)生變化,運行時發(fā)生不可預(yù)測的結(jié)果。建議檢查復(fù)制的數(shù)據(jù)大小,避免造成不必要的錯誤。
//舊版本的memecpy源碼
void *my_memcpy(void *dest, const void *src, size_t count)
{
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++;
return dest;
}
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9};
//數(shù)組a的a+1數(shù)據(jù)后2位復(fù)制到a
my_memcpy(a+1,a,sizeof(int)*2);
printf("%s\n",ch);
int i;
for(i = 0; i < 10; i++)
{
printf("%d ",a[i]);
}
printf("\n");
return 0;
}
結(jié)果
1 1 1 4 5 6 7 8 9 0
這里看到數(shù)據(jù)不對,不應(yīng)該是1 1 2 4 5 6 7 8 9 0才對嗎,這是因為源與目標(biāo)地址重疊,造成重疊的數(shù)據(jù)可能數(shù)據(jù)會被修改,沒有被封保存,所以結(jié)果不可預(yù)測。
如果源與目標(biāo)地址有重疊,就有兩種情況:
1.恰巧重疊部分的數(shù)據(jù)與前面數(shù)據(jù)重復(fù),修改后數(shù)據(jù)仍然一樣
2.兩者數(shù)據(jù)沒有關(guān)系,造成重疊部分?jǐn)?shù)據(jù)被修改后原值丟失,memcpy后數(shù)據(jù)有變。
所以 如果to 和 from 重疊,則函數(shù)行為不確定。
但是現(xiàn)在的源碼已經(jīng)改進(jìn),源與目標(biāo)地址可以重疊,但是為了穩(wěn)定性,建議在此種情況使用mommove函數(shù)。
memmove
void *memmove( void *to, const void *from, size_t count );
功能: 與mencpy相同,不同的是當(dāng)to 和 from 重疊,函數(shù)正常仍能工作。
int a[10] = {1,2,3,4,5,6,7,8,9};
memmove(b+1,b,sizeof(int)*2);
int i;
for(i = 0; i < 10; i++)
{
printf("%d ",b[i]);
}
printf("\n");
結(jié)果
1 1 2 4 5 6 7 8 9 0
源與目標(biāo)重疊時,建議使用這個函數(shù)。memmove函數(shù)會比memcpy效率低點,因為memmove是先復(fù)制一份數(shù)據(jù),然后再復(fù)制到目標(biāo)。
memchr
void *memchr( const void *buffer, int ch, size_t count );
功能:函數(shù)在buffer指向的數(shù)組的count個字符的字符串里查找ch 首次出現(xiàn)的位置。返回一個指針,指向ch 在字符串中首次出現(xiàn)的位置, 如果ch 沒有在字符串中找到,返回NULL。
char str[] = "welcom to Beijing";
char *ptr = memchr(str,'l',sizeof(char)*10);
printf("%c\n",*ptr);
結(jié)果
l
這個函數(shù)使用與strchr差不多,只是memchr不會因'\0',停止。
memset
void *memset( void *buffer, int ch, size_t count );
功能: 函數(shù)拷貝ch 到buffer 從頭開始的count 個字符里, 并返回buffer指針。 memset() 可以應(yīng)用在將一段內(nèi)存初始化為某個值。
int array[10];
memset(array,0,sizeof(array));
int i;
for(i = 0; i < 10; i++)
{
printf("%d ",array[i]);
}
printf("\n");
結(jié)果
0 0 0 0 0 0 0 0 0 0
這是一個將數(shù)組置為某個值的快捷方法。
memcmp
int memcmp( const void *buffer1, const void *buffer2, size_t count );
功能:函數(shù)比較buffer1 和 buffer2的前count 個字符。
char str[] = "Hello";
char ch[] = "welcom";
int cmp = memcmp(str,ch,sizeof(char)*4);
用法簡單,和strcmp效果差不多,但是memcmp支持處理的數(shù)據(jù)類型更多。
總結(jié)
內(nèi)存操作函數(shù)不會檢查源的數(shù)據(jù)大小,只會根據(jù)使用者傳入的count來操作,所以要避免傳入的count大于源地址的數(shù)據(jù)大小,也不要大于目標(biāo)地址的數(shù)據(jù)大小。如果源與目標(biāo)存在重疊,建議使用mommove,雖然memcpy已經(jīng)改進(jìn),但為了穩(wěn)定性,還是使用mommove。