programing

루프 출구에서는 다른 플랫폼에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는

sourcetip 2022. 7. 14. 23:01
반응형

루프 출구에서는 다른 플랫폼에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는 루프 출구에서는

저는 최근에 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마다 결과가 다른 이유를 설명할 수 있는 사람이 있습니까?

EDIT: for 루프가 범위를 벗어났다는 것을 알고 있습니다.일부러 하는 거예요.OS와 컴퓨터에 따라 동작이 어떻게 다른지 알 수 없습니다.

Ubuntu 14.04를 실행하고 있는 노트북에서는 이 코드가 끊어지지 않고 실행됩니다.CentOS 6.6을 실행하는 우리 학교 컴퓨터에서도 정상적으로 작동합니다.Windows 8.1 에서는, 루프는 종료하지 않습니다.

더 이상한 것은 의 조건을 편집하는 경우입니다.for루프처:i <= 11이 코드는 Ubuntu를 실행하는 노트북에서만 종료됩니다.CentOS 및 Windows는 종료되지 않습니다.

당신은 방금 기억의 뭉클함을 발견했어요.자세한 내용은 여기를 참조하십시오.'메모리 스톰프'란?

할당 시int array[10],i;이러한 변수는 메모리에 들어갑니다(구체적으로는 함수와 관련된 메모리 블록인 스택에 할당됩니다). array[]그리고.i메모리에서는 아마 서로 인접해 있을 것입니다.Windows 8.1에서는i설치 장소:array[10]CentOS에서는i설치 장소:array[11]Ubuntu에서는 어느 지점에도 없습니다(아마도,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; 
} 

당신이 선언하다int array[10]수단array인덱스가 있다0로.9(합계)10유지할 수 있는 정수 요소).하지만 다음 루프는

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

루프가 발생하다0로.10수단11시간이요. 그러므로 언제가i = 10버퍼가 오버플로하여 정의되지 않은 동작이 발생합니다.

그럼 이렇게 해 보세요.

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

또는,

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

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

int array[10],i;

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

array[i]=0;

부터array마지막 반복에서는 10개의 요소만 있습니다.array[10] = 0;버퍼 오버플로우입니다.버퍼 오버플로우는 정의되지 않은 동작입니다.즉, 하드 드라이브를 포맷하거나 코에서 악마가 튀어나올 수 있습니다.

모든 스택 변수가 서로 인접하게 배치되는 것은 매우 일반적입니다.한다면i설치 장소array[10]에 기입하면, UB 가 리셋 됩니다.i로.0따라서 종단되지 않은 루프가 발생합니다.

수정하려면 루프 조건을 다음과 같이 변경합니다.i < 10.

경계 위반이 있습니다.종단되지 않은 플랫폼에서는 실수로i루프의 끝에 0이 되도록 하기 위해 다시 시작합니다.

array[10]유효하지 않습니다.10개의 요소가 포함되어 있습니다.array[0]통해.array[9],그리고.array[10]11일입니다.루프가 정지하기 전에 기록되어야 합니다. 10다음과 같습니다.

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

어디에array[10]lands는 구현에 정의되어 있으며, 흥미롭게도 2개의 플랫폼에서는i이 플랫폼들은 그 직후에 배치되어 있는 것으로 보입니다.array.i루프는 계속됩니다.다른 플랫폼에서는i앞에 위치할 수 있다array, 또는array뒤에 패딩이 있을 수 있어요.

루프의 마지막 실행이 무엇이 되어야 하는지, 당신은 편지를 씁니다array[10]단, 배열에는 0 ~9의 번호가 매겨진 요소는 10개뿐입니다.C 언어 사양에서는, 이것은 「정의되지 않은 동작」이라고 하고 있습니다.이것이 실제로 의미하는 것은 프로그램이 다음 항목에 쓰기를 시도한다는 것입니다.int- 바로 뒤에 있는 메모리 크기array기억 속에.그 후 어떤 일이 일어나느냐는 운영체제뿐만 아니라 컴파일러, 컴파일러 옵션(최적화 설정 등), 프로세서 아키텍처, 주변 코드 등에 따라 달라집니다.주소 공간 랜덤화(아마 이 장난감 예에서는 아니지만 실제에서는 실제로 발생할 수 있음)로 인해 실행마다 다를 수 있습니다.다음과 같은 가능성이 있습니다.

  • 위치는 사용되지 않았습니다.루프는 정상적으로 종료됩니다.
  • 그 장소는 우연히 값이 0인 무언가에 사용되었습니다.루프는 정상적으로 종료됩니다.
  • 위치에는 함수의 반환 주소가 포함되어 있습니다.루프는 정상적으로 종료되지만 프로그램은 주소 0으로 점프하려고 하기 때문에 크래시합니다.
  • 위치에는 변수가 포함되어 있습니다.i루프는 종료되지 않습니다.i는 0으로 재기동합니다.
  • 위치에는 다른 변수가 포함되어 있습니다.루프는 정상적으로 종료되지만, 그 후에 「흥미로운」일이 발생합니다.
  • 위치가 잘못된 메모리 주소입니다. 예를 들어 다음과 같습니다.array가상 메모리 페이지의 바로 끝에 있으며 다음 페이지는 매핑되지 않습니다.
  • 네 코에서 악마가 튀어나온다.다행히 대부분의 컴퓨터에는 필요한 하드웨어가 없습니다.

당신이 Windows에서 관찰한 것은 컴파일러가 변수를 배치하기로 결정했다는 것입니다.i메모리 내의 어레이 직후에 표시되므로array[10] = 0결국에 할당되었다iUbuntu와 CentOS에서는 컴파일러가 다음 위치에 배치되지 않았습니다.i거의 모든 C 구현은 메모리 스택에 로컬 변수를 그룹화합니다. 단, 일부 로컬 변수는 완전히 레지스터에 배치될 수 있습니다.변수가 스택 상에 있더라도 변수의 순서는 컴파일러에 의해 결정되며, 이는 소스 파일의 순서뿐만 아니라 (구멍을 남기는 정렬 제약에 대한 메모리 낭비를 피하기 위해), 이름, 컴파일러의 내부 데이터 구조에 사용되는 해시 값 등에 따라 달라질 수 있습니다.

컴파일러가 무엇을 하기로 결정했는지 알고 싶다면 어셈블러 코드를 표시하도록 컴파일러에 지시할 수 있습니다.아, 그리고 조립자를 해독하는 법을 배웁니다(그건 쓰는 것보다 쉬워요.GCC(및 일부 다른 컴파일러, 특히 Unix 월드의 경우)에서는 옵션을 전달합니다.-S2진수 대신 어셈블러 코드를 생성합니다.예를 들어 amd64에서 GCC를 사용하여 최적화 옵션을 사용하여 컴파일한 루프의 어셈블러 스니펫을 다음에 나타냅니다.-O0(최적화 없음), 코멘트를 수동으로 추가합니다.

.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을 저장할 위치를 계산하기 위해 계산을 수행하는 것은 의미가 없습니다.대신 이 주소는 루프 변수이며 경계를 결정하기 위한 연산은 컴파일 시(44를 얻기 위해 어레이 요소당 4바이트씩 11회 반복)와 실행 시에도 실행되지만 루프가 시작되기 전에 완전히 실행됩니다(초기값을 얻기 위해 감산 실행).

이 간단한 예에서도 컴파일러 옵션을 변경하는 방법(최적화를 유효하게 하는 방법)과 사소한 것을 변경하는 방법을 확인했습니다.array[i]로.array[9-i]또는 전혀 관련이 없는 것을 변경(콜을 에 전송)할 수도 있습니다.use_array)는 컴파일러에 의해 생성된 실행 프로그램의 동작에 큰 차이를 가져올 수 있습니다.컴파일러의 최적화는 정의되지 않은 동작을 호출하는 프로그램에서 의도하지 않은 처럼 보일 수 있는 많은 작업을 수행할 수 있습니다.그것이 정의되지 않은 행동이 완전히 정의되지 않은 상태로 남아 있는 이유입니다.실제 프로그램에서는 트랙에서 아주 약간 벗어나게 되면 숙련된 프로그래머라도 코드가 무엇을 하고 무엇을 했어야 하는지를 이해하는 것은 매우 어려울 수 있습니다.

It is undefined at array[10], and gives undefined behavior as described before. Think about it like this:

I have 10 items in my grocery cart. They are:

0: A box of cereal
1: Bread
2: Milk
3: Pie
4: Eggs
5: Cake
6: A 2 liter of soda
7: Salad
8: Burgers
9: Ice cream

cart[10] is undefined, and may give an out of bounds exception in some compilers. But, a lot apparently don't. The apparent 11th item is an item not actually in the cart. The 11th item is pointing to, what I'm going to call, a "poltergeist item." It never existed, but it was there.

Why some compilers give i an index of array[10] or array[11] or even array[-1] is because of your initialization/declaration statement. Some compilers interpret this as:

  • "Allocate 10 blocks of ints for array[10] and another int block. to make it easier, put them right next to each other."
  • Same as before, but move it a space or two away, so that array[10] doesn't point to i.
  • Do the same as before, but allocate i at array[-1] (because an index of an array can't, or shouldn't, be negative), or allocate it at a completely different spot because the OS can handle it, and it's safer.

Some compilers want things to go quicker, and some compilers prefer safety. It's all about the context. If I was developing an app for the ancient BREW OS (the OS of a basic phone), for example, it wouldn't care about safety. If I was developing for an iPhone 6, then it could run fast no matter what, so I would need an emphasis on safety. (Seriously, have you read Apple's App Store Guidelines, or read up on the development of Swift and Swift 2.0?)

There are two things wrong here. The int i is actually an array element, array[10], as seen on the stack. Because you have allowed the indexing to actually make array[10] = 0, the loop index, i, will never exceed 10. Make it for(i=0; i<10; i+=1).

i++ is, as K&R would call it, 'bad style'. It is incrementing i by the size of i, not 1. i++ is for pointer math and i+=1 is for algebra. While this depends on the compiler, it is not a good convention for portability.

Unlike Java, C doesn't do array boundary check, i.e, there's no ArrayIndexOutOfBoundsException어레이 인덱스가 유효한지 확인하는 작업은 프로그래머에게 맡겨집니다.일부러 이렇게 하면 정의되지 않은 행동을 하게 되고, 무슨 일이든 일어날 수 있습니다.


어레이의 경우:

int array[10]

인덱스는 범위 내에서만 유효합니다.0로.9단, 다음과 같은 작업을 수행하려고 합니다.

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

접근array[10]여기서 조건을 로 변경합니다.i < 10

사이즈 10의 배열을 작성했기 때문에 루프 조건은 다음과 같습니다.

int array[10],i;

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

현재 메모리에서 할당되지 않은 위치에 액세스하려고 합니다.array[10]정의되지 않은 동작을 일으킵니다.정의되지 않은 동작은 프로그램이 결정되지 않은 방식으로 동작하므로 각 실행에서 다른 출력을 제공할 수 있습니다.

C 컴파일러는 전통적으로 경계를 확인하지 않습니다.프로세스에 "속하지 않는" 위치를 참조하는 경우 분할 결함을 얻을 수 있습니다.단, 로컬 변수는 스택에 할당되며 메모리 할당 방법에 따라 어레이 바로 뒤의 영역(array[10])는 프로세스의 메모리 세그먼트에 속할 수 있습니다.따라서 세그멘테이션 장애 트랩은 느려지지 않으며, 이는 사용자가 경험하는 것처럼 보입니다.다른 사람들이 지적했듯이, 이것은 C에서 정의되지 않은 동작이며, 당신의 코드는 불규칙한 것으로 간주될 수 있습니다.C를 배우고 있기 때문에 코드의 경계를 확인하는 습관을 들이는 것이 좋습니다.

메모리의 레이아웃이 되어, 다음에 쓰려고 할 가능성이 있는 것을 넘어서는 것입니다.a[10]실제로 덮어쓰기i또한 최적화 컴파일러는 다음 값으로는 루프 테스트에 도달할 수 없다고 판단할 수도 있습니다.i존재하지 않는 어레이 요소에 처음 액세스한 코드가 없는 경우 10개 이상a[10].

그 요소에 대한 접근 시도는 정의되지 않은 동작이기 때문에 컴파일러는 그 시점 이후에 프로그램이 무엇을 할 수 있는지에 대한 의무는 없습니다.보다 구체적으로 컴파일러는 루프 인덱스를 체크하기 위해 코드를 생성할 의무가 없기 때문에 체크하기 위해 코드를 생성할 의무는 전혀 없습니다.대신 컴파일러는 이 코드가 10보다 클 수 있다고 가정할 수 있습니다.<=10테스트는 항상 참이 됩니다.이것은 코드가 읽혀져도 해당됩니다.a[10] rather than writing it.

When you iterate past i==9 you assign zero to the 'array items' which are actually located past the array, so you're overwritnig some other data. Most probably you overwrite the i variable, which is located after a[]. That way you simply reset the i variable to zero and thus restart the loop.

You could discover that yourself if you printed i in the loop:

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

instead of just

      printf("test \n");

Of course that result strongly depends on the memory allocation for your variables, which in turn depends on a compiler and its settings, so it is generally Undefined Behavior — that's why results on different machines or different operating systems or on different compilers may differ.

the error is in portion array[10] w/c is also address of i (int array[10],i;). when array[10] is set to 0 then the i would be 0 w/c resets the entire loop and causes the infinite loop. there will be infinite loop if array[10] is between 0-10.the correct loop should be for (i = 0; i <10 ; i++) {...} int array[10],i; for (i = 0; i <=10 ; i++) array[i]=0;

I will suggest something that I dint find above:

Try assigning array[i] = 20;

I guess this should terminate the code everywhere.. (given you keep i< =10 or ll)

If this runs you can firmly decide that the answers specified here already are correct [the answer related to memory stomping one for ex.]

ReferenceURL : https://stackoverflow.com/questions/31016660/why-does-this-for-loop-exit-on-some-platforms-and-not-on-others

반응형