programing

변수에 레이블 주소를 저장하고 goto를 사용하여 해당 주소로 이동할 수 있는가?

prostudy 2022. 5. 13. 23:49
반응형

변수에 레이블 주소를 저장하고 goto를 사용하여 해당 주소로 이동할 수 있는가?

다들 고토 싫어하는 거 알아.내 코드에서, 내가 생각해 왔고 편안한 이유로, 그들은 효과적인 해결책을 제공한다. (즉, 나는 "그러지 마"를 답으로 찾는 것이 아니라, 나는 당신의 예약을 이해하고, 내가 그것들을 왜 사용하는지 이해한다.)

지금까지는 환상적이었지만, 나는 라벨에 포인터를 저장한 후 나중에 그들에게 갈 수 있는 그런 방식으로 기능성을 확장하고 싶다.

만약 이 코드가 작동한다면, 그것은 내가 필요로 하는 기능성의 유형을 나타낼 것이다.하지만 효과가 없고, 30분 동안 구글을 검색해도 아무것도 드러나지 않았다.누구 아이디어 있는 사람?

int main (void)
{
  int i=1;
  void* the_label_pointer;

  the_label:

  the_label_pointer = &the_label;

  if( i-- )
    goto *the_label_pointer;

  return 0;
}

C와 C++ 표준은 이 기능을 지원하지 않는다.그러나 GNU 컴파일러 컬렉션(GCC)에는 이에서 설명한 대로 이를 수행하기 위한 비표준 확장자가 포함되어 있다.본질적으로, 그들은 라벨의 주소를 타입 "void*"로 보고하는 특수 연산자 "&&"를 추가했다.자세한 내용은 기사를 참조하십시오.

P.S. 다시 말해서, 당신의 예에서 "&"가 아닌 "&&"를 사용하면 GCC에서 효과가 있을 것이다.
P.P.S. 내가 말하길 원하지 않는 건 알지만, 어쨌든 말하겠어...그러지 마!!!

나는 모든 사람들이 그렇게 해서는 안 된다고 말하는 것을 안다. 단지 그렇게 해야만 한다.GNU C에서 사용&&the_label;라벨의 주소를 가지다(https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html) 당신이 추측한 구문은goto *ptr에서void*는 사실 GNU C가 사용하는 것이다.

또는 어떤 이유로 인라인 어셈블리를 사용하려는 경우 GNU C를 사용하여 수행하는 방법은 다음과 같다.

// unsafe: this needs to use  asm goto so the compiler knows
// execution might not come out the other side
#define unsafe_jumpto(a) asm("jmp *%0"::"r"(a):)

// target pointer, possible targets
#define jumpto(a, ...) asm goto("jmp *%0" : : "r"(a) : : __VA_ARGS__)

int main (void)
{
  int i=1;
  void* the_label_pointer;

  the_label:
  the_label_pointer = &&the_label;

label2:

  if( i-- )
    jumpto(the_label_pointer, the_label, label2, label3);

label3:
  return 0;
}

라벨 목록에는 다음에 대한 모든 가능한 값이 포함되어야 한다.the_label_pointer.

거시적 확장은 다음과 같은 것이 될 것이다.

asm goto("jmp *%0" : : "ri"(the_label_pointer) : : the_label, label2, label3);

에 gcc 4.5이 으로 컴파일한다.asm goto8.0. https://godbolt.org/z/BzhckE 이후 얼마간 지원.그 결과 Asm은 GCC9.1의 "루프"를 최적화한 것에 대해 이렇게 보인다.i=i/i--그리고 그냥.the_label 의 뒤에jumpto그러니까 C 소스처럼 정확히 한 번 작동되는 거군

# gcc9.1 -O3 -fpie
main:
    leaq    .L2(%rip), %rax     # ptr = &&label
    jmp *%rax                     # from inline asm
.L2:
    xorl    %eax, %eax          # return 0
    ret

그러나 땡땡이는 최적화를 하지 않았고 여전히 루프를 가지고 있다.

# clang -O3 -fpie
main:
    movl    $1, %eax
    leaq    .Ltmp1(%rip), %rcx
.Ltmp1:                                 # Block address taken
    subl    $1, %eax
    jb      .LBB0_4                  # jump over the JMP if i was < 1 (unsigned) before SUB.  i.e. skip the backwards jump if i wrapped
    jmpq    *%rcx                   # from inline asm
.LBB0_4:
    xorl    %eax, %eax              # return 0
    retq

라벨 주소 운영자 &&&는 gcc로만 작업할 것이다.그리고 분명히 점프토 조립 매크로가 각 프로세서에 대해 특별히 구현되어야 한다(32비트 및 64비트 x86 모두에서 작동한다.

또한 (없이는) 그 점을 명심하라.asm goto 같은 함수의 서로 다른 두 지점에서 스택의 상태가 동일하다는 보장은 없을 것이다.그리고 적어도 어느 정도 최적화가 켜져 있으면 컴파일러는 라벨 뒤의 지점에서 어떤 값을 포함하도록 일부 레지스터를 가정할 수 있다.이런 일들은 컴파일러가 예상하지 못한 미친 짓을 하다가 쉽게 엉망이 될 수 있다.컴파일된 코드를 반드시 읽어보십시오.

이런 이유들이 있다.asm goto컴파일러에게 점프 장소와 목적지를 알려줌으로써 안전하게 하기 위해 필요하다.

매우 오래된 버전의 C 언어에서(Dennis Ritchie가 쓴 문서를 지칭하는) "C reference Manual" 버전으로 알려진 (공룡이 지구를 돌아다녔던 때를 생각해 보라), 라벨에는 형식적으로 "int의 배열"(이상하지만 사실)이 있었는데, 이는 당신이 a를 선언할 수 있다는 것을 의미한다.int *가변적

int *target;

그리고 해당 변수에 레이블 주소를 할당하십시오.

target = label; /* where `label` is some label */

나중에 그 변수를 의 피연산자로 사용할 수 있을 것이다.goto성명서

goto target; /* jumps to label `label` */

그러나 ANSI C에서는 이 기능이 제외되었다.표준 현대 C에서는 라벨의 주소를 얻을 수 없으며 "변수"를 할 수 없다.goto. 이 행동은 다음과 같이 모의실험되어야 한다.switch문장, 포인터 대 포인터 및 기타 방법 등실제로 "C 참조 매뉴얼" 자체도 "레이블 변수는 일반적으로 좋지 않은 생각이며, 스위치 문장으로 인해 거의 항상 불필요하다"고 말했다. ("14.4 라벨" 참조)

여기서 기능적으로 기술된 (&&& in gcc 포함)은 C에서 포스어 통역관 구현에 이상적이라는 점에 유의하겠다.그것은 모든 "그러지 말라"는 주장을 물 밖으로 날려버린다 - 그 기능성과 포워스의 내부 통역관이 일하는 방법 사이의 적합성은 무시하기에는 너무 좋다.

#include <stdio.h>

int main(void) {

  void *fns[3] = {&&one, &&two, &&three};   
  char p;

  p = -1;

  goto start; end:   return 0;     
  start:   p++;   
  goto *fns[p];
  one:  printf("hello ");  
  goto start;  
  two:  printf("World. \n");  
  goto start;
  three:  goto end;
}

C99 표준 § 6.8.6에 따르면, 다음과 같은 구문goto다음과 같은 경우:

    에 가다 식별자 ;

그래서 라벨의 주소를 받아도 goto와 함께 사용할 수 없었다.

A를 결합할 수 있다.gotoA과 함께switch, 그것은 계산된 것과 같다.goto, 유사한 효과의 경우:

int foo() {
    static int i=0;
    return i++;
}

int main(void) {
    enum {
        skip=-1,
        run,
        jump,
        scamper
    } label = skip; 

#define STATE(lbl) case lbl: puts(#lbl); break
    computeGoto:
    switch (label) {
    case skip: break;
        STATE(run);
        STATE(jump);
        STATE(scamper);
    default:
        printf("Unknown state: %d\n", label);
        exit(0);
    }
#undef STATE
    label = foo();
    goto computeGoto;
}

이걸 난독화된 C 콘테스트 말고 다른 데 쓰면 내가 널 사냥해서 다치게 할 거야.

setjmp/longjmp로 비슷한 일을 할 수 있다.

int main (void)
{
    jmp_buf buf;
    int i=1;

    // this acts sort of like a dynamic label
    setjmp(buf);

    if( i-- )
        // and this effectively does a goto to the dynamic label
        longjmp(buf, 1);

    return 0;
}

switch ... case진술은 본질적으로 계산된 것이다. 어떻게 작동하는지 보여주는 좋은 예는 Duff's Device라고 알려진 이상한 해킹이다.

send(to, from, count)
register short *to, *from;
register count;
{
    register n=(count+7)/8;
    switch(count%8){
    case 0: do{ *to = *from++;
    case 7:     *to = *from++;
    case 6:     *to = *from++;
    case 5:     *to = *from++;
    case 4:     *to = *from++;
    case 3:     *to = *from++;
    case 2:     *to = *from++;
    case 1:     *to = *from++;
        }while(--n>0);
    }
}

할 수 없다.goto이 기법을 사용하여 임의의 위치에서, 그러나 당신은 당신의 전체 기능을switch변수를 기준으로 한 다음 원하는 위치를 나타내는 변수를 설정하고goto그 개폐문.

int main () {
  int label = 0;
  dispatch: switch (label) {
  case 0:
    label = some_computation();
    goto dispatch;
  case 1:
    label = another_computation();
    goto dispatch;
  case 2:
    return 0;
  }
}

물론 이 일을 많이 하면 포장할 매크로를 좀 쓰고 싶을 것이다.

이 기술은 몇몇 편의 매크로와 함께 C에서 코루틴을 구현하는 데까지 사용될 수 있다.

&&&amp;를 사용하여 변수에 레이블을 할당할 수 있다.여기 수정된 코드 입니다.


int main (void)
{
  int i=1;
  void* the_label_pointer = &&the_label;

  the_label:


  if( i-- )
    goto *the_label_pointer;


  return 0;
}

함수 포인터와 약간의 루프를 사용한다.다른 사람이 당신을 위해 고친 것을 후회하게 될 코드를 만들지 마라.

나는 네가 대외적으로 라벨의 주소를 변경하려고 한다고 추측한다.기능 포인터가 작동한다.

에서 할 수 한 공식 는 CETORO 입니다.goto눈치채셨듯이 주소지를 가져가거나 변수나 다른 곳에 보관할 수 없다.그래서 "그러지 마"라고 말하는 대신 "그럴 수는 없어"라고 말할 겁니다.

다른 해결책을 찾아야 할 것 같아.성능이 중요한 경우 조립 언어를 선택하십시오.

이것을 읽어라: setjmp.h - 위키백과 앞서 말한 바와 같이, 변수에 점프를 저장하고 나중에 다시 점프할 수 있는 setjmp/longjmp로 가능하다.

이 실에 따르면, 라벨 포인트는 표준이 아니므로, 라벨 포인트의 작동 여부는 당신이 사용하고 있는 컴파일러에 따라 달라진다.

기능 포인터가 있으면 포트란의 컴퓨터 goto 같은 것을 할 수 있다.

// global variables up here

void c1(){ // chunk of code

}

void c2(){ // chunk of code

}

void c3(){
// chunk of code

}

void (*goTo[3])(void) = {c1, c2, c3};

// then
int x = 0;

goTo[x++] ();

goTo[x++] ();

goTo[x++] ();

참조URL: https://stackoverflow.com/questions/1777990/is-it-possible-to-store-the-address-of-a-label-in-a-variable-and-use-goto-to-jum

반응형