1. 程式人生 > >【C語言】字串函式探幽

【C語言】字串函式探幽

目錄

1、strcpy()

a)如果src長度大於dest會發生什麼?

i.執行到strcpy函式之前,檢視a和b的值和記憶體:

ii、執行strcpy,觀察記憶體

iii、得出結論

b)如果src長度小於dset呢?

c)手寫strcpy

d)總結

2、strlen()

3、strcat()

最近學習C++時遇到了字串的問題,結合之前的面試題深入分析下。

字串庫函式有strcpy、strlen、strstr等,可以參見C語言字串操作總結大全

1、strcpy()

strcpy():char *strcpy(char* dest, const char *src);返回指向dset的指標

strcpy()被認為是不安全的函式,因為它的函式實現非常簡單,沒有考慮異常情況。下面考慮兩種情況:

a)如果src長度大於dest會發生什麼?

linux man page裡面提到了這個BUG:

If the destination string of a strcpy() is not large enough, then anything might happen. Overflowing fixed-length string buffers is a favorite cracker technique for taking complete control of the machine. Any time a program reads or copies data into a buffer, the program first needs to check that there's enough space. This may be unnecessary if you can show that overflow is impossible, but be careful: programs can get changed over time, in ways that may make the impossible possible.

大概意思是這樣做固定長度的字串就會發生溢位。如果程式沒有事先檢查記憶體空間,就會發生難以預料的事情。

寫好程式碼:char a[2] = "a"; char b[10] = "bbbbbbbb";  auto p=strcpy(a, b);  在VS斷點除錯,步驟如下:

i.執行到strcpy函式之前,檢視a和b的值和記憶體:

發現VS編譯器以16進位制數c來作為未被使用的記憶體,a後面還有6位元組的空記憶體。

ii、執行strcpy,觀察記憶體

發現a雖然只有2個位元組的記憶體,但是後面的記憶體都被覆蓋掉了(對比上下兩圖可以發現值為9f的這個記憶體被覆蓋成00了,其他的CC被62填充)

iii、得出結論

會覆蓋字串陣列之外的記憶體(陣列越界)。假設陣列b無限大,那豈不是把整個堆都覆蓋掉了?果然這個函式非常危險,要改用strncpy()。

b)如果src長度小於dset呢?

src從頭到\0都被複制到dset中,dest多餘的部分沒有變化(右圖)。這種情況沒什麼問題。

c)手寫strcpy

發現網上大家的程式碼都沒能解決陣列越界的問題,我加了一行程式碼 if (*dst == '\0') break; 目前看來沒什麼問題。

char* my_strcpy(char *dst, const char *src) {
	assert(dst != NULL && src != NULL);//斷言輸入不為空,空則呼叫abort()異常終止程序
	char *ret = dst;
	while ((*dst = *src) != '\0') {
		dst++;
		src++;
		if (*dst == '\0') break;//如果此時目標陣列已經結束,就不用繼續複製了
	}
	return ret;
}

除錯發現執行結束時不會覆蓋dst後面的記憶體了。

d)總結

在一本面試書上看到過,說這個函式是MS考慮到普適性,故意寫的很簡單。不過這個問題也很老了,只是憑興趣研究下。總結起來就是程式設計師要注意記憶體溢位的問題,特別在使用指標的時候,這種小問題很可能會導致一些重大安全問題。

2、strlen()

size_t  __cdecl strlen(const char * s){
    int i = 0;
    while( *s ) {
        i++;
        s++;
    }
    return i;
}

3、strcat()

char * __cdecl strcat(char * dst, const char * src){
    char *p = dst;
    while( *p )
        p++;
    while( *p ++ = *src ++ )
        ;
    return dst;
}

4、strcmp()

int strcmp(const char *str1, const char *str2){
   int ret=0;
   while( !(ret = *(unsigned char*)str1 - *(unsigned char*)str2 ) && *str1 )
     {
       str1++;
       str2++;
     }
     if(ret < 0)
        return -1;
     else if(ret > 0) 
        return 1;
     return 0;    
 }