developer tip

배열의 모든 멤버를 동일한 값으로 초기화하는 방법은 무엇입니까?

optionbox 2020. 9. 28. 09:02
반응형

배열의 모든 멤버를 동일한 값으로 초기화하는 방법은 무엇입니까?


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"이 반드시 "모든 비트 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 };

값을 변경해야하는 경우 한 곳에서만 교체해야합니다.

편집 : 가능한 유용한 확장

( 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...

배열의 크기가 변경되는 경우.


위에서 설명한대로 전체 정적 이니셜 라이저 작업을 수행 할 수 있지만 배열 크기가 변경 될 때 (배열이 비대화 될 때 적절한 추가 이니셜 라이저를 추가하지 않으면 가비지가 발생합니다) 정말 실망 스러울 수 있습니다.

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 확장

지정된 이니셜

그런 다음 질문하십시오. 언제 C 확장을 사용할 수 있습니까?

위의 코드 샘플은 임베디드 시스템에 있으며 다른 컴파일러의 빛을 볼 수 없습니다.


'정상'데이터 유형 (예 : int 배열)을 초기화하려면 대괄호 표기법을 사용할 수 있지만 배열에 여전히 공백이있는 경우 마지막 이후의 값이 0이됩니다.

// 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 (즉, 모두 0 또는 0xA5A5A5A5)에 정확한 시간에 맞는 경우 가장 좋은 방법은 memset () 을 사용하는 것입니다 .

그렇지 않으면 인덱스를 이동하는 루프에서 memcpy ()를 호출하십시오.


주어진 값으로 모든 유형의 배열을 초기화하는 빠른 방법이 있습니다. 큰 배열에서 매우 잘 작동합니다. 알고리즘은 다음과 같습니다.

  • 배열의 첫 번째 요소 초기화 (일반적인 방법)
  • 설정되지 않은 부분으로 설정된 부분을 복사하여 다음 복사 작업마다 크기를 두 배로 늘립니다.

들면 1 000 000소자 int어레이는 일정한 루프 초기화 (I5, 2 개 코어, 2.3 GHz의 4GiB 메모리, 64 비트)보다 4 배 빠른 것이다 :

loop runtime 0.004248 [seconds]

memfill() runtime 0.001085 [seconds]


#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" );
}

There are a few caveats above; one is that UINT myArray[size]; is not directly initialized upon declaration, however the very next code block or function call does initialize each element of the array to the same value you want. The other caveat is, you would have to write an initializing function for each type you will support and you would also have to modify the printArray() function to support those types.


You can try this code with an online complier found here.


For delayed initialization (i.e. class member constructor initialization) consider:

int a[4];

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

Back in the day (and I'm not saying it's a good idea), we'd set the first element and then:

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

Not even sure it would work any more (that would depend on the implementation of memcpy) but it works by repeatedly copying the initial element to the next - even works for arrays of structures.


I see no requirements in the question, so the solution must be generic: initialization of an unspecified possibly multidimensional array built from unspecified possibly structure elements with an initial member value:

#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);
}

Result:

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 changed to (char*)start+element_size


I know the original question explicitly mentions C and not C++, but if you (like me) came here looking for a solution for C++ arrays, here's a neat trick:

If your compiler supports fold expressions, you can use template magic and std::index_sequence to generate an initializer list with the value that you want. And you can even constexpr it and feel like a boss:

#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

You can take a look at the code at work (at Wandbox)

참고URL : https://stackoverflow.com/questions/201101/how-to-initialize-all-members-of-an-array-to-the-same-value

반응형