C의 어레이 초기화에 대한 혼란
C 언어에서는 어레이를 다음과 같이 초기화합니다.
int a[5] = {1,2};
그러면 명시적으로 초기화되지 않은 배열의 모든 요소가 0으로 암묵적으로 초기화됩니다.
다만, 다음과 같이 어레이를 초기화할 경우:
int a[5]={a[2]=1};
printf("%d %d %d %d %d\n", a[0], a[1],a[2], a[3], a[4]);
출력:
1 0 1 0 0
이해가 안 되는데, 왜?a[0]
인쇄물1
대신0
정의되지 않은 동작입니까?
주의: 이 질문은 인터뷰에서 한 질문입니다.
TL;DR: 제 생각엔 그 행동이int a[5]={a[2]=1};
적어도 C99에서는 잘 정의되어 있습니다.
재밌는 건 네가 물어보는 부분밖에 없다는 거야a[0]
로 설정되어 있다.1
할당 연산자가 할당된 값을 반환하기 때문입니다.그 외 모든 것이 불분명합니다.
만약 코드가int a[5] = { [2] = 1 }
모든 것이 쉬웠을 것이다.지정된 이니셜라이저 설정입니다.a[2]
로.1
기타 모든 것0
하지만...{ a[2] = 1 }
지정되지 않은 이니셜라이저가 할당식을 포함하고 있어 토끼굴에 떨어집니다.
지금까지 알아낸 내용은 다음과 같습니다.
a
로컬 변수여야 합니다.6.7.8 초기화
- 정적 저장 기간이 있는 객체의 이니셜라이저 표현식은 모두 상수 표현식 또는 문자열 리터럴이어야 합니다.
a[2] = 1
상수 표현이 아니기 때문에a
자동 저장 장치가 있어야 합니다.a
자체 초기화 범위 내에 있습니다.6.2.1 식별자의 범위
- 구조, 결합 및 열거 태그에는 태그를 선언하는 유형 지정자에 태그가 나타난 직후에 시작되는 범위가 있습니다.각 열거 상수에는 정의 열거자가 열거자 목록에 나타난 직후에 시작되는 범위가 있습니다.다른 식별자에는 선언자 완료 직후에 시작되는 범위가 있습니다.
선언자는
a[5]
따라서 변수는 자체 초기화 범위 내에 있습니다.a
는 자체 초기화 중에 활성화되어 있습니다.6.2.4 물건의 보관기간
연결 없이 스토리지 클래스 지정자 없이 식별자가 선언된 개체입니다.
static
에는 자동 저장 기간이 있습니다.가변 길이 배열 유형이 없는 객체의 경우 해당 객체의 라이프타임이 연관된 블록의 엔트리부터 해당 블록의 실행이 어떤 방식으로든 종료될 때까지 연장됩니다(봉쇄된 블록에 들어가거나 함수를 호출하면 현재 블록의 실행이 중단되지만 종료되지는 않습니다).블록이 재귀적으로 입력될 경우 객체의 새 인스턴스가 매번 생성됩니다.개체의 초기 값이 불확실합니다.오브젝트에 대해 초기화가 지정되어 있는 경우 블록 실행 시 선언에 도달할 때마다 값이 실행됩니다.그렇지 않으면 선언에 도달할 때마다 값이 미확정 상태가 됩니다.
뒤에 시퀀스 포인트가 있습니다.
a[2]=1
.6.8 문과 블록
- 완전 표현식은 다른 표현식이나 선언자의 일부가 아닌 표현식입니다.다음 각 항목은 완전한 표현입니다.Initializer, Expression 스테이트먼트 내의 표현, 선택 스테이트먼트의 제어 표현(
if
또는switch
); 의 제어while
또는do
스테이트먼트; (옵션)의 각 표현for
스테이트먼트; (임의) 의 표현return
진술.완전 표현식의 끝은 시퀀스 포인트입니다.
주의:
int foo[] = { 1, 2, 3 }
그{ 1, 2, 3 }
part는 initializer의 괄호로 묶은 목록이며, 각 목록 뒤에는 시퀀스 포인트가 있습니다.- 완전 표현식은 다른 표현식이나 선언자의 일부가 아닌 표현식입니다.다음 각 항목은 완전한 표현입니다.Initializer, Expression 스테이트먼트 내의 표현, 선택 스테이트먼트의 제어 표현(
초기화는 initializer 목록 순서로 수행됩니다.
6.7.8 초기화
- 각 괄호로 둘러싸인 이니셜라이저 목록에는 현재 개체가 관련되어 있습니다.지정이 없는 경우 현재 객체의 하위 객체는 현재 객체의 유형(첨자 순서 증가 배열 요소, 선언 순서 구성 멤버 및 유니언의 첫 번째 명명된 멤버)에 따라 초기화됩니다. [...]
- 초기화는 특정 서브오브젝트에 대해 제공된 각 이니셜라이저가 동일한 서브오브젝트에 대해 이전에 열거된 이니셜라이저를 덮어쓰는 이니셜라이저 목록 순서로 수행되어야 합니다.명시적으로 초기화되지 않은 모든 서브오브젝트는 정적 저장 기간을 가진 오브젝트와 암묵적으로 동일하게 초기화되어야 합니다.
단, 이니셜라이저 식은 반드시 순서대로 평가되는 것은 아닙니다.
6.7.8 초기화
- 초기화 리스트 식 중 부작용이 발생하는 순서는 지정되지 않았습니다.
그러나, 아직 몇 가지 의문점이 남아 있습니다.
시퀀스 포인트는 관련이 있나요?기본 규칙은 다음과 같습니다.
6.5 식
- 이전 시퀀스 포인트와 다음 시퀀스 포인트 사이에서 객체는 식을 평가하여 저장된 값을 한 번만 수정해야 합니다.또한 이전 값은 저장할 값을 결정하기 위해서만 읽어야 한다.
a[2] = 1
는 표현이지만 초기화는 그렇지 않습니다.이것은 Annex J에 의해 약간 모순된다.
J.2 정의되지 않은 동작
- 2개의 시퀀스 포인트 사이에서 오브젝트가 여러 번 수정되거나 수정되어 저장되는 값을 결정하는 것 이외의 이전 값을 읽는다(6.5).
Annex J는 표현식에 의한 수정뿐만 아니라 모든 수정이 중요하다고 말한다.하지만 부속문서가 비규범적인 것을 고려하면, 우리는 아마 그것을 무시할 수 있을 것이다.
이니셜라이저 식과 관련하여 서브오브젝트의 초기화 시퀀스는 어떻게 이루어집니까?모든 이니셜라이저가 먼저 평가되고(어떤 순서로), 다음으로 서브오브젝트가 결과와 함께 초기화됩니까(이니셜라이저 목록 순서)?아니면 인터리브가 가능합니까?
생각합니다int a[5] = { a[2] = 1 }
는 다음과 같이 실행됩니다.
- 저장소:
a
는, 그 격납 블록이 입력되었을 때에 할당됩니다.그 내용은 현시점에서는 불확실하다. - (유일한) 이니셜라이저가 실행됩니다(
a[2] = 1
) 뒤에 시퀀스 포인트가 표시됩니다.이 가게1
에a[2]
및 반환1
. - 그거
1
초기화에 사용됩니다.a[0]
(첫 번째 이니셜라이저는 첫 번째 서브오브젝트를 초기화합니다).
하지만 여기엔 남은 요소들이 있기 때문에 흐릿해진다.a[1]
,a[2]
,a[3]
,a[4]
)는, 로 초기화됩니다.0
하지만 언제가 될지는 명확하지 않습니다.a[2] = 1
평가되고 있습니까?그렇다면,a[2] = 1
"승리"하여 덮어쓰게 됩니다.a[2]
그러나 제로 초기화 및 할당식 사이에 시퀀스 포인트가 없기 때문에 이 할당에 정의되지 않은 동작이 있습니까?시퀀스 포인트는 관련이 있습니까(위 참조)?아니면 모든 이니셜라이저를 평가한 후에 초기화가 제로입니까?그렇다면,a[2]
종국에는 이 될 것이다0
.
C기준은 여기서 일어나는 일을 명확하게 정의하지 않기 때문에 (누락으로) 동작이 정의되지 않았다고 생각합니다.
이해가 안 되는데, 왜?
a[0]
인쇄물1
대신0
?
아마도a[2]=1
초기화a[2]
첫 번째, 표현 결과는 초기화에 사용됩니다.a[0]
.
N2176(C17 드래프트):
6.7.9 초기화
- 초기화 리스트 표현식의 평가는 서로에 대해 결정적이지 않은 순서로 이루어지기 때문에 부작용이 발생하는 순서는 특정되지 않는다.154)
그래서 그 결과물들은1 0 0 0 0
또한 가능할 그랬습니다
결론:는 파리에 초기화된 변수를 수정하는 이니셜 라이저 쓰지 마세요.
나는 C11 표준과 그 결과가 지정되어 있지 않다고 말한다 이러한 행동에 달하고 나는 C18 이 지역에는 타당한 변경을 만들었다고 생각하지 않는다고 생각한다.
표준 언어 구문 분석하기는 쉽지 않다.표준의 관련 부분은§6.7.9 Initialization.그 구문:문서화되어 있습니다.
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designation
opt
initializer
initializer-list , designation
opt
initializer
designation:
designator-list =
designator-list:
designator
designator-list designator
designator:
[ constant-expression ]
. identifier
용어 중 하나가 assignment-expression이라는 점에 유의하십시오.a[2] = 1
는 반드시 할당식입니다.이는 비정적 지속시간을 가진 어레이의 이니셜라이저 내부에서 허용됩니다.
④ 정적 또는 스레드 저장시간을 갖는 물체의 이니셜라이저에서의 표현은 모두 상수 표현 또는 문자열 리터럴이어야 한다.
주요 단락 중 하나는 다음과 같습니다.
§19 초기화 순서는 이니셜라이저 목록 순서로 이루어지며, 각 이니셜라이저는 동일한 151)서브오브젝트에 대해 이전에 열거된 이니셜라이저보다 우선한다.명시적으로 초기화되지 않은 모든 서브오브젝트는 정적 저장시간을 갖는 오브젝트와 암묵적으로 동일해야 한다.
151) 서브오브젝트의 이니셜라이저는 덮어쓰기되어 해당 서브오브젝트를 초기화하기 위해 사용되지 않을 경우 전혀 평가되지 않을 수 있습니다.
또 다른 중요한 단락은 다음과 같습니다.
③ 초기화 리스트 표현식의 평가는 서로에 대해 부정적으로 배열되므로 부작용이 발생하는 순서를 특정하지 않는다.152)
152) 특히 평가순서는 서브오브젝트 초기화순서와 같을 필요는 없습니다.
23항은 질문의 표기법이 다음과 같은 것을 나타내고 있다고 확신합니다.
int a[5] = { a[2] = 1 };
를 지정하지 않은 동작으로 이어집니다.에의 할당a[2]
는 부작용이며, 표현식의 평가 순서는 서로에 대해 부정적으로 배열됩니다.따라서 특정 컴파일러가 이를 올바르게 또는 잘못 취급하고 있다고 주장할 수 있는 방법은 없다고 생각합니다.
나는 그렇게 믿어요.int a[5]={ a[2]=1 };
프로그래머가 자기 발에 총을 쏘는 것은 좋은 예다.
당신이 말한게 무슨 뜻이었는지...int a[5]={ [2]=1 };
이는 C99 지정 이니셜라이저 설정 요소 2에서 1로, 나머지는 0으로 설정됩니다.
네가 정말로 말하고자 하는 드문 경우라면int a[5]={ 1 }; a[2]=1;
그러면 재미있는 글쓰기가 되겠네요.어쨌든, 코드의 요점은 이것입니다만, 여기에 있는 일부의 지적에 의하면, 기입의 타이밍이 명확하지 않은 경우도 있습니다.a[2]
실제로 실행됩니다.여기서의 함정은 이다.a[2]=1
는 지정 이니셜라이저가 아니라 값이 1인 단순한 할당입니다.
제가 알기로는a[2]=1
값 1을 반환하여 코드가
int a[5]={a[2]=1} --> int a[5]={1}
int a[5]={1}
a[0]=1에 대한 값 할당
따라서 a[0]에 대해 1을 인쇄합니다.
예를들면
char str[10]={‘H’,‘a’,‘i’};
char str[0] = ‘H’;
char str[1] = ‘a’;
char str[2] = ‘i;
나는 퍼즐에 대해 짧고 간단한 답을 주려고 한다.int a[5] = { a[2] = 1 };
- 첫번째
a[2] = 1
설정되었습니다.즉, 어레이는 다음과 같이 표시됩니다.0 0 1 0 0
- 하지만 봐, 네가 한 짓이
{ }
대괄호(배열을 순서대로 초기화하기 위해 사용됨)는 첫 번째 값(즉,1
)로 설정합니다.a[0]
마치...int a[5] = { a[2] };
남아있을 것이다, 우리가 이미 가지고 있는 곳에a[2] = 1
. 결과 어레이는 다음과 같습니다.1 0 1 0 0
또 다른 예는 다음과 같습니다.int a[6] = { a[3] = 1, a[4] = 2, a[5] = 3 };
·순서는 다소 제멋대로이지만, 왼쪽에서 오른쪽으로 이동한다고 가정하면, 다음의 6개의 스텝으로 진행됩니다.
0 0 0 1 0 0
1 0 0 1 0 0
1 0 0 1 2 0
1 2 0 1 2 0
1 2 0 1 2 3
1 2 3 1 2 3
과제a[2]= 1
의 가치를 갖는 표현입니다.1
그리고 당신은 기본적으로int a[5]= { 1 };
(부작용으로)a[2]
할당되어 있다1
(도 마찬가지입니다).
언급URL : https://stackoverflow.com/questions/52307474/confusion-about-array-initialization-in-c
'programing' 카테고리의 다른 글
gradlew 명령을 찾을 수 없습니까? (0) | 2022.07.21 |
---|---|
vue.js 2.0 모달 컴포넌트에 근접하여 소품 리셋 (0) | 2022.07.21 |
왜 C-포크 폭탄은 Bash 폭탄처럼 작동하지 않는 거죠? (0) | 2022.07.21 |
글로벌 도우미 js 함수를 저장할 위치 (0) | 2022.07.21 |
범용 타입 파라미터를 문서화하기 위한 javadoc 태그가 있습니까? (0) | 2022.07.21 |