경계를 벗어나는 대신 초기화되지 않은 이유는 무엇입니까?
아래 코드에서 이유는b[9]
초기화되지 않은 상태로 유지할 수 있습니까?
#include <stdio.h>
int main(void)
{
char b[] = {'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!'};
printf("b[9] = %d\n", b[9]);
return 0;
}
컴파일러 호출:
% gcc -O2 -W -Wall -pedantic -c foo.c
foo.c: In function ‘main’:
foo.c:6:5: warning: ‘b[9]’ is used uninitialized in this function [-Wuninitialized]
printf("b[9] = %d\n", b[9]);
% gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.6) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
업데이트: 이제 이것은 이상합니다.
#include <stdio.h>
void foo(char *);
int main(void)
{
char b[] = {'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!'};
foo(&b[9]);
foo(&b[10]);
printf("b[9] = %d\n", b[9]);
printf("b[10] = %d\n", b[10]);
return 0;
}
이를 컴파일하면 예상되는 경고가 표시됩니다.
% gcc -O2 -W -Wall -pedantic -c foo.c
foo.c: In function ‘main’:
foo.c:9:5: warning: array subscript is above array bounds [-Warray-bounds]
foo(&b[10]);
^
foo.c:10:29: warning: array subscript is above array bounds [-Warray-bounds]
printf("b[9] = %d\n", b[9]);
^
foo.c:11:29: warning: array subscript is above array bounds [-Warray-bounds]
printf("b[10] = %d\n", b[10]);
갑자기 gcc는 그것이 무엇인지에 대한 경계 밖을 봅니다.
저는 이것이 여기에 해당될 수 있다고 믿습니다. 첫 번째 코드에서 GCC는 전체 문자 배열이 전혀 필요하지 않다는 것을 알아차립니다.b[9]
그래서 코드를 대체할 수 있습니다.
char b_9; // = ???
printf("b[9] = %d\n", b_9);
이는 완전히 합법적인 변환입니다. 어레이가 범위를 벗어나 액세스되었기 때문에 동작이 완전히 정의되지 않기 때문입니다.후자의 단계에서만 다음과 같은 대체 변수를 알 수 있습니다.b[9]
이 초기화되지 않고 진단 메시지를 실행합니다.
내가 왜 이걸 믿을까요?예를 들어 메모리에 있는 어레이의 주소를 참조하는 코드만 추가하면printf("%p\n", &b[8]);
어디서든 이제 어레이는 메모리에서 완전히 실현되며 컴파일러는 어레이 첨자가 어레이 경계를 초과하는지 진단합니다.
제가 더욱 흥미롭게 생각하는 것은 최적화가 활성화되지 않는 한 GCC가 경계 밖 액세스를 전혀 진단하지 않는다는 것입니다.이것은 당신이 새로운 프로그램을 작성할 때마다 디버깅 모드로 버그를 숨기는 대신 버그를 잘 볼 수 있도록 최적화를 사용하여 컴파일해야 한다는 것을 다시 제안합니다. ;)
독서에 대한 행동b[9]
또는b[10]
정의되지 않았습니다.
경고 텍스트가 약간 오해의 소지가 있지만 기술적으로 올바르지는 않지만 컴파일러가 경고를 실행하고 있습니다(그럴 필요는 없습니다).제 생각에, 그것은 꽤 영리합니다. (AC 컴파일러는 경계 밖 접근에 대한 진단을 발행할 필요가 없습니다.)
에 관하여&b[9]
컴파일러는 그것을 참조 해제하는 것이 허용되지 않으며, 그것을 평가해야 합니다.b + 9
배열의 끝을 지나 포인터를 설정할 수 있습니다.포인터를 다음으로 설정하는 동작&b[10]
정의되지 않았습니다.
몇 가지 추가 실험 결과입니다.
사용.char b[9]
대신에char b[]
차이가 없는 것처럼 보이지만, gcc는 여전히 동일하게 경고합니다.char b[9]
.
흥미롭게도, 다음 멤버를 통해 한 번 통과된 요소를 초기화합니다.struct
는 "초기화된" 경고를 완화하고 2) 배열 외부로의 등록에 대해 경고하지 않습니다.
#include <stdio.h>
typedef struct {
char c[9];
char d[9];
} TwoNines;
int main(void) {
char b[9] = { 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' };
printf("b[] size %zu\n", sizeof b);
printf("b[9] = %d\n", b[9]); // 'b[9]' is used uninitialized in this function [-Wuninitialized]
TwoNines e = { { 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' }, //
{ 'N', 'i', 'c', 'e', ' ', 'y', 'o', 'u', '!' } };
printf("e size %zu\n", sizeof e);
printf("e.c[9] = %d\n", e.c[9]); // No warning.
return 0;
}
산출량
b[] size 9
b[9] = 0
e size 18 // With 18, we know `e` is packed.
e.c[9] = 78 // 'N'
사항
- - - - - - - - c - - length= 0 - v - MP ...gcc - std= c11 - O3 - g3 - pedantic - Wall - Wextra - Wconversion - c - fmessage - length = 0 - v - MMD - MP...
gcc/gcc-7.3.0-2.i686
-O2로 코드를 컴파일할 때 예제의 사소한 부분이 이 변수를 최적화합니다.그래서 경고는 100% 정확합니다.
언급URL : https://stackoverflow.com/questions/51381625/why-uninitialized-instead-of-out-of-bounds
'sourcetip' 카테고리의 다른 글
도커 mongodb 구성 파일 (0) | 2023.07.17 |
---|---|
각도 주입토큰이 '주입 공급자 없음'을(를) 슬로우합니다.토큰' (0) | 2023.07.17 |
한 분기에 있는 커밋이 다른 분기에 없는 것을 확인하는 방법은 무엇입니까? (0) | 2023.07.07 |
"git diff"를 할 때 어떻게 나란히 diff를 얻을 수 있습니까? (0) | 2023.07.07 |
데이터 유형을 문자열로 변경하는 방법은 무엇입니까? (0) | 2023.07.07 |