【C言語】
NULLにキャストはいらない!
不要なキャストを削除しよう

if(ptr==(void *)NULL) NULLに キャスト不要

warning: cast from pointer to integer of different size

警告:ポインタ型を型幅の違う整数型にキャストした
[-Wpointer-to-int-cast]


■(int)NULL

#include    <stdio.h>
#include    <string.h>
void f1(void){
    char    buf[1024];
    memset(buf,(int)NULL,sizeof(buf));//NG
}

「0 はマジックナンバーなので使用禁止!」と
教育された初級者が
0と書けず
NULLと書き、
コンパイラに警告されて (int)NULL とキャストする場合が多いです。


➡(int)NULL➡0に修正

void f2(void){
    char    buf[1024];
    memset(buf,0,sizeof(buf));//OK
}

0はマジックナンバーではないので素直にべたで0と書く。

■(char)NULL

size_t f3_strlen(char *s)
{
  char  *p;
  for (p = s; *p != (char)NULL; p++)//NG
    ;
  return p - s;
}

「文字列の最後にはNULL文字がある」と教育された初級者が
‘\0’と書かずに
NULLと書き、
コンパイラに警告されてキャストする場合が多いです。

➡(char)NULL➡’\0’に修正

size_t f4_strlen(char *s)
{
  char  *p;
  for (p = s; *p != '\0'; p++)//OK
    ;
  return p - s;
}

「文字列の最後には終端文字’\0′がある」と教育しましょう。

■問題:文字列にキャストすると?

warning: suspicious usage of ‘sizeof(A*)’; pointer to aggregate

警告:余計なキャストが疑わしい
[misc-sizeof-expression]

問題:次のプログラムは何と表示されるでしょうか?

#include    <stdio.h>
#include    <string.h>
#define STRING  (const char *)"123456789abcdef" 
int main(void)
{
  printf("%ld\n",sizeof(        "123456789abcdef"));
  printf("%ld\n",sizeof((char *)"123456789abcdef"));
  printf("%ld\n",sizeof(STRING));
  printf("%ld\n",strlen(STRING));
}

sizeof((char *)“123456789abcdef”)は余計なキャストをしているため、文字列リテラル”123456789abcdef”は無視され
sizeof(char *)と同じ結果が求まります。

sizeof(STRING)も同様ですが、
キャストがマクロで隠されるのでより問題の発見が難しくなります。

●解答

16
8
8
15

■(void *)NULL

warning: redundant cast to the same type

警告:冗長キャストがコードを汚す
[google-readability-casting]

#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
int main(void)
{
    char    *cp = (char *)malloc(sizeof(char)*(int)1024);
    if((char *)cp == (char *)NULL){
        printf("メモリ確保失敗\n");
        return  (int)1;
    }
    memset((void *)cp,(int)'\0',sizeof(char)*1024);
    strncpy((char *)cp,(const char *)"a-b-c",(int)6);
    char    *sp = strchr((char *)cp,(int)'-');
    if((char *)sp == (char *)NULL){
        printf((const char *)"'-'検索失敗\n");
        return  (int)1; 
    }
    printf((const char *)"%s\n",(char *)sp);
    if((char *)cp != (char *)NULL){
        free((void *)cp);
    }
    return  (int)0;
}

●例1:malloc()/free()にキャストしたがる理由

C-FAQ7.7

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

MEM02-C. Immediately cast the result of a memory allocation function call into a pointer to the allocated type

ここの末尾のコメント欄の議論が面白い

●例2:NULLにキャストしたがる理由

NULLについて

http://f4.aaacafe.ne.jp/~pointc/log1018.html

●例3:文字定数にキャストしたがる理由

推測⇒
C言語では文字定数がint型だと知らないから
int型に明示的にキャストする?

●例4:memset/cpy/cmpの引数にキャストしたがる理由

推測⇒
K&Rという初代C言語では汎用ポインタの void * がなく
char *で代用していた大昔の習慣が現代に継承されている?

➡不要なキャストを削除すると読みやすい

#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
int main(void)
{
    char    *cp = malloc(1024);
    if(cp == NULL){
        printf("メモリ確保失敗\n");
        return  1;
    }
    memset(cp,'\0',1024);
    strcpy(cp,"a-b-c");
    char    *sp = strchr(cp,'-');
    if(sp == NULL){
        printf("'-'検索失敗\n");
        free(cp);
        return  1;
    }
    printf("%s\n",sp);
    free(cp);
}

不要キャストを削除してサッパリしませんか?


■mallocにキャストしないとコンパイルエラーになる?

C言語なのにC++コンパイラ使ってる?

ソースコード.cpp
サフィックスを.cppにすると
C++コンパイラが起動されるので
サフィックスを.cにして下さい。

➡50年以上前のK&Rコンパイラを使ってる?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//void *を返すmalloc関数の戻り値にわざわざキャストした例。
//尚、mallocのNULLチェックとfree処理はここでは省略する
typedef struct {
    char    data[1024];
} tag_t;
int main(void){
    char    *cp = (char *)malloc(sizeof(char)*strlen("abc")+1);
    strcpy(cp,"abc");

    short   *sp = (short *)malloc(sizeof(short));
    *sp = 0x1234;

    long    *lp = (long *)malloc(sizeof(long));
    *lp = 0x1234567887654321;

    tag_t   *tp = (tag_t *)malloc(sizeof(*tp));
    tp->data[0] = 'X';

    printf("%s:%x:%lx:%c\n",cp,*sp,*lp,tp->data[0]);
}

(1)このようなキャストが必要なコンパイラは
K&Rという古いコンパイラかもしれません。
⇒そんな古いコンパイラはスミソニアン博物館に寄付しましょう。

(2)このようなキャストが必要なコンパイラは
C++コンパイラかもしれません。
⇒Visual Studio使用者でC言語のつもりでC++コンパイラを起動している場合がありました。

(3)将来CからC++へ移行するかもしれないので今からキャストすると言う人はYAGNI原則
勉強しましょう。


■今時のコンパイラはmallocにキャスト不要

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//void *を返すmalloc関数の戻り値の不要キャストを削除した例。
//尚、mallocのNULLチェックとfree処理はここでは省略する
typedef struct {
    char    data[1024];
} tag_t;
int main(void){
    char    *cp = malloc(strlen("abc")+1);
    strcpy(cp,"abc");

    short   *sp = malloc(sizeof(short));
    *sp = 0x1234;

    long    *lp = malloc(sizeof(long));
    *lp = 0x1234567887654321;

    tag_t   *tp = malloc(sizeof(*tp));
    tp->data[0] = 'X';

    printf("%s:%x:%lx:%c\n",cp,*sp,*lp,tp->data[0]);
}

今のCコンパイラはこのキャストの無いコードを正常にコンパイル出来ます。

エラーが出るのは太古のコンパイラか
C++コンパイラを使用している場合です。


参考:

C-FAQ7.7:mallocが返した値を確保したデータ型のポインターに注意深くキャス トしている