大師的火力展示,**大師也觀摩別人的code嗎?**1988年C語言成熟度已經達到很高的水平嗎?
在第5章,有很多玄妙的指標寫法:
大師不愧為大師,一個字串複製,寫了4個版本 而且愈寫愈玄。
/* strcpy: copy t to s; array subscript version */
void strcpy(char *s, char *t)
{
int i;
i = 0;
while ((s[i] = t[i]) != '\0')
i++;
}
For contrast, here is a version of strcpy with pointers:
/* strcpy: copy t to s; pointer version */
void strcpy(char *s, char *t)
{
int i;
i = 0;
while ((*s = *t) != '\0') {
s++;
t++;
}
}
In practice, strcpy would not be written as we showed it above. Experienced C programmers would prefer
/* strcpy: copy t to s; pointer version 2 */
void strcpy(char *s, char *t)
{
while ((*s++ = *t++) != '\0')
;
}
As the final abbreviation, observe that a comparison against '\0' is redundant, since the question is merely whether the expression is zero. So the function would likely
be written as
/* strcpy: copy t to s; pointer version 3 */
void strcpy(char *s, char *t)
{
while (*s++ = *t++)
;
}
第一版是陣列版,第二版是指標版,我們可以比較清楚的看出一個字元逐一個字元拷貝的動作。
而第三/四版,表面上看起來是while迴圈,但是在gdb中
(gdb) s
strcpy (s=0x22ff34 "0\032@", t=0x22ff3e "ABCDEFG") at a01.c:67
67 while (*s++ = *t++)
(gdb) n
69 }(gdb) n
直接塞一串字元!!覺得這種寫法不是很容易理解,但是精簡得令人覺得不可思議,且
一步到位。
測試的code是:
#include <stdio.h>
void strcpy(char *s, char *t);
void strcat(char s[], char t[]);
int main()
{
char src[10]="ABCDEFG";
char des[10]="HI";
char *ps;
char *pd;
ps=src;
pd=des;
strcpy(pd,ps);
printf("%s \n",pd);
printf("%s \n",ps);
}
在第二章,有一個例子:
/* strcat: concatenate t to end of s; s must be big enough */
void strcat(char s[], char t[])
{
int i, j;
i = j = 0;
while (s[i] != '\0') /* find end of s */
i++;
while ((s[i++] = t[j++]) != '\0') /* copy t */
;
}
最玄妙的是最後一行,有了上面的預防針,當然,也是一步到位。
while ((s[i++] = t[j++]) != '\0') /* copy t */
測試的code如下:
int main()
{
char src[10]="ABCDEFG";
char des[2]="HI";
char *ps;
char *pd;
ps=src;
pd=des;
strcat(ps,pd);
printf("%s \n",pd);
printf("%s \n",ps);
}
習題5-3
Exercise 5-3. Write a pointer version of the function strcat that we showed in Chapter 2: strcat(s,t) copies the string t to the end of s.
寫一個指標版的strcat
void strcat(char *s, char *t)
{
int i, j;
i = j = 0;
while ( *(s+i) != '\0') /* find end of s */
i++;
while ((*(s+i++) = *(t+j++)) != '\0') /* copy t */
;
}
筆者覺得這樣比較能理解。對於**while ((*(s+i++) = *(t+j++)) != '\0') /* copy t */
;**這一行總是可以一步到位,覺得很神奇。
因為這樣會讓人想把while拿掉。
拿掉while試試,結果變成
$ a
HI
ABCDEFGH
$ a
HI
ABCDEFGHI
所以while還是有用,拿掉只會做一次。沒拿掉會做多次,只是在gdb trace模式,都是一次!!
筆者把測試code改成。
#include <stdio.h>
void strcpy(char *s, char *t);
void strcat(char s[], char t[]);
int main()
{
char src[30]="ABCDEFG";
char des[10]="HIJKLMNOPQ";
char *ps;
char *pd;
ps=src;
pd=des;
//strcpy(&src,&des);
//strcpy(pd,ps);
strcat(ps,pd);
printf("%s \n",pd);
printf("%s \n",ps);
}
這時結果就不如預期,用gdb trace一下,發現:
Program received signal SIGSEGV, Segmentation fault.
0x0040148f in strcat (
s=0x22ff1a "ABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLM
NOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGH
IJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLM"...,
t=0x22ff10 "HIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABC
DEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNO
PQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABCDEFGHIJKLMNOPQABC"...) at a01.c:41
41 while ((*(s+i++) = *(t+j++)) != '\0') /* copy t */
(gdb)
除錯的過程中,發現
(gdb) p s
$8 = 0x22ff1a "ABCDEFGHIJKLMNOPQAB"
(gdb) p t
$9 = 0x22ff10 "HIJKLMNOPQABCDEFGHIJKLMNOPQAB"
(gdb) p &s
$10 = (char **) 0x22ff00
(gdb) p &t
$11 = (char **) 0x22ff04
0x22ff10 和 0x22ff1a 如果以16進位來說的話,a 是10,會不會是 靠得太近引起的問題??
筆者不死心的一路試下去,發現
是 **char des[13]="HIJKLMNOPQR";**的問題,如果是des[13],不能放13個字元,只能放12個,不然會產生異常,算是筆者耍寶。
timloo提到:
如果是des[13],不能放13個字元,只能放12個
這就是str和mem的差別囉,str一定要留個位置來放"\0".