0

だから私は64回目のバッファリングライブラリを書き込もうとしていて、かなり高度なものに取り掛かっています。私はこれについていくつかの専門家の意見を求めたいと思いました。

私の最初のヘッダーファイルにはこれがあります:

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

私がこれを持っている最初のヘッダーファイルである別のヘッダーファイルで#includes

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

この関数ポインタをvoidと宣言すると、呼び出しが妨げられますか?それらには、値の署名による一致があります。私はこれまでvoidと宣言された関数ポインタを見たことがありませんが、それをきれいにコンパイルする唯一の方法です。

スタック的には、アセンブラーコーディングで学んだこととまったく違いはありません。

無関係なOMG!StackOverflowでStackwiseと言いました!

うーん..ここで想定しすぎたようです。よろしければ、はっきりさせておきます。アドレスに保存されているデータの「タイプ」は関係ありません。私が懸念しているのは、「ユニット」のサイズとアドレスにあるユニットの数だけです。次の場合は、APIのインターフェース契約を確認してください。

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などは、アドレスの内容を気にしないでください。必要なのは、ここで明確に追跡している詳細だけです。

この契約に従う最初のプロトタイプを今すぐ見てください:

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は場合によって処理が異なると言えますが、結局のところ、同じマシンでメモリを処理している限り、anaddressaddress、a、byteis byte、alongはaです。long

このシステムをまとめた目的は、このタイプベースのジャグリングCをすべて削除することです。これは、私がやりたいことには無意味です。これは、任意のアドレスにある任意の標準サイズのデータ​​(1、2、4、8、sizeof(RandomStruct))のプロトタイプを順守するコントラクトを作成することです。

コードを使用して独自のキャストを実行し、特定の長さのメモリユニットを使用して特定の長さのメモリブロックを操作するAPI関数を使用してそのデータを操作する機能を備えています。ただし、プロトタイプには公式のデータポインター型が含まれている必要があります。これは、エンドユーザーがそのアドレスポインターで何かを実行するたびにデータを再キャストする必要があることは意味がないためです。ポインタがvoidの場合、これを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;
4

4 に答える 4

3

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

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

void *キャストなしでポインタを関数ポインタに変換しています。Cでは、void *ポインターはキャストなしでオブジェクト型へのポインターに変換できますが、関数ポインター型には変換できません。(C ++では、void *安全性を高めるために、オブジェクトタイプに変換するためにもキャストが必要です。)

ここで必要なのは、関数ポインタ型間でキャストすることです。

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

次に、関数が異なるタイプであるため、同じタイプのパンニングを実行しています。StdBufferをとる関数へのポインタを使用して、をとる関数を呼び出そうとしていますCharBuffer

このタイプのコードは明確に定義されていませんC.型システムを打ち負かした後は、テスト、オブジェクトコードの調査、またはこの種のものがそのコンパイラで機能することをコンパイラの作成者から保証することに依存します。 。

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

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

于 2012-04-24T20:09:17.037 に答える
1

この標準では、関数ポインタをにキャストした結果は定義されていませんvoid *

同様に、関数ポインター間で変換してから間違ったポインターを呼び出すことも、未定義の動作です。

于 2012-04-24T20:11:53.363 に答える
0

標準に準拠するCコンパイラが一貫して実装する必要がある構造もあれば、99%のCコンパイラが一貫して実装する構造もありますが、標準に準拠するコンパイラは別の方法で自由に実装できます。あるタイプのポインターをとる関数へのポインターを、別のタイプのポインターをとる関数へのポインターにキャストしようとすると、後者のカテゴリーに分類されます。C規格では、avoid*とachar*同じサイズである必要があります。同じビットレベルのストレージ形式を共有する必要があるものは何もありません。パラメータの受け渡しの規則ははるかに少ないです。ほとんどのマシンでは、ワードとほぼ同じ方法でバイトにアクセスできますが、そのような機能は普遍的ではありません。アプリケーションバイナリインターフェイス[特にパラメータがルーチンに渡される方法を指定するドキュメント]の設計者は、char*バイトアクセスの効率を最大化する方法でaを渡すことを指定し、aは次のvoid*ように渡す必要があります。アラインされていないバイトアドレスを保持する機能を維持しながら、ワードアクセスの効率を最大化します。おそらく、補足ワードを使用して0または1を保持し、LSB/MSBを示します。void*そのようなマシンで、パスを期待するコードからの呼び出しを期待するルーチンを持つchar*ルーチンが任意の間違ったデータにアクセスする可能性があります。

于 2013-01-09T16:41:48.713 に答える
-1

いいえ、データの保存に使用されるデータ型は関係ありません。タイプCがそのデータの読み取りと書き込みに使用すること、およびデータが十分なサイズであることだけが重要です。

于 2012-04-24T20:07:29.587 に答える