0

私は64時間目にバッファリングライブラリを作成しようとしています。かなり高度なものに取り掛かります。私はこれに関するいくつかのproffesional入力を求めるだろうと思った。関数ポインタをキャストする効果は何ですか?

typedef struct CharBuffer { char* address; } CharBuffer; 
void (*CharBufferClear)(CharBuffer) = (void*) StdBufferClear; 

は、この関数ポインタボイドを妨げる宣言するだろう:私はこれを持っている最初のヘッダファイルを#includes別のヘッダファイルで

typedef struct StdBuffer { void* address; } StdBuffer; 
extern void StdBufferClear(StdBuffer); 

:私の最初のヘッダファイルで

私はこれを持っていますコール?彼らは価値の署名で一致しています。私は関数ポインタが以前は無効と宣言されたことはありませんでしたが、それをきれいにコンパイルする唯一の方法です。

Stackwiseこれはアセンブラコーディングで学んだこととはまったく異なるものではありません。

irrelevent OMG!私はちょうどStackOverflowでStackwiseを言った!

Hmm ..私はここであまりにも多くを仮定したように見えます。もし私が可能なら再確認することができます。私は、データの「タイプ」がそのアドレスに格納されているかどうかは気にしません。私が懸念しているのは、「ユニット」のサイズとそのアドレスにあるユニットの数です。 、あなたが見

typedef struct StdBuffer { 

    size_t width;  ///< The number of bytes that complete a data unit. 
    size_t limit;  ///< The maximum number of data units that can be allocated for this buffer. 
    void * address;  ///< The memory address for this buffer. 
    size_t index;  ///< The current unit position indicator. 
    size_t allocated; ///< The current number of allocated addressable units. 
    StdBufferFlags flags;///< The API contract for this buffer. 

} StdBuffer; 

をアドレスに、彼らが望むすべてが私は具体的でいただきました!memcpyを、MEMMOVEなどは本当に気にしない:あなたがする場合は、APIのインターフェース契約の契約を見てみましょう明らかにここを追跡しています。あなたはっきりデータ型はこのコンテキストでは関係ありません見たよう

typedef struct CharBuffer { 

    size_t width;  ///< The number of bytes that complete a data unit. 
    size_t limit;  ///< The maximum number of data units that can be allocated for this buffer. 
    char * address;  ///< The memory address for this buffer. 
    size_t index;  ///< The current unit position indicator. 
    size_t allocated; ///< The current number of allocated addressable units. 
    CharBufferFlags flags;///< The API contract for this buffer. 

} CharBuffer; 

は、この契約に従うことを最初のプロトタイプで、今見てください。場合によってはCが異なる方法で処理すると言うこともできますが、終わりには addressは、 bytebytelonglongです。同じマシン上のメモリを扱っている限り。

このシステムの目的は、このタイプのすべてのジャグリングを削除することです。ジャグリングCはとても誇りに思っています(そして正当な理由で...)。私がしたいことはまさに無意味です。任意のアドレスにある標準サイズのデータ​​(1、2、4、8、sizeof(RandomStruct))のプロトタイプを作成します。

コードを使用して独自のキャストを実行し、特定の長さのメモリ単位でメモリの特定の長さのブロックで動作するAPI関数を使用してデータを操作できます。ただし、プロトタイプには公式のデータポインタ型が含まれている必要があります。エンドポインターで何か処理を行うたびに、エンドユーザーがデータを再作成する必要がないからです。ポインタが無効な場合、それをCharBufferと呼ぶのは意味がありません。

StdBufferは、すべての契約に準拠したデータ型を管理するために、API自体以外では決して使用されない汎用タイプです。

このシステムに組み込むapiは、私の最新のバッファリング版です。ここではっきりと文書化されています@Google Code私は、これをすべて一緒にするためにいくつかのことを変える必要があることを知っています。つまり、適切な研究や意見収集をしなくても、API内から直接データを操作する能力はありません。

StdBufferFlagsメンバーにSigned/Unsignedビットフラグが必要であることに注目したばかりです。

おそらく、このパズルの最後の部分はあなたの熟読のためです。

/** \def BIT(I) 
    \brief A macro for setting a single constant bit. 
* 
* This macro sets the bit indicated by I to enabled. 
* \param I the (1-based) index of the desired bit to set. 
*/ 
#define BIT(I) (1UL << (I - 1)) 

/** \enum StdBufferFlags 
    \brief Flags that may be applied to all StdBuffer structures. 

* These flags determine the contract of operations between the caller 
* and the StdBuffer API for working with data. Bits 1-4 are for the 
* API control functions. All other bits are undefined/don't care bits. 
* 
* If your application would like to use the don't care bits, it would 
* be smart not to use bits 5-8, as these may become used by the API 
* in future revisions of the software. 

*/ 
typedef enum StdBufferFlags { 

    BUFFER_MALLOCD = BIT(1), ///< The memory address specified by this buffer was allocated by an API 
    BUFFER_WRITEABLE = BIT(2), ///< Permission to modify buffer contents using the API 
    BUFFER_READABLE = BIT(3), ///< Permission to retrieve buffer contents using the API 
    BUFFER_MOVABLE = BIT(4)  ///< Permission to resize or otherwise relocate buffer contents using the API 

}StdBufferFlags; 

答えて

3

このコードは、診断が必要です。

void (*CharBufferClear)(CharBuffer) = (void*) StdBufferClear; 

あなたはキャストせずに関数ポインタにvoid *ポインタを変換しています。 Cでは、void *ポインタは、キャストのないオブジェクト型へのポインタに変換できますが、ポインタ型は機能しません。あなたがここに欲しい(C++では、キャストが追加安全のために、また、オブジェクト型にvoid *を変換するために必要とされる)

は単なる関数ポインタ型、すなわち間にキャストすることです:

void (*CharBufferClear)(CharBuffer) = (void (*)(CharBuffer)) StdBufferClear; 

次に、あなたがしています関数は異なる型であるため、同じ型のpunningをやっています。あなたは、CharBufferを取る関数へのポインタを使ってStdBufferを取る関数を呼び出そうとしています。

このタイプのコードはよく定義されていません。C.型システムを打ち負かしてしまった場合は、テスト、オブジェクトコードの検証、またはコンパイラライターからの何らかの保証の取得が必要ですそのコンパイラで動作します。

アセンブリ言語では、「マシンアドレス」や「32ビットワード」などの基本データ型が少ないため、アセンブラコーディングで学んだことは適用されません。同じレイアウトと低レベルの表現を持つ2つのデータ構造が互換性のない型であるという概念は、アセンブリ言語では発生しません。

Cコンパイラは、タイプ規則が違反していないという前提に基づいてプログラムを最適化することができます(別の例では、unsigned intunsigned longはまったく同じです)。例えば、ABが同じメモリ位置を指しているとします。 A->memberオブジェクトに代入すると、A->memberB->memberに互換性のない型(char *とその他のvoid *など)がある場合、CコンパイラはオブジェクトB->memberがこの影響を受けないと見なすことができます。生成されたコードは、メモリ内のコピーがA->memberへの代入によって上書きされたとしても、古い値のB->memberをレジスタにキャッシュし続けます。これは無効なエイリアシングの例です。

+0

ok最悪の場合のシナリオ:この '静的ライブラリ'のみの最適化を無効にします。その上の任意の入力? –

+1

「最適化を無効にする」よりも細かい外科用器具があるかもしれません。例えば、GCCには厳密なエイリアシングに基づいて最適化を無効にするオプションがあります: '-fno-strict-aliasing'。 – Kaz

+0

今どこかに行ってきました!まあ、少なくともそれはいいと思う。 –

-1

いいえ、データの格納に使用するデータタイプは問われません。 C型がそのデータを読み書きするために使用する型だけであり、データのサイズは十分です。

+3

多分、この場合は未定義の動作です。 –

1

標準では、ファンクションポインタをvoid *にキャストした結果は定義されていません。

同様に、関数ポインタ間を変換し、間違ったものを呼び出すと、未定義の動作になります。

+0

これは、ANSI C互換機を越えて複製するならば、私が利用しようとしているバグです。 –

+0

書くことから私を救うコードの量を考えるだけで、 "netherworld"からの誘惑のようなものです。 –

+0

@ TristonJ.Taylor:これを行う必要がある場合は、高レベルの問題が何であれ、根本的により良い解決策がある可能性が高いです。 –

0

標準準拠のCコンパイラが一貫して実装する必要があり、Cコンパイラの99%が一貫して実装するコンストラクトがいくつかありますが、標準準拠のコンパイラは自由に実装できます。 1つのタイプのポインタを取る関数へのポインタを別のタイプのポインタを取る関数へのポインタにキャストしようとすると、後者のカテゴリに入ります。 C標準ではとchar*は同じサイズでなければならないと指定していますが、同じビットレベルの記憶形式を共有する必要はありません。ほとんどのマシンでは、単語とほとんど同じ方法でバイトにアクセスできますが、そのような能力は普遍的なものではありません。アプリケーションバイナリインタフェースの設計者(特に、パラメータがルーチンに渡される方法を指定するドキュメント)は、void*がバイトアクセスの効率を最大化する方法でchar*が渡されることを指定することがあります。 LSB/MSBを示すために補助ワードを使用してゼロまたは1を保持することによって、アライメントされていないバイトアドレスを保持する能力を保持しながら、ワードアクセスの効率を最大化する。そのようなマシンでは、char*を渡すと予想されるコードから呼び出されたを期待するルーチンを持つと、ルーチンが任意の間違ったデータにアクセスする可能性があります。

関連する問題