변수에 레이블 주소를 저장하고 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 goto
8.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를 결합할 수 있다.goto
A과 함께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에서 코루틴을 구현하는 데까지 사용될 수 있다.
&&&를 사용하여 변수에 레이블을 할당할 수 있다.여기 수정된 코드 입니다.
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++] ();
'programing' 카테고리의 다른 글
술어로 스트림 제한 (0) | 2022.05.13 |
---|---|
vue에서 어레이를 모델로 사용하는 경우 확인란 값을 가져오는 방법 (0) | 2022.05.13 |
Vue Js의 Javascript 파일에서 상태 값 업데이트 (0) | 2022.05.13 |
C에서 "참조" 및 "회의"의 의미 (0) | 2022.05.13 |
NuxTJS 하위 도메인 (0) | 2022.05.13 |