E05200029:Expected an expression. のエラーと#defineのスコープについて

E05200029:Expected an expression. のエラーについて質問があります。

使用マイコン:RX64M
開発環境:e2studio
コンパイラ:CC-RX V3.00

現在ヘッダファイルに下記のマクロ定義と、

#define NUM_BYTE 0x09
#define NUM1 0x01
#define NUM2 0x02
#define NUM3 0x03
#define NUM4 0x04
#define NUM5 0x05
#define NUM6 0x06
#define NUM7 0x07
#define NUM8 0x08
#define NUM9 0x09
ソースファイルに下記の関数を作りました。
void syori(int data, int *buf1 , int *buf2)
{
        switch (data)
        {
        case 1:
                buf1[NUM_BYTE] = {NUM1,NUM2,NUM3,NUM4,NUM5,NUM6,NUM7,NUM8,NUM9};
                buf2[NUM_BYTE] = {NUM1,NUM2,NUM3,NUM4,NUM5,NUM6,NUM7,NUM8,NUM9};
               
                break;
       
   /*~その他case文~*/
        default:
                break;
        }

}
 
この中で、配列の要素をマクロ定義した定数で初期化しようとすると、E05200029エラーで「式がありません」となります。
プログラムを書いている最中でも、「Ctrl+spaceキー」で buf1[NUM_BYTE] = {};の波かっこの中で補完機能を使おうとしても、
そこでだけは「デフォルトの候補がありません」と表示されます。
原因は分かりますでしょうか。
#defineはマクロ定義なので、スコープなどないと考えていたのですが、そのあたり自分だけの調査で限界が来たので、教えていただければと思います。
  • C言語の文法として配列を丸ごと代入することができません。あとbuf1[NUM_BYTE]はbuf1配列のNUM_BYTEで指定された位置の格納位置を意味しています。おそらく呼び出しものがNUM_BYTEの配列だと思います。でも、配列で確保した範囲でアクセスできるのはN要素なら0からN-1です。これは代入に間違いがありますし、範囲外のエリアへのアクセスになります。それからNUM_BYTEというのは気持ちが悪いのでNUM_COUNTSみたいなのの方がいいかと思います。intはバイトじゃないですから。もし配列まるごと値セットしたいなら、下のような記述になります。

    void syori(int data, int *buf1 , int *buf2)
    {
            switch (data)
            {
            case 1:
        {
                      static const int data_table[] = {NUM1,NUM2,NUM3,NUM4,NUM5,NUM6,NUM7,NUM8,NUM9};
                      memcpy(buf1, data_table, sizeof(data_table));
                      memcpy(buf2, data_table, sizeof(data_table));
                  }
                    break;
           
       /*~その他case文~*/
            default:
                    break;
            }
    }

  • マクロはC言語処理系ではCコンパイラの前処理ソフトで処理されます。ファイルで一度定義されるとファイル終端まで#undefで無効化されるまで有効です。#include <ファイル名>をよく使うと思いますが、これはソースコードに#includeで指定したファイルを文字通り含んだ状態にしてしまいます。やろうと思えば#includeで.cファイルを指定しても問題ないわけです。

  • わわいです

    defineの定義やらスコープとは全く関係ありません。

    buf1[NUM_BYTE] = {NUM1,NUM2,NUM3,NUM4,NUM5,NUM6,NUM7,NUM8,NUM9};

    > buf2[NUM_BYTE] = {NUM1,NUM2,NUM3,NUM4,NUM5,NUM6,NUM7,NUM8,NUM9};
    これらの行の記述がC言語として不当なもの、なのでそのエラーが出ているだけですね
  • Yamamoto様

    ご回答ありがとうございます。そもそも要素の指定ができていなかったですね。
    forループで配列をコピーするやり方に変更し、配列がコピーされているのは確認できましたが、やはり配列の代入方法が間違っていたみたいです。ご指摘の通り要素数の指定も間違いですね。
    また、case1文の中身を{}でスコープしていることについては、switch文の理解不足でした。{}でくくらなければswitch文全体にスコープされてしまいますね。
    「static const int data_table[]」とする書き方は大変勉強になりました。この書き方だと不用意に書き換えられることも防げるのでまねさせていただきます。memcpyを使うとforループでの代入に比べて、コード量が減りますね。自分のforループで代入するやり方はコード量が多く見づらいので、参考にさせていただきます。
    実際にプログラムを動かし、配列に格納されていることが確認できました。ありがとうございました。
    #defineについて今一度理解しました。

  • わわい様

    規格に沿っていないだけでした。(初心者にはこんな程度でも「だけ」ではない大事になってしまします..。)

  • わわいです

    んで、スコープに関して、ですが、中括弧 { } が重要となります

    いくつかの例外はありますが、変数を定義した場合、その変数は、定義した行以降、定義した中括弧の中、のみが有効となります。

    グローバル変数なんかは、すべてのカッコの外側で定義されるために、(そのソースの)全体がスコープとなります

  • わわい様

    今回特にswitch文の中でのスコープの仕方について問題がありました。
    エラーが起きていた原因としては、書き方の問題がそもそもありましたが、Yamamoto様の回答をお借りすると、「static const int data_table[]」は{}スコープの中にあることで、スコープされている変数はコンパイルされるけれども、スコープされていない場合には対象外になってしまうからでしょうか。switch文の外で定義した変数(例えば int = a; )をcase文の中で使用する場合(  a = 1;)には{}が必要ないですよね。
    別の書き方としては、

    switch(data){

      int a;

    case1:

          a = 1;

        break;

    }

    が許されるので、配列についても使用する配列を共通化する場合には、

    void syori(int data, int *buf1 , int *buf2)
    {
            switch (data)
            {
                    int data_table[NUM_BYTE];  
            case 1:
           
                     data_table[0] = NUM1;
                     data_table[1] = NUM2;
                     data_table[2] = NUM3;
                     data_table[3] = NUM4;
                     data_table[4] = NUM5;
                     data_table[5] = NUM6;
                     data_table[6] = NUM7;
                     data_table[7] = NUM8;
                     data_table[8] = NUM9;
                   
                    //data_table[] = {NUM1,NUM2,NUM3,NUM4,NUM5,NUM6,NUM7,NUM8,NUM9};
                    memcpy(buf1, data_table, sizeof(data_table));
                    memcpy(buf2, data_table, sizeof(data_table));

                    break;
           
            default:
                    break;
            }

    }

    と書くことで、関数が実行されるたびに場合分けで配列の中身がその都度コピーされるようになりますね。

    こう見ると、使用する配列を共通化する/しない が判断基準でしょうか。

  • わわいです

    その書き方だと、switch文を抜けるとdata_tableは消滅してしまいますが、そんでいいのん?ってはなしになりますが。

    で、それでいいなら、その配列の定義はcase 1 の直後に持ってくる方がわかりやすいですね。

    まあしかし、そういう用途なら、ローカル変数とせずに、const付きのグローバル変数にしてしまうのが一般的だと思います。

  • わわい様

    現在の用途的にはdata_tableはbuf1、buf2にコピーしているので、消えても問題ないかなとは思っています。変わらない値であれば、const付のグローバル変数で初期化してあるものを使うのが確かに良いですね。大変勉強になりました。ありがとうございます。

  • わわいです

    んで、ここでなんでconst付きのグローバル変数がいいか、ってのを説明しときます

    この手のマイコン用のC言語の処理系では(もちろん例外もありますが)

    const付きのグローバル変数にしとけば、それは定数とみなされてROM領域にそのデータが展開されます。

    こうなると、この変数の使用に際し、スタックは消費されませんし、コードの実行時のコストにはなりません

    ローカル変数だと、その初期化に実行時間がかかってしまうことになります