掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.26346

strncpyについて
投稿者---JCLエラー(2006/03/07 16:08:58)


strncpyについて質問です。
以下のソースを実行すると、期待するものが出力されません。
なぜなのか?頭の悪い私に教えてください。(XP cygwin gcc)

<期待する出力>
コピー先DATA=
コピー元q=123456789
コピー後DATA=123456789
初期化後DATA=
コピー先DATA=
コピー元q=abcde
コピー後DATA=abcde ←ここ
初期化後DATA=

<実際の出力>
コピー先DATA=
コピー元q=123456789
コピー後DATA=123456789
初期化後DATA=
コピー先DATA=
コピー元q=abcde
コピー後DATA=abcde6789 ←ここ
初期化後DATA=

<file.txtの内容>
aa:123456789
bb:abcde

<以下ソース>
#include <stdio.h>
#include <string.h>

#define FILE_PATH "./file.txt"
#define ENCODE 2

int main(void){

    FILE *fp;
    char REC[128];
    char ID[3]="";
    char DATA[20]="";
    char *p,*q;
    int i=0;

    if ((fp = fopen(FILE_PATH,"r")) == NULL){
        printf("<ERROR>%s can not open.\n", FILE_PATH);
        exit(-1);
    }
    
    while((fgets(REC,sizeof(REC),fp)) != NULL){

        REC[strlen(REC) - ENCODE] = '\0';

        for(p = REC; (q = strtok(p,":")) != NULL; p = NULL){

            switch (i){
            case 0  : strncpy(ID, q, strlen(q));
                        break;
            case 1  : printf("コピー先DATA=%s\n", DATA);
                        printf("コピー元q=%s\n", q);
                        strncpy(DATA, q, strlen(q));
                        printf("コピー後DATA=%s\n", DATA);
                        break;
            default :    break;
            }

        ++i;

        }

        //初期化?

        DATA[0] = '\0';
        printf("初期化後DATA=%s\n", DATA);

        i=0;

    }

    fclose(fp);

}



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:strncpyについて 26347 Blue 2006/03/07 16:16:13


No.26347

Re:strncpyについて
投稿者---Blue(2006/03/07 16:16:13)


あまりソースをよんでいませんが、

strncpyはstrcpyと違って、n文字コピーしたあとに'\0'を入れません。
なので、自分で入れる必要があります。

初期化の時点で、(memset関数かfor文で)全ての文字を'\0'で埋めておけばうまくいくかも。


この投稿にコメントする

削除パスワード

No.26348

Re:strncpyについて
投稿者---wis(2006/03/08 03:30:25)


>strncpyはstrcpyと違って、n文字コピーしたあとに'\0'を入れません。
>なので、自分で入れる必要があります。
勘違いしそうなので
strncpyは n文字までコピーした後コピー元がnより小さいとき
残った部分を'\0'で埋め尽くします。
しかし以下の場合
strncpy(DATA, q, strlen(q));
コピー元文字数とnが等しいために'\0'で埋め尽くすという作業を行いません
このコードの場合にDATAのバッファ領域にコピーするなら
strncpy(DATA, q, sizeof(DATA)-1);
とすると上手くいくかもしれません。



この投稿にコメントする

削除パスワード

No.26349

Re:strncpyについて
投稿者---wis(2006/03/08 03:34:16)


もう一点。
ENCODE 2 ってなってるけどホントにいいの?


この投稿にコメントする

削除パスワード

No.26350

Re:strncpyについて
投稿者---Blue(2006/03/08 08:59:35)


>strncpyは n文字までコピーした後コピー元がnより小さいとき
>残った部分を'\0'で埋め尽くします。
このような使い方はしたことなかったので少し勉強になりました。

後々よく考えてみたら、strncpyじゃなくてstrcpyでよいのではと思ったりして。


この投稿にコメントする

削除パスワード

No.26353

Re:strncpyについて
投稿者---JCLエラー(2006/03/08 16:06:31)


ありがとうございます。
とりあえず、memsetで解決しました。

>もう一点。
>ENCODE 2 ってなってるけどホントにいいの?
今はwindowsのテキストファイルでINPUT FILEのテスト用データを書いており、改行コードは2バイト、完成したプログラムを実際に動かすのはLinux上でINPUT FILEのデータの改行コード1バイトなんで、今はdefineでENCODE 2としています。

>後々よく考えてみたら、strncpyじゃなくてstrcpyでよいのではと思ったりして。
どこかのサイトで、「strcpy」より「strncpy」を使用したほうがよいというのを見た覚えがありまして、strcpyはコピー元がコピー先より大きかったらどうのこうのとか書いてあったと思いましたが、「こっちのほうがいいんだったら・・・」と安直な考えでstrncpyを使用してしまっていました。調べてみようと思います。



この投稿にコメントする

削除パスワード

No.26354

Re:strncpyについて
投稿者---wis(2006/03/08 19:43:27)


>ありがとうございます。
>とりあえず、memsetで解決しました。
strncpy(DATA, q, strlen(q));
このコードが非常に危険なコードと分かっていてmemsetで解決したのなら
問題はないのですが。。。

>>もう一点。
>>ENCODE 2 ってなってるけどホントにいいの?
>今はwindowsのテキストファイルでINPUT FILEのテスト用データを書いており、改行コードは2バイト、完成したプログラムを実際に動かすのはLinux上でINPUT FILEのデータの改行コード1バイトなんで、今はdefineでENCODE 2としています。

Windows上のテキストファイルをfopenのテキストモードでオープンした場合、改行コード\r\nは自動的に\n変換されるはずですが。。。?

>>後々よく考えてみたら、strncpyじゃなくてstrcpyでよいのではと思ったりして。
>どこかのサイトで、「strcpy」より「strncpy」を使用したほうがよいというのを見た覚えがありまして、strcpyはコピー元がコピー先より大きかったらどうのこうのとか書いてあったと思いましたが、「こっちのほうがいいんだったら・・・」と安直な考えでstrncpyを使用してしまっていました。調べてみようと思います。

これは上でいったこのコードの危険性を回避できるためです。
strncpy(DATA, q, strlen(q));
この場合DATAのバッファ領域を超える長い文字列がqに入った場合、
バッファオーバーフローという致命的な欠陥を持つことになります。
つまり strncpy(DATA, q, sizeof(DATA)-1); としておけば、
悪くともバッファオーバフローは無いからです。
sizeof(DATA)-1というのはstrncpyの特性である\0で埋めるを
利用するためのもので、これをしないとあなたの提示した
コピー後DATA=abcde6789
という結果が出てしまいます。

これはたまたま、DATAの領域が同じで9の後ろに'\0'が入っていた為に
ここまで表示されただけで、もしここに'\0'が無ければメモリ上のどこかに'\0'が出るまで表示し続けます。

と、長々書きましたが
とりあえず、おめでとうございます。


この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧