0

私は C で書くことに比較的慣れていません。オンラインや印刷物で見つけたリソースを使って独学しました。これは、C プログラミングの最初の実際のプロジェクトです。職場でのトレーニングが大好きです。

Texas Instruments C6701 デジタル シグナル プロセッサで使用されているコードを C で書いています。具体的には、シリアル ポートを介してインターフェイスする一連の通信関数を作成しています。

私が取り組んでいるプロジェクトには、シリアル ポート経由でデータを送信するための既存のパケット プロトコルがあります。これは、送信されるデータへのポインターとその長さ (バイト単位) を渡すことによって機能します。私がしなければならないことは、送信するバイトをメモリ内の「配列」に書き込むことだけです (送信機はその一連のバイトをバッファにコピーして送信します)。

私の質問は、送信するデータの最適なフォーマット方法に関するものです。送信する必要があるデータは、いくつかの異なるデータ型 (unsigned char、unsigned int、float など) で構成されています。通信帯域幅に制約があり、パケットをできるだけ小さく保つ必要があるため、すべてを float (または int) に拡張することはできません。

もともと配列を使用してデータをフォーマットしたかったのですが、

unsigned char* dataTx[10];
dataTx[0]=char1;
dataTx[1]=char2;
etc...

これは、すべてのデータが char であるとは限らず、一部のデータが unsigned int または unsigned short であることを除いて機能します。

short と int を処理するために、ビット シフトを使用しました (今のところ、リトルエンディアンとビッグ エンディアンは無視します)。

unsigned char* dataTx[10];
dataTx[0]=short1>>8;
dataTx[1]=short1;
dataTx[2]=int1>>24;
dataTx[3]=int1>>16;
etc...

ただし、これを行う別の (そしてより良い?) 方法は、ポインターとポインター演算を使用することだと思います。

unsigned char* dataTx[10]
*(dataTx+0) = int1;
*(dataTx+4) = short1;
*(dataTx+6) = char1;
etc...

私の質問(最後に) は、どちらの方法 (ビットシフトまたはポインター演算) がより受け入れられる方法ですか? また、実行する方が速いですか?(実行時の制約もあります)。

私の要件:データは、ギャップ、ブレーク、またはパディングなしで、連続してメモリに配置されます。

構造が解決策として機能するかどうかを知るには、構造についてまだ十分に知りません。具体的には、構造体が常に連続してブレークなしでメモリ位置を割り当てるかどうかはわかりません。8 バイト ブロックで割り当てられていることを示すものを読み、パディング バイトを導入する可能性があります。

現在、私はポインターメソッドに傾いています。長い記事のように見えるものをここまで読んでくれてありがとう。

4

4 に答える 4

0

共用体の配列を使用したい場合があります。

于 2011-08-24T21:23:56.173 に答える
0

多くのチップでは、たとえば 4 バイト整数を奇数バイト アドレス (または、より正確には、奇数バイト アドレスで始まる 4 バイトのセット) にコピーできないため、通常はビット シフト アプローチを使用します。 )。これをアライメントと呼びます。移植性が問題になる場合、または DSP がミスアライメント アクセスを許可しない場合は、シフトが必要です。DSP がミスアライン アクセスのために重大なパフォーマンス ヒットを被る場合、それについて心配するかもしれません。

ただし、示されているように、さまざまなタイプのシフトを手書きで記述したコードは記述しません。関数 (おそらくインライン) またはマクロを使用して、データのシリアル化と逆シリアル化の両方を処理することを期待しています。例えば:

unsigned char dataTx[1024];
unsigned char *dst = dataTx;

dst += st_int2(short1, dst);
dst += st_int4(int1, dst);
dst += st_char(str, len, dst);
...

関数形式では、これらの関数は次のようになります。

size_t st_int2(uint16_t value, unsigned char *dst)
{
    *dst++ = (value >> 8) & 0xFF;
    *dst   = value & 0xFF;
    return 2;
}

size_t st_int4(uint32_t value, unsigned char *dst)
{
    *dst++ = (value >> 24) & 0xFF;
    *dst++ = (value >> 16) & 0xFF;
    *dst++ = (value >>  8) & 0xFF;
    *dst   = value & 0xFF;
    return 4;
}

size_t st_char(unsigned char *str, size_t len, unsigned char *dst)
{
    memmove(dst, str, len);
    return len;
}

確かに、そのような関数はコードを退屈なものにします。一方で、ミスの可能性も減らします。名前を --st_uint2()の代わりにst_int2()するかどうかを決定できます。実際、長さをバイト単位にするか (ここのように)、ビット単位にするか (パラメーターの型のように) を決定できます。一貫性があり退屈である限り、好きなようにできます。これらの関数を組み合わせて、データ構造全体をパッケージ化するより大きな関数にすることもできます。

マスキング操作 ( & 0xFF) は、最新のコンパイラでは必要ない場合があります。むかしむかし、一部のプラットフォームの一部のコンパイラで時折発生する問題を回避するために必要だったことを覚えているようです (そのため、そのようなマスキング操作を含む 1980 年代にさかのぼるコードがあります)。上記のプラットフォームはおそらく安らかに眠っているので、それらが(まだ)そこにあるというのは私の側の純粋なパラノイアかもしれません.

これらの関数はビッグ エンディアンの順序でデータを渡していることに注意してください。関数はビッグ エンディアンとリトル エンディアンの両方のマシンで「そのまま」使用でき、データは両方のタイプで正しく解釈されるため、このコードを使用してさまざまなハードウェアがネットワーク経由で通信することができます。誤解がない。伝達する浮動小数点値がある場合は、ワイヤ上の表現についてもう少し心配する必要があります。とはいえ、チップ タイプ間のインターワーキングができるだけ単純になるように、プラットフォームに依存しない形式でデータを転送することを目指す必要があります。(これが、数値を含む型サイズを使用した理由でもあります。特に、'int' と 'long' は、異なるプラットフォームでは異なることを意味する可能性がありますが、4 バイトの符号付き整数は 4 バイトの符号付き整数のままです。

于 2011-08-24T21:48:57.573 に答える
0

問題を処理する最も簡単で最も伝統的な方法は、送信するデータを設定し、データへのポインターを送信ルーチンに渡すことです。最も一般的な例は、POSIXsend()ルーチンです。

ssize_t send(int socket, const void *buffer, size_t length, int flags);

あなたのケースでは、次のように単純化できます。

ssize_t send(const void *buffer, size_t length);

そして、次のようなものを使用します:

send(&int1, sizeof int1);
send(&short1, sizeof short1);

それを送信します。あなたの状況の例(しかしかなり素朴な)の実装は次のとおりです。

ssize_t send(const void *buffer, size_t length)
{
  size_t i;
  unsigned char *data = buffer;

  for (i = 0; i < length; i++)
  {
     dataTx[i] = data[i];
  }
}

つまり、 への自動変換を使用してvoid *から に戻りchar *、データへのバイト単位のアクセスを取得してから、適切に送信します。

于 2011-08-24T21:28:55.683 に答える
0

長い質問です。短い答えを試してみます。

続行しないでください *(dataTx+4) = short1; ほとんどのチップは、いくつかの位置合わせされた位置でのみ読み取り/書き込みを行う可能性があるため、この方法は失敗する可能性があるためです。2 でアラインされた位置には 16 ビットでアクセスでき、4 でアラインされた位置では 32 ビットでアクセスできますが、次の例を見てください。おそらく「バスエラー」またはそのようなものが発生します(使用するCPUによって異なります)。この問題を理解していただければ幸いです。

第 1 の方法 - 次のように宣言すると、構造体を試すことができます。

struct
{
    char a;
    int b;
    char c;
    short d;
};

コンパイラ自体が構造体のアラインメントを処理するため、問題は解決しました。もちろん、コンパイラのアラインメント関連のオプションについて読んでください (これが gcc の場合、これは単にアラインメントと呼ばれます)。おそらく、構造体フィールドのアラインメントまたは構造体フィールドのパッキングを強制する設定があるからです。GCC は、alignment-per-struct を定義することもできます (詳細はこちら)。

もう1つの方法は、「バッファのようなアプローチ」を使用することです-Carl Norumのanswer-postのようなものです(その回答を複製しません)が、より多くのデータがコピーされたときに memcpy() 呼び出しを使用することも検討します(これは、バイトごとにコピーするよりも高速な場合があるためです。

于 2011-08-24T21:40:47.943 に答える