解決のための"dereferencing`void*'のポインタ"で警告を発する構造体に。
-
08-07-2019 - |
質問
ったので作り出してしまおうというものを疑似超構造体への印刷配列の構造体.私の基本的な 構造は以下のとおり。
/* Type 10 Count */
typedef struct _T10CNT
{
int _cnt[20];
} T10CNT;
...
/* Type 20 Count */
typedef struct _T20CNT
{
long _cnt[20];
} T20CNT;
...
作成した以下の構造体への印刷の配列は、上記構造です。私dereferencing voidポインタがエラーを以下のコードスニペット.
typedef struct _CMNCNT
{
long _cnt[3];
} CMNCNT;
static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
int ii;
for(ii=0; ii<cmncnt_nelem; ii++)
{
CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];
fprintf(stout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
fprintf(stout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]);
fprintf(stout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
}
return SUCCESS;
}
T10CNT struct_array[10];
...
printCommonStatistics(struct_array, NELEM(struct_array), sizeof(struct_array[0]);
...
それについて共通の機能をすべて印刷をarrays.みなさん、そして、正しい方向での使用しています。
感謝します。
編集:パラメータ名変更cmncntinからcmncnt.また商品につけられたタエラーとなります。
おかげさ Mathew Liju
解決
行きたいというニーズがデザイン格者による電気工事が必要かもしれませんが、同じもunconvincedのその他の回答を見完全に、より深い理由はいかがでしょうか。
れようとしているのに使用Cへの対応汎用種類あるということは、常に取得する毛.ができない場合はご注意ですが、簡単なことではありません、この場合、私のだろうかという疑問を抱かない内容であることを期待する。
より深く理由:が一致していると仮定すると、取得し、過去の単なる統語(またはほとんどによ統語)。コードがT10CNTを含む20 int
とT20CNTを含む20 long
.現代の64ビットの機械-以下Win64- sizeof(long) != sizeof(int)
.そのため、コード内の印刷機能の区別をすることdereferencing int
配列および long
arrays.C++であり原則すべきでない"という処理の配列polymorphically、このようなことはいかがでしょうか。のCMNCNTタイプ3 long
値;異なるからT10CNTとT20CNT構造番号、その基底型の配列に一致すT20CNT.
スタイルの推薦:私は強くお勧めしを避ける主力に関す。一般的に、同じ名前とアンダースコア予約実施のために利用してマクロです。マクロってまったく尊重していない対象範囲この実装を定義するマクロ_cntでの難破船をごのコードです。がニュアンスが何をされている必要があり;いつくることがそのニュアンスを出すためにもが簡単になりう名前からアンダースコア予約できます劇場、ボリショイ劇場が明確に不具合が発生します。
スタイルの提案:印刷機能を返します成功の条件.な顕;ご関数は何も返さないよう、呼び出し側でなくて試験が成功または失敗(かすが失敗することはありません).なメリカ-サンフランシスコの方には、この機能を返しま状況は常にテストの状態を返し、エラー取り扱います。このコードのない実行することで、ものするものではなくどなたでもやコンパイラを決定します。
表面固定:一時的であると推測できますので int
や long
さらに同義語として;なばなければなりませんの考える癖がその同義語です。の void *
引数が正しい方法"ということをこの関数はポインタの不確定タイプ"。しかし、内部の機能は、変換する必要があるから void *
特定のタイプる前にごい数の
typedef struct _CMNCNT
{
long count[3];
} CMNCNT;
static void printCommonStatistics(const void *data, size_t nelem, size_t elemsize)
{
int i;
for (i = 0; i < nelem; i++)
{
const CMNCNT *cmncnt = (const CMNCNT *)((const char *)data + (i * elemsize));
fprintf(stdout,"STATISTICS_INP: %ld\n", cmncnt->count[0]);
fprintf(stdout,"STATISTICS_OUT: %ld\n", cmncnt->count[1]);
fprintf(stdout,"STATISTICS_ERR: %ld\n", cmncnt->count[2]);
}
}
(できればおもしろいと思い、ファイルのストリームと呼ばれ stout
ます。 の提案:使用カットパ'pasteのリソースコードで安心!私は一般的に使用"sed 's/^/ /' file.c
"準備コードカットパ'paste、その答えです。)
かな鋳造ラインかがですか?嬉しいお求---
- 最初の動作に変換するという事になりますの
const void *
入const char *
;を作成することはできないバイトサイズのアドレスです。日本の標準Cchar *
使用された場void *
としてのユニバーサルに対応。 - 次の操作を追加し、正しいバイト数を開始
i
th要素のオブジェクトの配列のサイズelemsize
. - 第二キャストをこのコンパイラの"信頼というよろしかったので、良かった"、"このアドレスのアドレスのCMNCNT構造"
からあり、コードは簡単です。年CMNCNT構造を含む long
値を使って %ld
真偽の見極めを fprintf()
.
てん約を変更データ本機能は悪くないの const
予選ます。
ることがありますのでご注意くだんに忠実である sizeof(long) != sizeof(int)
, ば二つの別々のブロックのコードから別の機能にお'の配列 int
'と'の配列 long
'構造です。
他のヒント
voidのタイプは意図的に不完全なままです。このことから、voidポインターを逆参照することはできず、そのサイズを取得することもできません。つまり、添字演算子は配列のように使用することはできません。
voidポインターに何かを割り当てると、型を指す元の型情報は失われるため、最初に元のポインター型にキャストした場合にのみ逆参照できます。
まず最も重要なことは、 T10CNT *
を関数に渡しますが、関数の CMNCNT *
に型キャスト(および参照解除)しようとすることです。これは有効ではなく、未定義の動作です。
配列要素のタイプごとに関数printCommonStatisticsが必要です。だから、持っている
printCommonStatisticsInt
、 printCommonStatisticsLong
、 printCommonStatisticsChar
。これらはすべて最初の引数によって異なります(一方は int *
を、もう一方は< code> long * など)。冗長なコードを避けるために、マクロを使用してそれらを作成できます。
構造体自体を渡すことはお勧めできません。なぜなら、構造体に含まれる配列のサイズごとに新しい関数を定義する必要があるからです(すべて異なる型であるため)。そのため、含まれている配列を直接渡した方がよい( struct_array [0] ._ cnt
、各インデックスに対して関数を呼び出す)
次のように関数宣言をchar *に変更します。
static int printCommonStatistics(char *cmncnt, int cmncnt_nelem, int cmncnt_elmsize)
void型は特定のサイズを想定しませんが、charはバイトサイズを想定します。
これはできません:
cmncnt->_cnt[0]
cmnctがvoidポインターの場合。
タイプを指定する必要があります。実装を再考する必要があるかもしれません。
関数
static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
char *cmncntinBytes;
int ii;
cmncntinBytes = (char *) cmncntin;
for(ii=0; ii<cmncnt_nelem; ii++)
{
CMNCNT *cmncnt = (CMNCNT *)(cmncntinBytes + ii*cmncnt_elmsize); /* Ptr Line */
fprintf(stdout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
fprintf(stdout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]);
fprintf(stdout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
}
return SUCCESS;
}
私のために働く。
問題は、「Ptr Line」とコメントされた行にあることです。コードは整数へのポインターを追加します。ポインターはchar *であるため、メモリsizeof(char)* ii * cmncnt_elemsizeに進みます。これは、charが1バイトであるためです。あなたのコードは、sizeof(void)* ii * cmncnt_elemsizeと同じことをしようとしましたが、voidにはサイズがないため、コンパイラーがエラーを返しました。
T10CNTとT20CNTを変更して、どちらもそれぞれ1つではなくintまたはlongを使用するようにしました。 sizeof(int)== sizeof(long)に依存しています
この行:
CMNCNT *cmncnt = (CMNCNT *)&cmncnt[ii*cmncnt_elmsize];
cmncntと呼ばれる新しい変数を宣言しようとしていますが、この名前の変数は関数のパラメーターとして既に存在しています。これを解決するには、別の変数名を使用することをお勧めします。
また、コンパイラはvoidポインタの代わりにCMNCNTへのポインタを関数に渡すこともできます。これは、コンパイラがポインタ演算を行うため、キャストする必要がないためです。 voidポインターを渡すのは、それをCMNCNTにキャストするだけでは渡せないという点がわかりません。 (ちなみに、これはデータ型のあまり説明的な名前ではありません。)
あなたの表現
(CMNCNT *)&cmncntin[ii*cmncnt_elmsize]
cmncntin [ii * cmncnt_elmsize]のアドレスを取得し、そのポインターを型(CMNCNT *)にキャストします。 cmncntinのタイプはvoid *であるため、cmncntin [ii * cmncnt_elmsize]のアドレスを取得できません。
Cの演算子の優先順位を調べ、必要に応じて括弧を挿入します。
情報のポイント:内部パディングは本当にこれを台無しにする可能性があります。
構造体を考慮する{char c [6]; }; -sizeof()= 6です。ただし、これらの配列がある場合は、各要素が8バイトの配置になるまでパディングされる可能性があります!
特定のアセンブリ操作は、不整合データを適切に処理しません。 (たとえば、intが2つのメモリワードにまたがる場合。)(はい、以前にこれに噛まれたことがあります。)
。
2番目:過去に、可変サイズの配列を使用しました。 (私は当時愚かだった...)あなたがタイプを変更していない場合、それは動作します。 (または、型の結合がある場合。)
例:
struct T { int sizeOfArray; int data[1]; };
割り当て済み
T * t = (T *) malloc( sizeof(T) + sizeof(int)*(NUMBER-1) );
t->sizeOfArray = NUMBER;
(パディング/アラインメントはまだあなたを台無しにすることができます。)
。
第三:考慮:
struct T {
int sizeOfArray;
enum FOO arrayType;
union U { short s; int i; long l; float f; double d; } data [1];
};
データの印刷方法を知ることで問題を解決します。
。
4番目:構造ではなくint / long配列を関数に渡すだけです。例:
void printCommonStatistics( int * data, int count )
{
for( int i=0; i<count; i++ )
cout << "FOO: " << data[i] << endl;
}
経由で呼び出し:
_T10CNT foo;
printCommonStatistics( foo._cnt, 20 );
または:
int a[10], b[20], c[30];
printCommonStatistics( a, 10 );
printCommonStatistics( b, 20 );
printCommonStatistics( c, 30 );
これは、構造体でデータを非表示にするよりもはるかに効果的です。構造体の1つにメンバーを追加すると、構造体間でレイアウトが変更され、一貫性がなくなる場合があります。 (構造体の開始に対する_cntのアドレスの意味は、_T20CNTではなく_T10CNTで変更される場合があります。デバッグ時間は楽しいです。結合された_cntペイロードを持つ単一の構造体はこれを回避します。)
例:
struct FOO {
union {
int bar [10];
long biff [20];
} u;
}
。
5番目: 構造体を使用する必要がある場合... C ++、iostream、およびテンプレートは、実装するのがはるかに簡単になります。
例:
template<class TYPE> void printCommonStatistics( TYPE & mystruct, int count )
{
for( int i=0; i<count; i++ )
cout << "FOO: " << mystruct._cnt[i] << endl;
} /* Assumes all mystruct's have a "_cnt" member. */
しかし、それはおそらくあなたが探しているものではありません...
Cは私のカップo'javaではありませんが、あなたの問題は&quot; void * cmncnt&quot; CMNCNT * cmncntである必要があります。
Cプログラマーの皆さん、今すぐ修正してください。これが、Javaプログラマーが素晴らしいものを手に入れることができない理由だと教えてください。
この行は拷問のようなものです、思いませんか?
CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];
もっと似たようなものはどうですか
CMNCNT *cmncnt = ((CMNCNT *)(cmncntin + (ii * cmncnt_elmsize));
さらに良いのは、cmncnt_elmsize = sizeof(CMNCNT)の場合
CMNCNT *cmncnt = ((CMNCNT *)cmncntin) + ii;
これは、void *を間接参照しなくなったため、警告も取り除く必要があります。
BTW:なぜこのようにしているかはわかりませんが、cmncnt_elmsizeがsizeof(CMNCNT)ではなく、実際に呼び出しごとに異なる場合は、この設計を再考することをお勧めします。それには十分な理由があるのではないかと思いますが、それは私には本当に不安定に見えます。物事を設計するより良い方法があることはほぼ保証できます。