【C言語】
#ifと#ifdefの違いは
非零か定義済みかの扱い

■#if DEBUG(DEBUGが非零なら成立)

#include    <stdio.h>
int main(void)
{
#define DEBUG   0
#if DEBUG       
    printf("0で定義されている\n");
#endif

#undef  DEBUG
#define DEBUG   1
#if  DEBUG   
    printf("1で定義されている\n"); 
#endif
}

#define DEBUG 0
#if DEBUG
DEBUGのは0
#if  0
0は偽
#if 偽
⇒展開されない

#define DEBUG 1
#if DEBUG
DEBUGのは1
#if  1
1は真
#if 真
⇒展開される

1で定義されている

■#ifdef DEBUG(DEBUGが定義済みなら成立)

#include    <stdio.h>
int main(void)
{
#ifdef DEBUG       
    printf("DEBUGは定義されていない\n");
#endif

#define DEBUG   1
#ifdef  DEBUG   
    printf("DEBUGは定義されている\n"); 
#endif
}

#ifdef DEBUG
#ifdef  定義済み
DEBUGは#defineで定義されていない
⇒展開されない

#define DEBUG 1
#ifdef DEBUG
#ifdef  定義済み
DEBUGは#defineで定義されている
⇒展開される

DEBUGは定義されている

■#ifと#endifしか使っていない例

char    *cpu[]={
    "z80",
#if     CPU==3
    "Core i3"
#endif
#if   CPU==5
    "Core i5"
#endif
#if   CPU==7
    "Core i7"
#endif
#if   CPU==9
    "Core i9"
#endif
};
#include <stdio.h>
int main(void){
    printf("%s\n",cpu[1]);//cpu[1]が展開されない場合暴走するかも
}

gcc -DCPU=9等とコンパイル時に適切な引数が与えられた時は問題がありませんが
gcc -DCPU=1234等と指定された場合コンパイルエラーにならず、
プログラム実行時異常終了してしまう可能性があります。

■#elifと#errorを使った例

char    *cpu[]={
    "Z80",
#if     CPU==3
    "Core i3"
#elif   CPU==5
    "Core i5"
#elif   CPU==7
    "Core i7"
#elif   CPU==9
    "Core i9"
#else
#error  "★未知のCPUはここでコンパイルエラーになる" 
#endif
};
#include <stdio.h>
int main(void){
    printf("%s\n",cpu[1]);
}

#elifと#errorを組み合わせると
予期せぬコンパイル状態となると
コンパイルエラーになってくれるので
間違いに直ぐ気が付きます。