【C言語】
malloc(strlen(src))
終端文字’¥0’の1byte足りない

’¥0’の 1byte足りない malloc(strlen(src))

warning: ‘strcpy’ writing one too many bytes into a region of a size that depends on ‘strlen’

警告:strlen()で確保した領域は終端文字’¥0’の考慮が足りない
[-Wstringop-overflow=]


終端文字’¥0’の1byte足りない

//gcc -fsanitize=address -g で潜在バグが表面化する
void I_hate_bug(void){
    char *str = malloc(strlen(__func__));
    if(str == NULL){
        perror(NULL);
        return  ;
    }
    strcpy(str,__func__);
    puts(str);
    free(str);
}

例えば
char *str = malloc(strlen(“abc”)); 
strlen(“abc”)の結果は3となり
終端文字’¥0’の分は
malloc()で確保されません。

strcpy()は終端文字’¥0’までコピーするの
strの領域は1byte足りずメモリ破壊されます。

熱心すぎる修正例(非推奨)

void I_love_casts(void){
    char *str ; 
    size_t len = strlen(__func__)+sizeof('\0');
    str = (char *)malloc(sizeof(char)*len);
    if((char *)NULL == str){
        perror(NULL);
        return  ;
    }
    strcpy(str,__func__); 
    puts(str);
    free(str);
}

(1)C言語では(char *)mallocのキャストが不要。C++と違う
(2)C言語ではsizeof(‘\0’)の結果は1ではない。C++と違う
(3)C言語ではsizeof(char)の結果は1と決まっているので掛け算不要
(4)NULLにキャスト不要

参考

http://www.kouno.jp/home/c_faq/c7.html#7

http://www.kouno.jp/home/c_faq/c7.html#8

修正例

void I_hate_casts(void){
    char *str = malloc(strlen(__func__)+1);
    if(str == NULL){
        perror(NULL);
        return  ;
    }
    strcpy(str,__func__);
    puts(str);
    free(str);
}

推奨修正例(strdupを使う)

void I_love_Simple(void){
    char *str = strdup(__func__);//標準関数を使う
    if(str == NULL){
        perror(NULL);
        return  ;
    }
    strcpy(str,__func__);
    puts(str);
    free(str);
}

malloc(strlen(func)+1)の1が
マジックナンバーで気になる人は
標準関数strdup()を使うと良いでしょう。


まとめ(コンパイル可能)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//終端文字’¥0’の1バイト足りない
//gcc -fsanitize=address -g で潜在バグが表面化する
void I_hate_bug(void){
    char *str = malloc(strlen(__func__));
    if(str == NULL){
        perror(NULL);
        return  ;
    }
    strcpy(str,__func__);
    puts(str);
    free(str);
}
//熱心すぎる修正例(非推奨)
//(1)C言語では(char *)mallocのキャストが不要。C++と違う
//(2)C言語ではsizeof('\0')の結果は1ではない。C++と違う
//(3)C言語ではsizeof(char)の結果は1と決まっているので掛け算不要
//(4)NULLにキャスト不要
void I_love_casts(void){
    char *str ; 
    size_t len = strlen(__func__)+sizeof('\0');
    str = (char *)malloc(sizeof(char)*len);
    if((char *)NULL == str){
        perror(NULL);
        return  ;
    }
    strcpy(str,__func__); 
    puts(str);
    free(str);
}
//修正例   
void I_hate_casts(void){
    char *str = malloc(strlen(__func__)+1);
    if(str == NULL){
        perror(NULL);
        return  ;
    }
    strcpy(str,__func__);
    puts(str);
    free(str);
}
//推奨修正例   
void I_love_Simple(void){
    char *str = strdup(__func__);//標準関数を使う
    if(str == NULL){
        perror(NULL);
        return  ;
    }
    strcpy(str,__func__);
    puts(str);
    free(str);
}
int main(void){
    I_hate_bug();
    I_love_casts();
    I_hate_casts();
    I_love_Simple();
}

コンパイル実行しても異常終了しない場合は
gcc -fsanitize=address -g
でコンパイル実行してみて下さい。

アンケート

3
10文字の文字列領域をmallocで確保するのに最善の方法はどれですか?