1

void*データ配列へのポインターを提供するCの単純な関数があります。このメモリブロック内の個々のデータポイントのサイズ(バイト単位)を知っているので、隣接するデータポイントを誤って変更することなく、このブロック内の各データポイントを変更できることを保証する必要があります。この例では、各値を1ずつデクリメントします。

すべてのデータポイントは、8ビット、16ビット、または32ビットのいずれかです。

例えば:

void myFunction(void* data, size_t arraySize, size_t widthPerDataPoint)
{
  if(!data)
    return -1;
  size_t w = widthPerDataPoint;
  int numPoints = arraySize / widthPerDataPoint;
  int i;
  for(i=0; i<numPoints; i++)
  {
    if(w==1)       // 8 bit
      (*((int8_t*)data + i))--;
    else if(w==2)  // 16 bit
      (*((int16_t*)data + i))--;
    else if(w==4)  // 32 bit
      (*((int32_t*)data + i))--;
  }
}

残念ながら、int8_tなどのデータ型は、C99仕様に従って最小サイズのみを保証し、正確なサイズは保証しません。データをインプレースで再キャストおよび変更して、アレイを壊したり、隣接するデータポイントに触れたりしないことを保証する方法はありますか?また、他のデータ幅(24ビット、60ビットなど)でも何らかの形で機能する同等の手法はありますか?

4

4 に答える 4

2

定義により、タイプのポインターを n だけインクリメントするT*n * sizeof(T)バイト単位でシフトされます。したがって、一貫性はコンパイラによって保証されます。心配ない。

于 2013-01-26T00:50:12.177 に答える
2

int8_tは正確に 8 ビットであることが保証されており、 の場合CHAR_BIT==1は正確に 1 バイトです。

最新の C 標準、セクション 7.20.1.1のN1570ドラフトを引用します。

typedef 名int N _tは、幅N、パディング ビットなし、および 2 の補数表現を持つ符号付き整数型を指定します。したがって、int8_tは、正確に 8 ビット幅の符号付き整数型を示します。

あなたの目的のためには、、、などを使用する方が理にかなっているかもしれませuint8_tuint16_t

実装が必要な特性を持つ型をサポートしていない場合、それらは定義されません。これは、たとえば次のようにチェックすることで検出できます。

#include <stdint.h>
#ifdef UINT8_MAX
/* uint8_t exists */
#else
/* uint8_t doesn't exist */
#endif

( の場合CHAR_BIT != 8、どちらint8_tuint8_t定義されません。)

標準で最小サイズのみが保証されているのは[u]intleast_N_tとの型です。[u]intfast_t

配列とその中のオフセットの両方が、アクセスに使用している型に対して適切に配置されていることを保証する必要があります。すでにお世話になっていると思います。

于 2013-01-26T00:50:16.557 に答える
1

コードは完全に不合理ではないようです。私は個人的におそらく次のようなことをするでしょう:

 switch(widthPerDataPoint)
 {
    case 1:
       {
          int8_t *dptr = data;
          for(i = 0; i < numPoints; i++)
             dptr[i]--;
       }
       break;
    case 2:
       {
          int16_t *dptr = data;
          for(i = 0; i < numPoints; i++)
             dptr[i]--;
       }
       break;

    case 4:
       {
          int32_t *dptr = data;
          for(i = 0; i < numPoints; i++)
             dptr[i]--;
       }
       break;

    default:
       fprintf(stderr, "Someone gave the wrong width - width=%d\n", 
               widthPerDatapoint);
       break;
}

ここでの利点は、すべてのループで多数の条件が得られないことです。とにかくコンパイラはそれを整理するかもしれませんが、私はコンパイラがそのようなことを理解することを常に信頼しているわけではありません-そして、それも少しきれいだと思います。

于 2013-01-26T00:50:04.313 に答える
-1

以下はいかがでしょうか?

  1. 値を任意のサイズのローカル int にコピーします

  2. ローカル変数でデクリメントを実行します

  3. 適切なビット マスクを使用して、配列内の位置をゼロにします (例: 8 ビットの場合は ~0xFF)。

  4. 「そして」ローカル変数を配列に戻します。

于 2013-01-26T00:53:10.470 に答える