2012年7月9日月曜日

【C言語】strcat関数を自作してみた。【第二弾】

蝉がミンミンとうるさく鳴く、そんな夏がやってきました。イトウです。

【C言語】strlen関数を自作してみた。

前回の記事と同じく、ライブラリ関数自作してみようシリーズです。
今回は文字列連結のstrcatを自作してみました。


 自作のmy_strcat関数(my_strcat)
#include<stdio.h>

// str1の後ろにstr2を連結する
char * my_strcat(char* str1, const char* str2){
    char *top;

    // (1) 返り値用に先頭アドレスを保持しておく
    top=str1;

    // (2) str1のポインタを最後まで進める
    while(*str1++ != '¥0');    
    *str1--;

    // (3)str1の背後から、str2の文字をコピーしていく
    while( (*str1++ = *str2++) != '¥0');

    return top;
}


main関数
#include<stdio.h>

int main(void){
     // (4)ポインタで定義して渡すとエラー
     // char *str1 = "Tokyo";
     // char *str2 = "Japan";

     // 配列で定義して渡せばOK
     char str1[] = "Tokyo";
     char str2[] = "Japan";    

     my_strcat(str1, str2);
     printf("%s¥n", str1);

     return 1;
}



では、コードについて少しだけ説明を補足します。



(1)返り値用に先頭アドレスを保持しておく

strcat関数の中でポインタ情報を変更するので、別の変数を作って先頭アドレスを保持しておく必要がある。もし普通通りに return str1; としてしまうと、ポインタを進めた先頭アドレス(=つまりstr1の先頭アドレスとは違う値)が帰ってしまう。


(2) str1のポインタを最後まで進める

文字列の最後には'¥0'が入っています。str != '¥0' が成立するまでwhile文を回すことで、str1の最後の終端文字(¥0)までポインタを進めることができます。そしてその後の*str1-- により、終端文字の位置にポインタを戻している。次の段階では、この位置からstr2の文字をコピーしていくことになる。


(3)str1の背後から、str2の文字をコピーしていく

一文字ずつ、str2の文字をstr1にコピーしていく。終端文字までコピーが完了したら、ループを終了する。


(4)ポインタで定義して渡すとエラー

my_strcat関数に文字列を渡す際、ポインタを渡すとエラーが発生する。この原因は不明だが、メモリ領域に関係するものではないかと考えられる。
つまり、*str1, *str2と続け様に定義すると、メモリ領域の連続した部分が使用される。my_strcat関数内で、str2はconst(定数、つまり代入しちゃダメ)で定義されているので、str1のメモリ領域にズカズカ文字をつっこんでいくとstr2の領域に被さり、そこはconstだからSegmentation Fault!みたいなノリじゃないかなと。推測ですが。

本家のstrcat関数でも同様のエラーが発生することから、配列で文字列を定義して渡すのが賢いと思われます。






ポインタの扱いは難しいですね。。CはJavaなんかと違ってエラーメッセージがあまり詳細じゃないので苦労しますね。しかし、ライブラリ関数を自作するのは勉強になって良いですね。



1 件のコメント:

  1. ご紹介ありがとうございます。
    一つ疑問なのですが、第一引数で確保されている連続したメモリー領域は、ローカル変数なので、ヒープにとられていないのではないかと思います。
    だとすれば、その最終アドレス以降に第二引数を単純にコピーすれば、データ領域のみならず、プログラム領域を破壊することにならないでしょうか?
    かといって、いちいちmallocでヒープを確保すれば、時間コストが高くつきそうです。ご指導宜しくお願いします。

    返信削除