質問

Cには大きな配列があります(違いがある場合はC ++ではありません)。すべてのメンバーを同じ値に初期化したい。私はかつてこれを行う簡単な方法を知っていたことを誓うことができました。私の場合は memset()を使用できますが、C構文に直接組み込まれている方法はありませんか?

役に立ちましたか?

解決

その値が0でない場合(この場合、イニシャライザーの一部を省略できます) 対応する要素は0に初期化されます)、簡単な方法はありません。

ただし、明らかな解決策を見逃さないでください:

int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };

欠損値のある要素は0に初期化されます:

int myArray[10] = { 1, 2 }; // initialize to 1,2,0,0,0...

したがって、これはすべての要素を0に初期化します:

int myArray[10] = { 0 }; // all elements 0

C ++では、空の初期化リストもすべての要素を0に初期化します。 これは Cでは許可されません

int myArray[10] = {}; // all elements 0 in C++

静的ストレージ期間を持つオブジェクトは、そうでない場合は0に初期化されることを覚えておいてください 初期化子が指定されています:

static int myArray[10]; // all elements 0

そして、その" 0"必ずしも「すべてのビットがゼロ」を意味するわけではないため、上記を使用すると memset()よりも優れた移植性があります。 (浮動小数点値は +0に初期化、null値へのポインターなど)

他のヒント

コンパイラがGCCの場合、次の構文を使用できます。

int array[1024] = {[0 ... 1023] = 5};

詳細な説明をご覧ください。 http://gcc.gnu.org/onlinedocs/gcc -4.1.2 / gcc / Designated-Inits.html

コピーアンドペーストを複数回行わずに、同じ値で大きな配列を静的に初期化するには、マクロを使用できます:

#define VAL_1X     42
#define VAL_2X     VAL_1X,  VAL_1X
#define VAL_4X     VAL_2X,  VAL_2X
#define VAL_8X     VAL_4X,  VAL_4X
#define VAL_16X    VAL_8X,  VAL_8X
#define VAL_32X    VAL_16X, VAL_16X
#define VAL_64X    VAL_32X, VAL_32X

int myArray[53] = { VAL_32X, VAL_16X, VAL_4X, VAL_1X };

値を変更する必要がある場合は、1か所でのみ交換する必要があります。

編集:便利な拡張機能の可能性

Jonathan Leffler の提供)

これを簡単に一般化できます:

#define VAL_1(X) X
#define VAL_2(X) VAL_1(X), VAL_1(X)
/* etc. */

バリアントは次を使用して作成できます。

#define STRUCTVAL_1(...) { __VA_ARGS__ }
#define STRUCTVAL_2(...) STRUCTVAL_1(__VA_ARGS__), STRUCTVAL_1(__VA_ARGS__)
/*etc */ 

構造または複合配列で動作します。

#define STRUCTVAL_48(...) STRUCTVAL_32(__VA_ARGS__), STRUCTVAL_16(__VA_ARGS__)

struct Pair { char key[16]; char val[32]; };
struct Pair p_data[] = { STRUCTVAL_48("Key", "Value") };
int a_data[][4] = { STRUCTVAL_48(12, 19, 23, 37) };

マクロ名は交渉可能です。

配列のすべてのメンバーが明示的に初期化されていることを確認したい場合は、宣言から次元を省略してください:

int myArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

コンパイラは、初期化子リストから次元を推測します。残念ながら、多次元配列の場合、最も外側の次元のみを省略できます:

int myPoints[][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9} };

大丈夫ですが、

int myPoints[][] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9} };

ではありません。

この構文を使用したコードを見ました:

char* array[] = 
{
    [0] = "Hello",
    [1] = "World"
};   

特に有用になるのは、列挙型をインデックスとして使用する配列を作成する場合です。

enum
{
    ERR_OK,
    ERR_FAIL,
    ERR_MEMORY
};

#define _ITEM(x) [x] = #x

char* array[] = 
{
    _ITEM(ERR_OK),
    _ITEM(ERR_FAIL),
    _ITEM(ERR_MEMORY)
};   

これは、偶然に列挙値の一部を順不同で書き込んでも、物事を整然と保ちます。

この手法の詳細については、こちらおよびこちら

>
int i;
for (i = 0; i < ARRAY_SIZE; ++i)
{
  myArray[i] = VALUE;
}

これはより良いと思う

int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5...

配列のサイズが変更された場合。

上記のように静的イニシャライザー全体を実行できますが、配列サイズが変更されると(配列embiggensの場合、適切なイニシャライザーを追加しないとガベージが発生します) p>

memsetは、作業を実行するためのランタイムヒットを提供しますが、正しく行われたコードサイズヒットは配列サイズの変更の影響を受けません。配列がたとえば数十個の要素よりも大きい場合、ほとんどすべての場合にこのソリューションを使用します。

配列が静的に宣言されることが本当に重要な場合は、プログラムを作成してプログラムを作成し、ビルドプロセスの一部にします。

別の方法を次に示します。

static void
unhandled_interrupt(struct trap_frame *frame, int irq, void *arg)
{
    //this code intentionally left blank
}

static struct irqtbl_s vector_tbl[XCHAL_NUM_INTERRUPTS] = {
    [0 ... XCHAL_NUM_INTERRUPTS-1] {unhandled_interrupt, NULL},
};

参照:

C-Extensions

指定された初期化

次に質問をします:C拡張機能はいつ使用できますか?

上記のコードサンプルは組み込みシステムにあり、他のコンパイラからの光を見ることはありません。

「int」配列などの「通常の」データ型を初期化するには、ブラケット表記を使用できますが、配列にまだスペースがある場合は最後の値の後の値をゼロにします:

// put values 1-8, then two zeroes
int list[10] = {1,2,3,4,5,6,7,8};

やや舌に近い答え。文を書く

array = initial_value
お気に入りの配列対応言語(はFortranですが、他にもたくさんあります)で

、それをCコードにリンクします。おそらく、それを外部関数になるようにまとめたいと思うでしょう。

配列がintまたはintのサイズを持つもの、またはmem-patternのサイズがintに正確に一致する場合(つまり、すべてゼロまたは0xA5A5A5A5)、最良の方法は memset()

それ以外の場合は、インデックスを移動するループでmemcpy()を呼び出します。

任意のタイプの配列を指定された値で初期化する高速な方法があります。大規模な配列で非常にうまく機能します。アルゴリズムは次のとおりです。

  • 配列の最初の要素を初期化する(通常の方法)
  • 設定されていない部分に設定されている部分をコピーし、次のコピー操作ごとにサイズを2倍にします

1 000 000 要素 int 配列の場合、通常のループ初期化(i5、2コア、2.3 GHz、4GiBメモリ、64ビット)の4倍高速です:

ループランタイム0.004248 [秒]

memfill()ランタイム0.001085 [秒]


#include <stdio.h>
#include <time.h>
#include <string.h>
#define ARR_SIZE 1000000

void memfill(void *dest, size_t destsize, size_t elemsize) {
   char   *nextdest = (char *) dest + elemsize;
   size_t movesize, donesize = elemsize;

   destsize -= elemsize;
   while (destsize) {
      movesize = (donesize < destsize) ? donesize : destsize;
      memcpy(nextdest, dest, movesize);
      nextdest += movesize; destsize -= movesize; donesize += movesize;
   }
}    
int main() {
    clock_t timeStart;
    double  runTime;
    int     i, a[ARR_SIZE];

    timeStart = clock();
    for (i = 0; i < ARR_SIZE; i++)
        a[i] = 9;    
    runTime = (double)(clock() - timeStart) / (double)CLOCKS_PER_SEC;
    printf("loop runtime %f [seconds]\n",runTime);

    timeStart = clock();
    a[0] = 10;
    memfill(a, sizeof(a), sizeof(a[0]));
    runTime = (double)(clock() - timeStart) / (double)CLOCKS_PER_SEC;
    printf("memfill() runtime %f [seconds]\n",runTime);
    return 0;
}

誰も初期化された配列の要素にアクセスするためのインデックスの順序について言及していません。私の例のコードは説明的な例を示します。

#include <iostream>

void PrintArray(int a[3][3])
{
    std::cout << "a11 = " << a[0][0] << "\t\t" << "a12 = " << a[0][1] << "\t\t" << "a13 = " << a[0][2] << std::endl;
    std::cout << "a21 = " << a[1][0] << "\t\t" << "a22 = " << a[1][1] << "\t\t" << "a23 = " << a[1][2] << std::endl;
    std::cout << "a31 = " << a[2][0] << "\t\t" << "a32 = " << a[2][1] << "\t\t" << "a33 = " << a[2][2] << std::endl;
    std::cout << std::endl;
}

int wmain(int argc, wchar_t * argv[])
{
    int a1[3][3] =  {   11,     12,     13,     // The most
                        21,     22,     23,     // basic
                        31,     32,     33  };  // format.

    int a2[][3] =   {   11,     12,     13,     // The first (outer) dimension
                        21,     22,     23,     // may be omitted. The compiler
                        31,     32,     33  };  // will automatically deduce it.

    int a3[3][3] =  {   {11,    12,     13},    // The elements of each
                        {21,    22,     23},    // second (inner) dimension
                        {31,    32,     33} };  // can be grouped together.

    int a4[][3] =   {   {11,    12,     13},    // Again, the first dimension
                        {21,    22,     23},    // can be omitted when the 
                        {31,    32,     33} };  // inner elements are grouped.

    PrintArray(a1);
    PrintArray(a2);
    PrintArray(a3);
    PrintArray(a4);

    // This part shows in which order the elements are stored in the memory.
    int * b = (int *) a1;   // The output is the same for the all four arrays.
    for (int i=0; i<9; i++)
    {
        std::cout << b[i] << '\t';
    }

    return 0;
}

出力は次のとおりです。

a11 = 11                a12 = 12                a13 = 13
a21 = 21                a22 = 22                a23 = 23
a31 = 31                a32 = 32                a33 = 33

a11 = 11                a12 = 12                a13 = 13
a21 = 21                a22 = 22                a23 = 23
a31 = 31                a32 = 32                a33 = 33

a11 = 11                a12 = 12                a13 = 13
a21 = 21                a22 = 22                a23 = 23
a31 = 31                a32 = 32                a33 = 33

a11 = 11                a12 = 12                a13 = 13
a21 = 21                a22 = 22                a23 = 23
a31 = 31                a32 = 32                a33 = 33

11      12      13      21      22      23      31      32      33

すべてのおしゃべりを切り取ると、簡単な答えは、コンパイル時に最適化を有効にすると、これ以上の効果は得られないということです:

int i,value=5,array[1000]; 
for(i=0;i<1000;i++) array[i]=value; 

ボーナスを追加:コードは実際に判読可能です:)

  1. 配列が静的またはグローバルとして宣言されている場合、すべての要素 配列のデフォルトのデフォルト値は0です。
  2. 一部のコンパイラは、デバッグモードで配列のデフォルトを0に設定します。
  3. デフォルトを0に設定するのは簡単です: int array [10] = {0};
  4. ただし、他の値については、memset()またはloopを使用しています。

例:     int array [10];     memset(array、-1、10 * sizeof(int));

#include<stdio.h>
int main(){
int i,a[50];
for (i=0;i<50;i++){
    a[i]=5;// set value 5 to all the array index
}
for (i=0;i<50;i++)
printf("%d\n",a[i]);
   return 0;
}

o / p 5 5 5 5 5 5 ......配列全体のサイズまで与えます

ユーザー Tarski が同様の方法でこの質問に回答したことは知っていますが、さらに詳細を追加しました。私はC ++を使いたいと思っているので少し錆びているので、Cの一部を許してください。しかし、ここに行きます。


事前に配列のサイズがわかっている場合...

#include <stdio.h>

typedef const unsigned int cUINT;
typedef unsigned int UINT;

cUINT size = 10;
cUINT initVal = 5;

void arrayInitializer( UINT* myArray, cUINT size, cUINT initVal );
void printArray( UINT* myArray ); 

int main() {        
    UINT myArray[size]; 
    /* Not initialized during declaration but can be
    initialized using a function for the appropriate TYPE*/
    arrayInitializer( myArray, size, initVal );

    printArray( myArray );

    return 0;
}

void arrayInitializer( UINT* myArray, cUINT size, cUINT initVal ) {
    for ( UINT n = 0; n < size; n++ ) {
        myArray[n] = initVal;
    }
}

void printArray( UINT* myArray ) {
    printf( "myArray = { " );
    for ( UINT n = 0; n < size; n++ ) {
        printf( "%u", myArray[n] );

        if ( n < size-1 )
            printf( ", " );
    }
    printf( " }\n" );
}

上記のいくつかの注意事項があります。 1つは、 UINT myArray [size]; は宣言時に直接初期化されませんが、次のコードブロックまたは関数呼び出しは、配列の各要素を希望する同じ値に初期化します。もう1つの注意点は、サポートする type ごとに initializing function を作成する必要があり、 printArray()これらのタイプをサポートする関数。


こちらで見つかったオンラインコンパイラでこのコードを試すことができます。

遅延初期化(つまり、クラスメンバーコンストラクターの初期化)の場合:

int a[4];

unsigned int size = sizeof(a) / sizeof(a[0]);
for (unsigned int i = 0; i < size; i++)
  a[i] = 0;

過去に(そしてそれが良いアイデアだとは言っていません)、最初の要素を設定してから:

memcpy(&amp; element [1]、&amp; element [0]、sizeof(element)-sizeof(element [0]);

機能するかどうかはわかりませんが(memcpyの実装に依存します)、最初の要素を次の要素に繰り返しコピーすることで機能します-構造体の配列でも機能します。

質問には要件がありませんので、解決策は一般的でなければなりません:初期メンバー値を持つ不特定のおそらく構造要素から構築された不特定の多次元配列の初期化:

#include <string.h> 

void array_init( void *start, size_t element_size, size_t elements, void *initval ){
  memcpy(        start,              initval, element_size              );
  memcpy( (char*)start+element_size, start,   element_size*(elements-1) );
}

// testing
#include <stdio.h> 

struct s {
  int a;
  char b;
} array[2][3], init;

int main(){
  init = (struct s){.a = 3, .b = 'x'};
  array_init( array, sizeof(array[0][0]), 2*3, &init );

  for( int i=0; i<2; i++ )
    for( int j=0; j<3; j++ )
      printf("array[%i][%i].a = %i .b = '%c'\n",i,j,array[i][j].a,array[i][j].b);
}

結果:

array[0][0].a = 3 .b = 'x'
array[0][1].a = 3 .b = 'x'
array[0][2].a = 3 .b = 'x'
array[1][0].a = 3 .b = 'x'
array[1][1].a = 3 .b = 'x'
array[1][2].a = 3 .b = 'x'

EDIT: start + element_size (char *)start + element_size

に変更されました

元の質問がC ++ではなくCを明示的に言及していることは知っていますが、(私のように)C ++配列のソリューションを探してここに来た場合は、ここにきちんとしたトリックがあります:

コンパイラがフォールド式をサポートしている場合、テンプレートマジックと std :: index_sequence を使用して、必要な値を持つ初期化リストを生成します。そして、あなたはそれを constexpr して、ボスのように感じることさえできます:

#include <array>

/// [3]
/// This functions's only purpose is to ignore the index given as the second
/// template argument and to always produce the value passed in.
template<class T, size_t /*ignored*/>
constexpr T identity_func(const T& value) {
    return value;
}

/// [2]
/// At this point, we have a list of indices that we can unfold
/// into an initializer list using the `identity_func` above.
template<class T, size_t... Indices>
constexpr std::array<T, sizeof...(Indices)>
make_array_of_impl(const T& value, std::index_sequence<Indices...>) {
    return {identity_func<T, Indices>(value)...};
}

/// [1]
/// This is the user-facing function.
/// The template arguments are swapped compared to the order used
/// for std::array, this way we can let the compiler infer the type
/// from the given value but still define it explicitly if we want to.
template<size_t Size, class T>
constexpr std::array<T, Size> 
make_array_of(const T& value) {
    using Indices = std::make_index_sequence<Size>;
    return make_array_of_impl(value, Indices{});
}

// std::array<int, 4>{42, 42, 42, 42}
constexpr auto test_array = make_array_of<4/*, int*/>(42);
static_assert(test_array[0] == 42);
static_assert(test_array[1] == 42);
static_assert(test_array[2] == 42);
static_assert(test_array[3] == 42);
// static_assert(test_array[4] == 42); out of bounds

職場でのコード(Wandbox)をご覧ください

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top