sourcetip

경계를 벗어나는 대신 초기화되지 않은 이유는 무엇입니까?

fileupload 2023. 7. 7. 21:02
반응형

경계를 벗어나는 대신 초기화되지 않은 이유는 무엇입니까?

아래 코드에서 이유는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

반응형