Visual C++ ネイティブかつ、C言語の構造体に関する質問です。

未知 40 評価のポイント
2026-05-08T21:12:57.51+00:00

皆様、お疲れ様です。C++から始めたので、Cのプログラムには疎いです。
後学の為、png ライブラリの 1.6.55 を見ているのですが、理解出来ない部分があって困っています。

以下、色々簡略化した疑似コードです。

static const struct
{
	png_handle_result_code	(*handler)(png_structrp, png_inforp, png_uint_32 length);
	png_uint_32		max_length : 12;	// Length min, max in bytes
}
read_chunks[PNG_INDEX_unknown] =
{
#define NoCheck	0x801U			// Do not check the maximum length

#define CDIDAT  NoCheck,    0U,		aIDAT,		hIHDR,  1
#define CDIEND  NoCheck,    0U,			0,		aIDAT,  0

#define PNG_CHUNK(cHNK, index) { png_handle_ ## cHNK, CD ## cHNK }, PNG_KNOWN_CHUNKS
#undef PNG_CHUNK
};

この様なプログラムをコンパイルすると、C2059 構文エラー : '};' となります。私には PNG_INDEX_unknown 数のread_chunks という構造体配列を作成、初期化を行っている様に思えるのですが、構造体の宣言と実際に使う構造体の実変数名(間違っていたらすいません)を定義しようとしている。或いはそれが出来る意味が解らないのです。
無論、構文エラーとなる意味も分かりません。

本来であれば、

static const typedef struct
{
	png_handle_result_code	(*handler)(png_structrp, png_inforp, png_uint_32 length);
	png_uint_32		max_length : 12;	// Length min, max in bytes}read_chunks[PNG_INDEX
} READ_CHUNKS, *READ_CHUNKS;

とでも宣言して、

READ_CHUNKS		read_chunks[PNG_INDEX_unknown]
for (int i = 0; i < PNG_INDEX_unknown; i++)
{
	read_chunks[i]	=
	{
		#define NoCheck	0x801U			// Do not check the maximum length

		#define CDIDAT  NoCheck,    0U,		aIDAT,		hIHDR,  1
		#define CDIEND  NoCheck,    0U,			0,		aIDAT,  0

		#define PNG_CHUNK(cHNK, index) { png_handle_ ## cHNK, CD ## cHNK }, PNG_KNOWN_CHUNKS
	}
}
#undef PNG_CHUNK

とでもするのではないかと思うのです(png_handle_ は順に PNG_INDEX_hIHDR、PNG_INDEX_aIDAT というように
#define をする部分の様)。

そこで、皆様にお尋ねしたいのは、このエラーの解消方法と、C言語では構造体宣言と、構造体の実体を同時に行う事が出来るのか。同時にこのコードはコンパイラの隙を付いたものなのか、正しい手法なのか、という事です。

確か、C言語環境下では構造体に関数を持たせる事は出来なかったはずなのに、最初のコードサンプルにある

png_handle_result_code	(*handler)(png_structrp, png_inforp, png_uint_32 length);

この部分も分かりません。私には構造体の関数に見えるのですが。

質問の為、かなり抜粋したコードになってしまいましたが、この理由と解決方法をご存じの方、よろしくお願い致します。

念の為、付け加えておきますが、コードの先頭と末尾には #ifdef __cplusplus extern "C" などは付けています。

なお、当然ながらGPLライセンスを逸脱するつもりはありません。

開発者テクノロジ | C++
開発者テクノロジ | C++

C プログラミング言語の拡張機能として作成された高レベルの汎用プログラミング言語。低レベルのメモリ操作機能に加えて、オブジェクト指向、汎用、関数型の機能を備えています。

0 件のコメント コメントはありません

質問作成者が受け入れた回答
gekka 14,551 評価のポイント MVP ボランティア モデレーター
2026-05-09T09:45:37.3333333+00:00

C2059は、Visual Studioを使っているのであれば「メニュー->表示->エラー一覧」で表示されるエラーウィンドウで、そのエラーをダブルクリックするとエラーの場所に飛べるので、括弧の組み合わせが一致しているかを確認してください。

#defineで定義されているマクロがあると、マクロに括弧やカンマなどの区切りがあってわかりにくなる場合があります。
その場合には#includeや#defineのない新しい.cファイルを作ってコピーして確認すると、そのコード部分で組み合わせがおかしいのか、マクロでおかしいくなっているのかが調べられるかもしれません。

このエラーは自分で間違った箇所を探すしかありません。


構造体の宣言と同時に初期化するには、初期化子リストという方法があります。

libpngのコードがマクロ展開をたくさん使っているのでわかりにくいですが、以下のように順番に解釈していけばわかります。

/// pngpriv.h
#define PNG_KNOWN_CHUNKS\
   PNG_CHUNK(IHDR,  0)\
   PNG_CHUNK(PLTE,  1)\
   PNG_CHUNK(IDAT,  2)\
   PNG_CHUNK(IEND,  3)\
   PNG_CHUNK(acTL,  4)\
   PNG_CHUNK(bKGD,  5)\
   PNG_CHUNK(cHRM,  6)\
   PNG_CHUNK(cICP,  7)\
   PNG_CHUNK(cLLI,  8)\
   PNG_CHUNK(eXIf,  9)\
   PNG_CHUNK(fcTL, 10)\
   PNG_CHUNK(fdAT, 11)\
   PNG_CHUNK(gAMA, 12)\
   PNG_CHUNK(hIST, 13)\
   PNG_CHUNK(iCCP, 14)\
   PNG_CHUNK(iTXt, 15)\
   PNG_CHUNK(mDCV, 16)\
   PNG_CHUNK(oFFs, 17)\
   PNG_CHUNK(pCAL, 18)\
   PNG_CHUNK(pHYs, 19)\
   PNG_CHUNK(sBIT, 20)\
   PNG_CHUNK(sCAL, 21)\
   PNG_CHUNK(sPLT, 22)\
   PNG_CHUNK(sRGB, 23)\
   PNG_CHUNK(tEXt, 24)\
   PNG_CHUNK(tIME, 25)\
   PNG_CHUNK(tRNS, 26)\
   PNG_CHUNK(zTXt, 27)


/// pngstruct.h
#define PNG_CHUNK(cHNK, i) PNG_INDEX_ ## cHNK = (i), //これはPNG_KNOWN_CHUNKSの各行をenumの列挙子にするマクロ
typedef enum
{
	PNG_KNOWN_CHUNKS  // ここではPNG_KNOWN_CHUNKSをenumの項目に展開している
	PNG_INDEX_unknown // この値は PNG_KNOWN_CHUNKSの最後の値+1。 PNG_KNOWN_CHUNKSの最後に27が定義されているので、その次の28。
} png_index;

/*
まずPNG_KNOWN_CHUNKSが展開され
typedef enum
{
	PNG_CHUNK(IHDR,  0),
	PNG_CHUNK(PLTE,  1)\
	  省略
	PNG_CHUNK(zTXt, 27),
	PNG_INDEX_unknown
} png_index;

のようになる。
さらに、展開されたPNG_CHUNKに上のマクロが適用されて

typedef enum
{
	PNG_INDEX_IHDR = (0),
	  省略
	NG_INDEX_zTXt = (27),
	PNG_INDEX_unknown
} png_index;

という列挙型になります
*/


#ifndef PNG_RESTRICT
#define  PNG_RESTRICT
#endif
typedef enum
{
	handled_error = 0,
	handled_discarded,
	handled_saved,
	handled_ok
} png_handle_result_code;


//---------------------------
typedef unsigned int png_uint_32;
typedef struct png_struct_def png_struct;
typedef png_struct* PNG_RESTRICT png_structrp;
typedef const png_struct* PNG_RESTRICT png_const_structrp;
typedef struct png_info_def png_info;
typedef png_info* PNG_RESTRICT png_inforp;
typedef const png_info* PNG_RESTRICT png_const_inforp;

#define PNG_HAVE_IHDR  0x01
#define PNG_HAVE_PLTE  0x02
#define PNG_AFTER_IDAT 0x08
#define PNG_HAVE_IDAT  0x04U
#define LZ77Min  (2U+5U+4U)

//テストコンパイル用偽関数定義生成
#define PNG_CHUNK(cHNK, i) png_handle_result_code png_handle_ ## cHNK (png_structrp a, png_inforp b, png_uint_32 length) {return handled_ok;} 
PNG_KNOWN_CHUNKS

//---------------------------

static const struct
{
	png_handle_result_code(*handler)(png_structrp, png_inforp, png_uint_32 length);
	png_uint_32 max_length : 12;
	png_uint_32 min_length : 8;
	png_uint_32 pos_before : 4;
	png_uint_32 pos_after : 4;
	png_uint_32 multiple : 1;
}
//これは直前の構造体の型の配列
read_chunks[PNG_INDEX_unknown] =
{
	//この下の#defineはPNG_KNOWN_CHUNKの項目のマクロ展開で使うパラメーター
#define  NoCheck 0x801U
#define  Limit   0x802U
#define  LKMin   3U+LZ77Min
#define hIHDR PNG_HAVE_IHDR
#define hPLTE PNG_HAVE_PLTE
#define hIDAT PNG_HAVE_IDAT
#define hCOL  (PNG_HAVE_PLTE|PNG_HAVE_IDAT)
#define aIDAT PNG_AFTER_IDAT
#define  CDIHDR      13U,   13U,  hIHDR,     0,        0
#define  CDPLTE  NoCheck,    0U,      0, hIHDR,        1
#define  CDIDAT  NoCheck,    0U,  aIDAT, hIHDR,        1
#define  CDIEND  NoCheck,    0U,      0, aIDAT,        0
#define  CDtRNS     256U,    0U,  hIDAT, hIHDR,        0
#define  CDcHRM      32U,   32U,   hCOL, hIHDR,        0
#define  CDgAMA       4U,    4U,   hCOL, hIHDR,        0
#define  CDiCCP  NoCheck, LKMin,   hCOL, hIHDR,        0
#define  CDsBIT       4U,    1U,   hCOL, hIHDR,        0
#define  CDsRGB       1U,    1U,   hCOL, hIHDR,        0
#define  CDcICP       4U,    4U,   hCOL, hIHDR,        0
#define  CDmDCV      24U,   24U,   hCOL, hIHDR,        0
#define  CDeXIf    Limit,    4U,      0, hIHDR,        0
#define  CDcLLI       8U,    8U,   hCOL, hIHDR,        0
#define  CDtEXt  NoCheck,    2U,      0, hIHDR,        1
#define  CDzTXt    Limit, LKMin,      0, hIHDR,        1
#define  CDiTXt  NoCheck,    6U,      0, hIHDR,        1
#define  CDbKGD       6U,    1U,  hIDAT, hIHDR,        0
#define  CDhIST    1024U,    0U,  hPLTE, hIHDR,        0
#define  CDpHYs       9U,    9U,  hIDAT, hIHDR,        0
#define  CDsPLT  NoCheck,    3U,  hIDAT, hIHDR,        1
#define  CDtIME       7U,    7U,      0, hIHDR,        0
#define  CDacTL       8U,    8U,  hIDAT, hIHDR,        0
#define  CDfcTL      25U,   26U,      0, hIHDR,        1
#define  CDfdAT    Limit,    4U,  hIDAT, hIHDR,        1
#define  CDoFFs       9U,    9U,  hIDAT, hIHDR,        0
#define  CDpCAL  NoCheck,   14U,  hIDAT, hIHDR,        0
#define  CDsCAL    Limit,    4U,  hIDAT, hIHDR,        0


#define  PNG_CHUNK(cHNK, index) { png_handle_ ## cHNK, CD ## cHNK }, //これは PNG_KNOWN_CHUNK を構造体の初期化子に展開するマクロ
//#define  PNG_CHUNK(cHNK, index) { png_handle, CD ## cHNK }, //これは PNG_KNOWN_CHUNK を構造体の初期化子に展開するマクロ
	PNG_KNOWN_CHUNKS //ここではPNG_KNOWN_CHUNKの各項目が、この上のPNG_CHUNKマクロによって、それぞれ構造体に展開される
#undef PNG_CHUNK

};


/*

つまり
  PNG_CHUNK(IHDR,  0)\
  PNG_CHUNK(PLTE,  1)\

のような項目が

  { png_handle_IHDR, CDIHDR} ,
  { png_handle_PLTE, CDPLTE} ,

と展開されて、さらに二つ目のCD*の部分もマクロ展開されることで

  { png_handle_IHDR,     13U,   13U,  hIHDR,     0,        0} , //6個のフィールドがある構造体の初期化子
  { png_handle_PLTE, NoCheck,    0U,      0, hIHDR,        1} ,

のように構造体の初期化子になります。

よって、最終的には

static const struct
{
	png_handle_result_code(*handler)(png_structrp, png_inforp, png_uint_32 length);
	png_uint_32 max_length : 12;
	png_uint_32 min_length : 8;
	png_uint_32 pos_before : 4;
	png_uint_32 pos_after : 4;
	png_uint_32 multiple : 1;
}
read_chunks[28] = {
	{ png_handle_IHDR,     13U,   13U,  hIHDR,     0,        0} ,
	{ png_handle_PLTE, NoCheck,    0U,      0, hIHDR,        1} ,
	 省略
	{ png_handle_zTXt,   Limit,    4U,  hIDAT, hIHDR,        0}
}

という構造体配列に対しての初期化子リストになります。

*/

unsigned int test() {
	return read_chunks[0].handler(0, 0, 0); //構造体に関数があるようにみえる
}

なお、Visual Studioであれば、マクロ展開される部分にマウスを持っていくと、マクロを展開した状態がポップアップされます

20260509_0 20260509_0


png_handle_result_code	(*handler)(png_structrp, png_inforp, png_uint_32 length);

これは「png_handle_result_code型を返す関数」への関数ポインタ型のフィールドですね。

  • フィールド名: handler
  • 関数の戻り値: png_handle_result_code型
  • 関数の第1引数: png_structrp型 で引数名は省略
  • 関数の第2引数: png_inforp型 で引数名は省略
  • 関数の第3引数: png_uint_32型 で引数名がlength

構造体のフィールドが関数ポインタ型であると、その関数ポインタは構造体に関数が定義されているように呼び出せます。 ただしC++のclassのメンバ関数のように暗黙のthisがないので自分自身を参照できるわけではないですね

# 20260510 0810 添付コードを単独でコンパイルできるように偽関数などを追加

この回答は役に立ちましたか?

2 人がこの回答が役に立ったと思いました。

0 件の追加の回答

並べ替え方法: 古い順

お客様の回答

質問作成者は回答に "承認済み"、モデレーターは "推奨" とマークできます。これにより、ユーザーは作成者の問題が回答によって解決したことを把握できます。