왜 일부 플랫폼에서는 그렇지 않고 다른 플랫폼에서는 루프 종료에 사용합니까?


240

저는 최근에 C를 배우기 시작했고 C를 ​​주제로 수업을 들었습니다. 나는 현재 루프로 놀고 있고 설명하는 방법을 모르는 이상한 행동을하고 있습니다.

#include <stdio.h>

int main()
{
  int array[10],i;

  for (i = 0; i <=10 ; i++)
  {
    array[i]=0; /*code should never terminate*/
    printf("test \n");

  }
  printf("%d \n", sizeof(array)/sizeof(int));
  return 0;
}

Ubuntu 14.04를 실행하는 랩톱에서이 코드는 깨지지 않습니다. 완료됩니다. CentOS 6.6을 실행하는 학교 컴퓨터에서도 정상적으로 작동합니다. Windows 8.1에서 루프는 종료되지 않습니다.

더 이상한 점은 for루프 조건을 다음과 같이 편집 i <= 11하면 코드가 Ubuntu를 실행하는 랩톱에서만 종료 된다는 것 입니다. CentOS 및 Windows에서는 종료되지 않습니다.

누구든지 메모리에서 무슨 일이 일어나고 있으며 동일한 코드를 실행하는 다른 OS가 다른 결과를 낼 수 있는지 설명 할 수 있습니까?

편집 : for 루프가 범위를 벗어난 것을 알고 있습니다. 의도적으로하고 있습니다. OS와 컴퓨터마다 동작이 어떻게 다른지 알 수 없습니다.


147
배열을 오버런하기 때문에 정의되지 않은 동작이 발생합니다. 정의되지 않은 동작은 작동하는 것 등 모든 일이 발생할 수 있음을 의미합니다. 따라서 "코드를 종료해서는 안됩니다"는 올바른 예상이 아닙니다.
kaylum

37
정확히 C에 오신 것을 환영합니다. 배열에는 0에서 9까지 번호가 매겨진 10 개의 요소가 있습니다.
Yetti99

14
@JonCav 코드를 깨뜨 렸습니다. 코드가 깨진 정의되지 않은 동작이 발생합니다.
kaylum

50
글쎄요, 요점은 정의되지 않은 행동이 정확히 그런 것입니다. 당신은 그것을 확실하게 테스트 할 수 없으며 정의 된 것이 일어날 것임을 증명할 수 없습니다. 아마도 Windows 컴퓨터에서 진행되고있는 일은 변수 i가 끝나는 직후에 저장 array되어 있고을 (를) 덮어 쓰고 있다는 것입니다 array[10]=0;. 동일한 플랫폼에서 최적화 된 빌드에서는 그렇지 않을 수 있으며, 이는 i레지스터에 저장 되어 메모리에서 전혀 참조하지 않을 수 있습니다 .
paddy

46
예측 불가능 성은 정의되지 않은 행동의 기본 속성이기 때문입니다. 당신은 이것을 이해해야합니다 ... 절대적으로 모든 베팅이 해제되었습니다.
paddy

답변:


356

Ubuntu 14.04를 실행하는 랩톱 에서이 코드는 실행을 중단하지 않습니다. CentOS 6.6을 실행하는 학교 컴퓨터에서도 정상적으로 작동합니다. Windows 8.1에서 루프는 종료되지 않습니다.

더 이상한 점은 for루프 조건을 다음과 같이 편집하면 i <= 11Ubuntu를 실행하는 랩톱에서만 코드가 종료 된다는 것 입니다. CentOS와 Windows는 종료되지 않습니다.

방금 메모리 스톰 핑을 발견했습니다. 자세한 내용은 여기를 참조하십시오 : “메모리 스톰프”란 무엇입니까?

당신이 할당 할 때 int array[10],i;, 그 변수들은 메모리로 들어갑니다 (특히, 그것들은 함수와 관련된 메모리 블록 인 스택에 할당됩니다). array[]그리고 i메모리에서 서로 아마 인접. 윈도우 8.1에 보인다 i에 위치해 있습니다 array[10]. CentOS에서는 i에 있습니다 array[11]. 그리고 우분투에서는 어느 곳에도 없습니다 (아마도 array[-1]?에 있습니다).

이 디버깅 문을 코드에 추가하십시오. 반복 10 또는 11에서을 array[i]가리 킵니다 i.

#include <stdio.h>
 
int main() 
{ 
  int array[10],i; 
 
  printf ("array: %p, &i: %p\n", array, &i); 
  printf ("i is offset %d from array\n", &i - array);

  for (i = 0; i <=11 ; i++) 
  { 
    printf ("%d: Writing 0 to address %p\n", i, &array[i]); 
    array[i]=0; /*code should never terminate*/ 
  } 
  return 0; 
} 

6
고마워! 그것은 정말 꽤 설명했습니다. Windows에서는 i가 어레이에서 오프셋 10이면 CentOS와 Ubuntu에서는 -1이라고 말합니다. 이상한 점은 디버거 코드를 주석 처리하면 CentOS가 코드를 실행할 수 없지만 (멈춤) 디버깅 코드와 함께 실행됩니다. C는 지금까지 매우 언어 인 것 같습니다. X_x
JonCav

12
array[10]예를 들어, @JonCav "중지"는 스택 프레임 을 파괴하기 위해 쓰면 발생할 수 있습니다 . 디버깅 출력 유무에 관계없이 코드는 어떻게 다를 수 있습니까? 주소 i가 필요하지 않으면 컴파일러 최적화 할 수i 있습니다. 레지스터에, 따라서 스택의 메모리 레이아웃을 변경 ...
Hagen von Eitzen

2
나는 그것이 멈추지 않는다고 생각합니다. 메모리에서 루프 카운터를 다시로드하기 때문에 무한 루프에 있다고 생각합니다 array[10]=0. 어떤 종류의 메모리 액세스가 다른 메모리와 겹칠 수 있다고 가정하는 별칭 지정 규칙 주소를 사용하지 않는 로컬 변수로 컴파일러는 별칭을 지정하지 않는다고 가정 할 수 있다고 생각합니다. 배열의 정의는 정의되지 않은 동작입니다. 이에 따라 항상 피하도록 노력하십시오
Peter Cordes

4
또 다른 대안은 최적화 컴파일러가 (질문의 원래 코드에서) 관찰 가능한 효과가 없으므로 배열을 완전히 제거한다는 것입니다. 따라서 결과 코드는 상수 문자열을 11 번 인쇄 한 다음 일정한 크기를 인쇄하여 오버플로를 완전히 알 수 없게 만들 수 있습니다.
Holger

9
@JonCav 나는 일반적으로 메모리 관리에 대해 더 알 필요 가없고 정의되지 않은 코드를 쓰지 말고, 특히 배열의 끝을 지나서 쓰지 말아야 한다는 것을 알고 싶다 ...
T. Kiley

98

버그는 다음 코드들 사이에 있습니다.

int array[10],i;

for (i = 0; i <=10 ; i++)

array[i]=0;

array10 개의 요소 만 있기 때문에 마지막 반복 array[10] = 0;에서 버퍼 오버 플로우가 발생합니다. 버퍼 오버플로는 정의되지 않은 동작 입니다. 즉, 하드 드라이브를 포맷하거나 악마가 코에서 날아갈 수 있습니다.

모든 스택 변수가 서로 인접하여 배치되는 것이 일반적입니다. 쓰기 i위치에 있으면 array[10]UB가로 재설정 i되어 0종료되지 않은 루프가 발생합니다.

수정하려면 루프 조건을로 변경하십시오 i < 10.


6
Nitpick : 루트 (또는 동등한 장치)로 실행하지 않는 한 시판되는 모든 정상적인 OS에서 하드 드라이브를 실제로 포맷 할 수 없습니다.
Kevin

26
UB를 호출 할 때 @Kevin은 온전함을 주장하지 않습니다.
o11c

7
코드가 제정신인지 여부는 중요하지 않습니다. OS는 그렇게 할 수 없습니다.
Kevin

2
@Kevin 하드 드라이브를 포맷 한 예제는 오래 전에 시작되었습니다. 심지어 시간의 유닉스 (C가 시작된 곳)조차도 당신이 그런 일을 할 수있게되어 매우 기뻤습니다. 그리고 오늘날에도 많은 배포판은 rm -rf /여러분이 뿌리가 아닌 경우에도 모든 것을 삭제할 수있게 해줄 것입니다. 물론 전체 드라이브를 "포맷"하지만 여전히 모든 데이터를 파괴합니다. 아야.
Luaan

5
@Kevin이지만 정의되지 않은 동작은 OS 취약성을 악용 한 다음 새로운 하드 디스크 드라이버를 설치하기 위해 자체적으로 상승한 다음 드라이브 스크러빙을 시작할 수 있습니다.
ratchet freak

38

루프의 마지막 실행은 무엇입니까? array[10]에 쓰지만 배열에는 0에서 9까지 번호가 지정된 10 개의 요소 만 있습니다. C 언어 사양에서는 이것이 "정의되지 않은 동작"이라고 말합니다. 이것이 실제로 의미하는 것은 프로그램이 int메모리 바로 뒤에 array있는 크기의 메모리 에 쓰려고 시도한다는 것 입니다. 그러면 실제로 발생하는 내용에 따라 달라지며 운영 체제뿐만 아니라 컴파일러, 컴파일러 옵션 (예 : 최적화 설정), 프로세서 아키텍처, 주변 코드에 따라 달라집니다. 예를 들어 주소 공간 무작위 화 (아마이 장난감 예제에는 없지만 실제 상황에서는 발생) 로 인해 실행에 따라 달라질 수도 있습니다 . 몇 가지 가능성은 다음과 같습니다.

  • 위치가 사용되지 않았습니다. 루프가 정상적으로 종료됩니다.
  • 위치는 값이 0 인 무언가에 사용되었습니다. 루프가 정상적으로 종료됩니다.
  • 위치에 함수의 반환 주소가 포함되었습니다. 루프는 정상적으로 종료되지만 프로그램은 주소 0으로 점프하려고하기 때문에 충돌합니다.
  • 위치는 변수를 포함합니다 i. i0에서 다시 시작 하기 때문에 루프가 종료되지 않습니다 .
  • 위치에 다른 변수가 있습니다. 루프는 정상적으로 종료되지만 "흥미로운"일이 발생합니다.
  • 위치가 유효하지 않은 메모리 주소입니다. 예를 들어 array가상 메모리 페이지의 끝에 있고 다음 페이지가 매핑되지 않기 때문입니다.
  • 악마는 코에서 날아갑니다 . 다행히도 대부분의 컴퓨터에는 필수 하드웨어가 없습니다.

Windows에서 관찰 한 것은 컴파일러가 변수 i를 메모리 바로 다음에 메모리 에 배치하기로 결정 했기 때문에에 array[10] = 0할당했습니다 i. 우분투와 CentOS에서는 컴파일러가 설치되지 않았습니다 i. 거의 모든 C 구현은 하나의 주요 예외를 제외 하고 메모리 에서 로컬 변수를 메모리 스택으로 그룹화합니다 . 일부 로컬 변수는 레지스터 에 완전히 배치 될 수 있습니다 . 변수가 스택에 있더라도 변수의 순서는 컴파일러에 의해 결정되며 소스 파일의 순서뿐만 아니라 유형에 따라 달라질 수 있습니다 (구멍을 남길 수있는 정렬 제약 조건에 메모리 낭비를 피하기 위해) , 컴파일러 이름, 컴파일러 내부 데이터 구조 등에 사용되는 일부 해시 값

컴파일러에서 수행하기로 결정한 작업을 찾으려면 어셈블러 코드를 보여 주도록 지시 할 수 있습니다. 아, 그리고 어셈블러를 해독하는 법을 배우십시오 (작성하는 것보다 쉽습니다). GCC (및 특히 유닉스 세계의 다른 컴파일러) -S를 사용하면 바이너리 대신 어셈블러 코드를 생성 하는 옵션 을 전달하십시오 . 예를 들어, 다음은 최적화 옵션 -O0(최적화 안 함)을 사용하여 amd64에서 GCC로 컴파일하는 루프에 대한 어셈블러 스 니펫입니다 (주석이 수동으로 추가됨).

.L3:
    movl    -52(%rbp), %eax           ; load i to register eax
    cltq
    movl    $0, -48(%rbp,%rax,4)      ; set array[i] to 0
    movl    $.LC0, %edi
    call    puts                      ; printf of a constant string was optimized to puts
    addl    $1, -52(%rbp)             ; add 1 to i
.L2:
    cmpl    $10, -52(%rbp)            ; compare i to 10
    jle     .L3

여기서 변수 i는 스택 상단에서 52 바이트 아래에있는 반면 배열은 스택 상단에서 48 바이트 아래에서 시작합니다. 따라서이 컴파일러 i는 배열 바로 앞에 위치 합니다. 에 쓰면 덮어 쓰게 i됩니다 array[-1]. 로 변경 array[i]=0하면 array[9-i]=0이러한 특정 컴파일러 옵션을 사용하여이 특정 플랫폼에서 무한 루프를 얻게됩니다.

이제로 프로그램을 컴파일하자 gcc -O1.

    movl    $11, %ebx
.L3:
    movl    $.LC0, %edi
    call    puts
    subl    $1, %ebx
    jne     .L3

더 짧아요! 컴파일러는 스택 위치 할당을 거부했을 뿐 아니라 i레지스터에 저장되어있을 ebx뿐 아니라 array요소를 설정하기 위한 메모리를 할당 하거나 요소를 설정하기위한 코드를 생성하지 않았습니다. 이제까지 사용되었습니다.

이 예제를보다 잘 설명하기 위해 컴파일러에서 최적화 할 수없는 것을 제공하여 배열 할당이 수행되도록합니다. 별도의 컴파일, 컴파일러가 (이 링크시 최적화 된 않는 다른 파일에 어떻게되는지 모르기 때문에 - 그 작업을 쉽게 수행하는 방법은 다른 파일에서 배열을 사용하는 것 gcc -O0또는 gcc -O1하지 않습니다). 다음을 포함하는 소스 파일 use_array.c을 만듭니다.

void use_array(int *array) {}

소스 코드를

#include <stdio.h>
void use_array(int *array);

int main()
{
  int array[10],i;

  for (i = 0; i <=10 ; i++)
  {
    array[i]=0; /*code should never terminate*/
    printf("test \n");

  }
  printf("%zd \n", sizeof(array)/sizeof(int));
  use_array(array);
  return 0;
}

와 컴파일

gcc -c use_array.c
gcc -O1 -S -o with_use_array1.c with_use_array.c use_array.o

이번에는 어셈블러 코드가 다음과 같습니다.

    movq    %rsp, %rbx
    leaq    44(%rsp), %rbp
.L3:
    movl    $0, (%rbx)
    movl    $.LC0, %edi
    call    puts
    addq    $4, %rbx
    cmpq    %rbp, %rbx
    jne     .L3

이제 배열은 스택에서 위에 44 바이트입니다. 무엇에 대해 i? 어디에도 나타나지 않습니다! 그러나 루프 카운터는 레지스터에 유지됩니다 rbx. 정확히는 i아니지만의 주소입니다 array[i]. 컴파일러는의 값이 i직접 사용 된 적이 없기 때문에 루프를 실행할 때마다 0을 저장할 위치를 계산하기 위해 산술을 수행 할 필요가 없다고 결정했습니다 . 대신 해당 주소는 루프 변수이며, 경계를 결정하기위한 산술은 컴파일 타임 (배열 요소 당 11 반복에 4 바이트를 곱하여 44를 얻음)과 런타임에 한 번 또는 루프가 시작되기 전에 한 번만 수행되었습니다 ( 빼기를 수행하여 초기 값을 얻습니다).

이 매우 간단한 예에서도 컴파일러 옵션 변경 (최적화 켜기) 또는 사소한 변경 ( array[i]~ array[9-i]) 또는 관련이없는 것으로 변경 (호출 추가 use_array)이 실행 프로그램이 생성하는 것과 큰 차이를 만드는 방법을 보았습니다. 컴파일러에 의해 수행됩니다. 컴파일러 최적화는 정의되지 않은 동작을 호출하는 프로그램에서 직관적이지 않은 것처럼 보일 수있는 많은 작업을 수행 할 수 있습니다 . 이것이 정의되지 않은 행동이 완전히 정의되지 않은 이유입니다. 실제 프로그램에서 트랙에서 약간 벗어나면 숙련 된 프로그래머조차도 코드의 기능과 수행해야 할 작업 간의 관계를 이해하기가 매우 어려울 수 있습니다.


25

Java와 달리 C는 배열 경계 검사를 수행하지 않습니다. 즉, ArrayIndexOutOfBoundsException배열 인덱스가 유효한지 확인하는 작업은 프로그래머에게 맡겨집니다. 이 작업을 의도적으로 수행하면 정의되지 않은 동작이 발생하여 어떤 일이 발생할 수 있습니다.


배열의 경우 :

int array[10]

인덱스는 0~ 범위에서만 유효 합니다 9. 그러나 다음을 시도하고 있습니다.

for (i = 0; i <=10 ; i++)

array[10]여기에 액세스 하여 조건을i < 10


6
의도적으로하지 않으면 정의되지 않은 동작이 발생합니다. 컴파일러는 말할 수 없습니다! ;-)
Toby Speight 2016 년

1
매크로를 사용하여 오류를 경고로 표시하십시오. #define UNINTENDED_MISTAKE (EXP) printf ( "경고 :"#EXP "실수 \ n");
lkraider

1
내 말은, 만약 당신이 고의로 실수를 저지르고 있다면, 그 실수를 식별하고 정의되지 않은 행동을 피하는 것이 안전하도록 만들 수 있습니다; D
lkraider

19

경계 위반이 있으며 비 종료 플랫폼에서는 i루프가 끝날 때 실수 로 0으로 설정 되어 다시 시작되도록 믿습니다 .

array[10]유효하지 않다; 그것은 10 개 요소를 포함 array[0]통해 array[9], 그리고 array[10]11이다. 루프는 다음과 같이 전에 중지되도록 작성해야 10합니다.

for (i = 0; i < 10; i++)

어디 array[10]땅은 구현 정의하고, 재미있게, 당신의 플랫폼이에, 그것은에 토지 i이러한 플랫폼은 분명히 바로 뒤에 배치하는 array. i는 0으로 설정되고 루프는 영원히 계속됩니다. 다른 플랫폼의 경우, i이전 array에 위치 하거나 array그 뒤에 패딩이있을 수 있습니다.


나는 여전히 유효한 위치이기 때문에 valgrind가 이것을 잡을 수는 없다고 생각하지만 ASAN은 가능합니다.
o11c

13

당신 은 인덱스 를 가지고 int array[10]있음 array을 선언 0합니다 9( 10보유 할 수있는 총 정수 요소). 그러나 다음 루프는

for (i = 0; i <=10 ; i++)

루프 는 시간 010의미 11합니다. 따라서 i = 10버퍼 오버플로가 발생하여 정의되지 않은 동작이 발생 합니다.

따라서 이것을 시도하십시오 :

for (i = 0; i < 10 ; i++)

또는,

for (i = 0; i <= 9 ; i++)

7

에 정의되어 있지 array[10]않으며 앞에서 설명한대로 정의되지 않은 동작 을 제공합니다 . 다음과 같이 생각하십시오.

식료품 카트에 10 개의 품목이 있습니다. 그들은:

0 : 시리얼
1 상자 : 빵
2 : 우유
3 : 파이
4 : 계란
5 : 케이크
6 : 2 리터의 소다
7 : 샐러드
8 : 햄버거
9 : 아이스크림

cart[10]정의되지 않았으며 일부 컴파일러에서 범위를 벗어난 예외가 발생할 수 있습니다. 그러나 많은 것은 분명히 그렇지 않습니다. 명백한 11 번째 항목은 실제로 장바구니에 없는 항목입니다 . 열한 번째 항목은 내가 "폴더리스트 항목"이라고 부릅니다. 그것은 존재하지 않았지만 거기에있었습니다.

어떤 컴파일러가 제공하는 이유 i의 인덱스를 array[10]하거나 array[11]또는 array[-1]때문에 당신의 초기화 / 선언문이다. 일부 컴파일러는 이것을 다음과 같이 해석합니다.

  • "10 개 블록 할당 int에 대한들 array[10]과 다른 int. 블록 , 쉽게하기 위해 서로 바로 옆에 넣어."
  • 이전과 동일하지만 공백을 한두 칸 이동하면을 array[10]가리 키지 않습니다 i.
  • 이전과 동일한 작업을 수행하지만, 할당 i에서 array[-1], 또는 OS가 처리 할 수 있기 때문에 완전히 다른 지점에이를 할당하고 그것의 (배열의 인덱스는 할 수없는, 또는 음수가 될 수 없습니다해야하기 때문에) 안전합니다.

일부 컴파일러는 작업이 더 빨라지기를 원하고 일부 컴파일러는 안전을 선호합니다. 상황에 관한 모든 것입니다. 예를 들어 고대 BREW OS (기본 전화의 OS) 용 앱을 개발하는 경우 안전에 신경 쓰지 않습니다. iPhone 6을 개발 중이라면 무엇이든 빠르게 실행할 수 있으므로 안전에 중점을 둘 필요가 있습니다. (Apple의 App Store 지침을 읽거나 Swift 및 Swift 2.0 개발에 대해 읽어 보셨습니까?)


참고 : 목록을 입력하여 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9"가되었지만 SO의 마크 업 언어가 주문한 목록의 위치를 ​​수정했습니다.
DDPWNAGE

6

크기가 10 인 배열을 만들었으므로 for 루프 조건은 다음과 같아야합니다.

int array[10],i;

for (i = 0; i <10 ; i++)
{

현재 메모리를 사용하여 할당되지 않은 위치에 액세스하려고 array[10]하는데 정의되지 않은 동작 이 발생하고 있습니다 . 정의되지 않은 동작은 프로그램이 결정되지 않은 방식으로 작동하므로 각 실행마다 다른 출력을 줄 수 있습니다.


5

글쎄, C 컴파일러는 전통적으로 경계를 확인하지 않습니다. 프로세스에 "포함되지 않은"위치를 참조 할 경우 분할 오류가 발생할 수 있습니다. 그러나 로컬 변수는 스택에 할당되며 메모리가 할당되는 방식에 따라 배열 바로 옆의 영역 ( array[10])이 프로세스의 메모리 세그먼트에 속할 수 있습니다. 따라서 분할 오류 트랩이 발생하지 않으며 이것이 바로 사용자가 경험하는 것입니다. 다른 사람들이 지적했듯이 이것은 C에서 정의되지 않은 동작이며 코드가 불규칙한 것으로 간주 될 수 있습니다. C를 배우고 있으므로 코드에서 경계를 확인하는 습관을들이는 것이 좋습니다.


4

a[10]실제로 덮어 쓰기를 시도하기 위해 메모리가 배치 될 수있는 가능성을 넘어서, i최적화 컴파일러는 i코드에 먼저 액세스하지 않고 루프 테스트에 10보다 큰 값으로 도달 할 수 없다고 결정할 수도 있습니다. 존재하지 않는 배열 요소 a[10].

해당 요소에 액세스하려는 시도는 정의되지 않은 동작이므로 컴파일러는 해당 시점 이후에 프로그램이 수행 할 수있는 작업에 대한 의무가 없습니다. 보다 구체적으로, 컴파일러는 루프 인덱스가 10보다 클 경우 루프 인덱스를 검사하기위한 코드를 생성 할 의무가 없으므로,이를 검사하기위한 코드를 생성 할 의무는 없습니다. 대신 <=10테스트가 항상 참 이라고 가정 할 수 있습니다. 코드를 a[10]작성하지 않고 읽을 경우에도 마찬가지 입니다.


3

당신으로 반복은 과거 때 i==9실제로 위치한 '배열 항목'제로 지정 배열 과거 일부 다른 데이터를 overwritnig 것 정도. 아마도 당신 i은 뒤에 위치한 변수 를 덮어 쓸 것입니다 a[]. 당신은 단순히 그런 식으로 리셋 i제로에 변수를 따라서 그리고 루프를 다시 시작합니다.

i루프에서 인쇄하면 스스로 알아낼 수 있습니다 .

      printf("test i=%d\n", i);

그냥 대신

      printf("test \n");

물론 그 결과는 변수에 대한 메모리 할당에 크게 좌우되며, 결과적으로 컴파일러 및 해당 설정에 따라 달라 지므로 일반적으로 정의되지 않은 동작 이므로 다른 컴퓨터 또는 다른 운영 체제 또는 다른 컴파일러의 결과가 다를 수 있습니다.


0

에러는 부분 배열 [10]에있다. w / c는 또한 i의 주소이다 (int array [10], i;). array [10]이 0으로 설정되면 i는 0입니다. w / c는 전체 루프를 재설정하고 무한 루프를 발생시킵니다. array [10]이 0-10 사이이면 무한 루프가 발생합니다. 올바른 루프는 (i = 0; i <10; i ++) {...} int array [10], i; (i = 0; i <= 10; i ++) 배열 [i] = 0;


0

나는 위에서 찾을 수없는 것을 제안 할 것이다.

array [i] = 20을 할당 해보십시오;

나는 이것이 어디에서나 코드를 끝내야한다고 생각한다. (i <= 10 또는 ll을 유지한다면)

이것이 실행되면 여기에 지정된 답변이 이미 올바른지 확실하게 결정할 수 있습니다 [예를 들어 메모리를 소모하는 것과 관련된 답변].


-9

여기에 두 가지 잘못된 점이 있습니다. int i는 실제로 스택에서 볼 수있는 배열 요소 array [10]입니다. 인덱싱이 실제로 array [10] = 0을 만들도록 허용했기 때문에 루프 인덱스 i는 절대로 10을 초과하지 않습니다 for(i=0; i<10; i+=1).

i ++는 K & R 이 '나쁜 스타일'이라고 부릅니다. i가 1이 아닌 i의 크기만큼 증가합니다. i ++는 포인터 수학 용이고 i + = 1은 대 수용입니다. 이것은 컴파일러에 따라 다르지만 이식성에 대한 좋은 규칙은 아닙니다.


5
-1 완전히 잘못되었습니다. 변수 i는 배열 요소가 아니므 a[10]로 컴파일러가 스택 바로 뒤에 스택에 배치해야 할 의무 나 제안이 없습니다 a[]. 배열 앞에 배치하거나 추가 공간으로 분리 할 수도 있습니다. 메인 메모리 외부, 예를 들어 CPU 레지스터에 할당 될 수도 있습니다. 또한 ++정수가 아닌 포인터를위한 것입니다. 완전히 잘못된 것은 'i ++가 i의 크기만큼 i를 증가시킵니다'입니다 – 언어 정의에서 연산자 설명을 읽으십시오!
CiaPan

그렇기 때문에 일부 플랫폼에서는 작동하지만 다른 플랫폼에서는 작동하지 않습니다. 왜 그것이 Windows에서 영원히 반복되는지에 대한 유일한 논리적 설명입니다. I ++와 관련하여 정수가 아닌 포인터 수학입니다. 성경을 읽으십시오 ... 'C 프로그래밍 언어'. Kernigan과 Ritche의 필자가 사인을 원하고 1981 년부터 c로 프로그래밍 한
적이 있다면

1
OP로 소스 코드를 읽고 변수 선언을 찾으십시오 . i이는 int유형입니다. 포인터 가 아닌 정수입니다 . 받는 인덱스로서 사용되는 정수 array.
CiaPan

1
저도 그렇게했기 때문에 제가 댓글을 달았습니다. 컴파일러에 스택 검사가 포함되어 있지 않으면이 경우 I = 10 일 때 실제로 일부 인덱스에서는 배열 인덱스를 참조하고 스택 영역의 경계 내에있는 스택 참조로 중요하지 않습니다. 컴파일러는 바보를 고칠 수 없습니다. 컴파일은 이것이 보이는 것처럼 수정을 할 수 있지만 c 프로그래밍 언어의 순수한 해석은이 규칙을 지원하지 않으며 OP가 휴대용 결과가 아닌 결과를 말한 것처럼 말입니다.
SkipBerne

@SkipBerne : 더 부정적인 점으로 "보상"되기 전에 답을 삭제하는 것을 고려하십시오.
피터 VARGA
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.