LoginSignup
7
3

More than 5 years have passed since last update.

strcpyで最近よく間違えること。

Last updated at Posted at 2017-03-13

同じミスを繰り返すのでメモ

Cでよくこんな感じのコードを書く。

そして、Segmentation fault(コアダンプ)と表示されてしばらく悩む(^_^;)
gdbでデバッグ始めて、落ちてる場所特定して、で終われば良いんだけど、そこじゃないところで落ちてるように見えるんで、さらに悩む。

test.c

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char OrgMessage[] = "Original Message";
    char *temp = NULL;

    strcpy(temp, OrgMessage);

    printf("%s\r\n", temp);

    return 0;
}

正しくはこっち

test.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char OrgMessage[] = "Original Message";
    char *temp = NULL;

    /* '\0'を含んだサイズを確保するから[+1]は不要! @shiracamusさん
    temp = (char*)malloc(sizeof(char) * sizeof(OrgMessage) + 1);
    */
    temp = (char*)malloc(sizeof(char) * sizeof(OrgMessage));

    if(temp == NULL){
        printf("failed malloc\r\n");
        /* コメントからエラーの場合は終わらせよう!@fujitanozomuさん */
        exit(EXIT_FAILURE); 
    }

    strcpy(temp, OrgMessage);

    printf("%s\r\n", temp);

    free(temp);

    return 0;
}

ただのポインタのtempに文字列コピーしようとして0アクセスしてるのが原因。
ちゃんとサイズを確保しよう。

先輩より
strcpy(), strncpy(), strlcpy(), strdup(), strndup()どれを使うにしても、
コピー先が十分大きくないとデータが捨てられてしまうので、作りたいものを検討してから
どの関数が適当か考えましょう。

データ捨てられてダメなら、どの関数使おうが関係無く、その手前で判定入れようね!
とのことでした(^_^;)

コンパイラと環境

(Windows 7 64bit, cygwin 2.4.1)

gcc --version
gcc(GCC) 5.3.0

gcc -Wall test.c

./a.exe
Original Message

追記(strncpy()版)

せっかくコメントいただいたので調べたことをメモ。
strlcpyは標準に含まれていないようなので今回は見送り(/_;)
strcpy()は'\0'までをコピーしてくれますが、終端文字'\0'が無ければいくらでも(?)コピーしてメモリ壊してくれます。
そのため、strncpy()を推奨。
できればstrlcpy()を使いましょう。(標準ではありません。)

test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char OrgMessage[] = "Original Message";
    char *temp = NULL;
    int i;

    /* tempのサイズは、"Original Message" + '\0' 分 */
    temp = (char*)malloc(sizeof(char) * sizeof(OrgMessage));

    if(temp == NULL){
        printf("failed malloc\r\n");
        /* malloc失敗したら止める */
        exit(EXIT_FAILURE); 
    }
    /* strlenは'\0'を含まないサイズを返す。 */
    /* strncpyはOrgMessageより大きいsizeを指定すると'\0'で残りを埋める。 */
    strncpy(temp, OrgMessage, strlen(OrgMessage) + 1);

    printf("%s\r\n", temp);

    for(i = 0; i < strlen(temp) + 1; i++){
        printf("%x ", temp[i]);
    }

    free(temp);

    return 0;
}

実行結果

gcc -Wall test.c

./a.exe
Original Message
4f 72 69 67 69 6e 61 6c 20 4d 65 73 73 61 67 65 0

追記(strdup()版)

コメントいただいたのを追加!
strdupのが楽ですね。
free()するのに、が必要なのがなんか嫌ですが。。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char OrgMessage[] = "Original Message";
    char *temp = NULL;
    int i;

    temp = strdup(OrgMessage);

    if(temp == NULL){
        printf("failed strdup\r\n");
        exit(EXIT_FAILURE); 
    } else {
        printf("%s\r\n", temp);

        for(i = 0; i < strlen(temp) + 1; i++){
            printf("%x ", temp[i]);
        }

        free(temp);
    }
    return 0;
}

実行結果


gcc -Wall test.c

./a.exe
Original Message
4f 72 69 67 69 6e 61 6c 20 4d 65 73 73 61 67 65 0

追記(strndup()版)

strdup()はstrcpy()と同じ問題があるような感じがしたので、
strndup()版も作成してみました。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char OrgMessage[] = "Original Message";
    char *temp = NULL;
    int i;

    temp = strndup(OrgMessage, (strlen(OrgMessage) + 1));

    if(temp == NULL){
        printf("failed strdup\r\n");
        exit(EXIT_FAILURE); 
    } else {
        printf("%s\r\n", temp);

        for(i = 0; i < strlen(temp) + 1; i++){
            printf("%x ", temp[i]);
        }

        free(temp);
    }
    return 0;
}

実行結果


./a.exe
Original Message
4f 72 69 67 69 6e 61 6c 20 4d 65 73 73 61 67 65 0


追記(strlen()について)

sizeof()と異なり、strlen()は文字数分のメモリオーバーヘッドが発生するとコメントをいただきました。

文字数が毎回異なり、sizeを取得し直すようなプログラムを作って、遅くて困った時は、
文字数を固定値にしてしまえばstrlen()分の処理が不要になって早くなるのかなーって感じですかねー???

クロックの遅いマイコン等だったら(malloc()とあわせて)検討すると良いかも???

現状(私が作っている環境)ではそこまで非力じゃないから気にしなくて良いかな??

メモ(プロトタイプ)


#include <string.h>

char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
size_t strlen(const char *s);
  /* 文字列*sの長さを返す。長さに'\0'は含まない。 */

char *strdup(const char *s);
char *strndup(const char *s, size_t n);

謝辞

ご指摘ありがとうございますm(_ _)m

  • shiracamusさん
  • fujitanozomuさん
  • bsdhackさん

変更履歴

日付 変更箇所
2017/03/13 新規作成
コードに色つけました(^_^)b
2017/03/14 コメントいただいた内容を反映。(strncpy版)
2017/03/16 コメントいただいた内容を反映。(strdup版、ついでにstrndup版)
7
3
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3