如果讀者不知道sprintf, snprintf, asprintf, 可以google一下,因為他們不是把字串打印到畫面,而是功能少一點,只有格式化,而常見的printf, fprintf, 格式化字串後還會輸出到畫面。
它一開頭給的兩個例子不是很好,筆者也不是看得很懂,可以參考這篇文章
我實作了一下
#define _GNU_SOURCE //cause stdio.h to include asprintf
#include <stdio.h>
int main() {
int tmp = 10000; //100
char *cstr;//[20];
//sprintf(cstr, "%d * %d = %d", tmp, tmp, tmp * tmp );
//snprintf(cstr,sizeof(cstr), "%d * %d = %d", tmp, tmp, tmp * tmp );
asprintf(&cstr, "%d * %d = %d", tmp, tmp, tmp * tmp );
printf("%s \n", cstr);
}
傳統的sprintf,函數有個缺點,就是有
buffer overflow的問題,
一開始,當tmp =100 時,輸出10 * 10 = 100,但是tmp=10000. 就
*** buffer overflow detected ***: ./sprf terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x63)[0xb76b17b3]
以下省略,
這時候,C99 就新增了snprintf, 比sprintf 多了一個參數,字串長度
sizeof(cstr),這樣就不會buffer overflow,可是又帶來了一個問題,
上面程式的輸出結果為
10000 * 10000 = 100
snprintf是給多少長度,就印出多長的字。
而GNU C Library的維護者,又發明asprintf來改善sprintf,
乾脆連長度也不用指定了,幫你分配長度。
而asprintf 做了兩件事,
作者寫了比對,
老式寫法,
int len = strlen("strings ") + strlen(in) + 1;
cmd = malloc(len);
snprintf(cmd, len, "strings %s", in);
新式寫法,
asprintf(&cmd, "strings %s", in);
只是引進了asprintf,仍然有安全性的問題,因為不限長度,還是有可能遇到記憶体不足。還不如snprintf。
所以作者又編了一個巨集,來防止這個情況,
Stopif(asprintf(&str, "%s", user_input)==-1, return -1, "asprintf failed.")
再來是
Constant Strings 的問題,
#include <stdio.h>
int main(){
char *s1 = "Thread";
char *s2;
asprintf(&s2, "Floss");
printf("%s\n", s1);
printf("%s\n", s2);
//s2[0]='f'; //Switch Floss to lowercase.
//s1[0]='t'; //Segfault.
free(s2); //Clean up.
free(s1); //Segfault.
}
這時的
asprintf 變成一個可變換字元的複製函數,
程式中的s2不會當掉,而s1會
程式記憶體區段錯誤 (core dumped)
或
*** Error in `./strdup': munmap_chunk(): invalid pointer: 0x08048566 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x767c2)[0xb76357c2]
想一想以往高階語言的觀念,好像沒有char的觀念,用mid, left, substr, right來替換字串裏的字元,在C語言就要小心。
編譯時,聰明的編譯器會告訴你
strdup.c:12:5: warning: attempt to free a non-heap object [-Wfree-nonheap-object]
一個簡單的宣告,背後藏著一些秘密。