dirname()とbasename()の挙動の確認
現在、社内の人と勉強会をしているLinuxProgramingInterfaceにて出て来たdirname()とbasename()の挙動について、実際に確認してみました。
18章に以下のような文言がありました。
dirname() と basename() はいずれも pathname の文字列を変更する可能性があります。 このため、pathname を再度利用する場合は、例 18-5 に挙げるようにコピーを作成し、コピーを dirname()、basename() へ渡します。
ここでいうpathnameはdirname() or basename()に与える/xxx/yyyみたいなやつです。
上記がどういうことかと教えてもらったところ、両関数は与えられた文字列を直接書き換えちゃうので、もっかい文字列を参照したい場合はコピーした文字列を両関数に渡してねってことらしいです。
本当に文字列を書き換えてしまうのか確認してみました。
使用したソースコードは、LinuxProgramingInterfaceの中に出て行きた例18-5です。
http://man7.org/tlpi/code/online/dist/dirs_links/t_dirbasename.c.html
gdbで見てみます。
下記は、行数の確認 & 引数の指定 & 引数の確認 & ブレイクポイントの設定です。
(gdb) list 23 #include <libgen.h> 24 #include "tlpi_hdr.h" 25 26 int 27 main(int argc, char *argv[]) 28 { 29 char *t1, *t2; 30 int j; 31 32 for (j = 1; j < argc; j++) { (gdb) set args /dirname/filename (gdb) show args Argument list to give program being debugged when it is started is "/dirname/filename". (gdb) b 30
実行!
与える文字列は/dirname/filename
としました。
文字列をt1,t2にコピーして、中身を確認します。
この時点ではt1,t2ともに/dirname/filename
のままみたいです。
(gdb) r Starting program: /xxxx/t_dirbasename /dirname/filename Breakpoint 1, main (argc=2, argv=0x7fffffffe648) at t_dirbasename.c:32 32 for (j = 1; j < argc; j++) { (gdb) n 33 t1 = strdup(argv[j]); (gdb) n 34 if (t1 == NULL) (gdb) n 36 t2 = strdup(argv[j]); (gdb) n 37 if (t2 == NULL) (gdb) n 40 printf("%s ==> %s + %s\n", argv[j], dirname(t1), basename(t2)); (gdb) p t1 $1 = 0x603010 "/dirname/filename" (gdb) p t2 $2 = 0x603030 "/dirname/filename" (gdb) p *t1 $3 = 47 '/' (gdb) p *t2 $4 = 47 '/' (gdb) p *(t1+1) $5 = 100 'd' (gdb) p *(t1+2) $6 = 105 'i' (gdb) p *(t1+3) $7 = 114 'r' (gdb) p *(t1+4) $8 = 110 'n' (gdb) p *(t1+5) $9 = 97 'a' (gdb) p *(t1+6) $10 = 109 'm' (gdb) p *(t1+7) $11 = 101 'e' (gdb) p *(t1+8) $12 = 47 '/' (gdb) p *(t1+9) $13 = 102 'f'
さて、問題のdirname(),basename()の実行。
確かにt1は/dirname
になり、t2は/dirname/filename
になりました。
着目すべきは*(t1+8)が /
ではなく、\000
に書き換えられているところですね!
だからt1が/dirname
になるんですね〜。
(gdb) n /dirname/filename ==> /dirname + filename 42 free(t1); (gdb) p t1 $14 = 0x603010 "/dirname" (gdb) p t2 $15 = 0x603030 "/dirname/filename" (gdb) p *t1 $16 = 47 '/' (gdb) p *t2 $17 = 47 '/' (gdb) p *(t1+8) $18 = 0 '\000' (gdb) p *(t1+9) $19 = 102 'f'
もちろん、p t1+9は、filename
になりました。
(gdb) p *(t2+1) $20 = 100 'd' (gdb) p t1+9 $21 = 0x603019 "filename" (gdb) quit
以上、確認終わり!