講真,要深入理解 printf,只能自己看源碼。666…
從現象到本質
前些時間,朋友問了一個問題:
printf
的 %s
格式輸出,如果參數是其它類型的數據強制轉換爲 char*
的,結果會怎麼樣?
我想最好的方法莫過於馬上動手測試一下,看看結果。如果再問:printf 是線程安全的嗎?… 問題很多,這些都只是問題的表象,要從現象看本質。Linux 是開源的,何不看源碼理解程序工作原理呢?
從源碼上看 strnlen
是通過查找內存的 ‘\0’ 字符串結束符的。如果強制轉換的內存數據,沒有 ‘\0’ 結束符,那 strnlen
就會出現問題。
/* https://github.com/torvalds/linux/blob/master/arch/x86/boot/printf.c */
int printf(const char *fmt, ...) {
char printf_buf[1024];
va_list args;
int printed;
va_start(args, fmt);
printed = vsprintf(printf_buf, fmt, args);
va_end(args);
puts(printf_buf);
return printed;
}
int vsprintf(char *buf, const char *fmt, va_list args) {
...
switch (*fmt) {
...
case 's':
s = va_arg(args, char *);
len = strnlen(s, precision);
if (!(flags & LEFT))
while (len < field_width--)
*str++ = ' ';
for (i = 0; i < len; ++i)
*str++ = *s++;
while (len < field_width--)
*str++ = ' ';
continue;
}
...
}
/* https://github.com/torvalds/linux/blob/master/arch/x86/boot/string.c */
size_t strnlen(const char *s, size_t maxlen) {
const char *es = s;
while (*es && maxlen) {
es++;
maxlen--;
}
return (es - s);
}
總結
不只 printf
的實現,很多基礎函數,都能從 linux 源碼中查看。可以從 github 下載一份源碼,方便查看。下載個 zip 文件包吧,git 文件有點太大了。
/* https://github.com/torvalds/linux/blob/master/lib/string.c */
char *strcpy(char *dest, const char *src) {
char *tmp = dest;
while ((*dest++ = *src++) != '\0')
/* nothing */;
return tmp;
}
- 更精彩內容,請關注作者博客:wenfh2020.com