programing

해당 루프의 함수 내에서 루프를 탈출하는 것

css3 2023. 10. 12. 23:26

해당 루프의 함수 내에서 루프를 탈출하는 것

있습니다.for해당 루프에서 호출되는 함수 내에서 루프가 발생합니다.그냥 함수가 값을 반환하게 한 다음 특정 값에 대해 확인한 후 깨지는 것이 가능하다는 것은 알고 있지만, 함수 내에서 직접 해보고 싶습니다.

이것은 제 기능의 기능 서명을 다음과 같이 하도록 요구하는 특정 하드웨어에 대해 사내 라이브러리를 사용하고 있기 때문입니다.

void foo (int passV, int aVal, long bVal)

반품 가격을 사용하지 않는 것은 매우 나쁜 관행이라는 것을 알고 있지만, 사정이 여의치 않으니, 부디 참아주시기 바랍니다.

다음과 같은 예를 생각해 봅니다.

#include <stdio.h>

void foo (int a) {
    printf("a: %d", a);
    break;
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        foo(i);
    }
    return 0;
}

이제 이것은 컴파일되지 않습니다.대신 다음과 같은 컴파일 오류가 발생합니다.

prog.c: 함수 'foo'에서 prog.c:6:2: 오류: break 문이 루프 또는 스위치 브레이크 내에 있지 않음;

뜻인지 (는 .) (를)foo()a 이내에 있지 않습니다.for프)

자, 브레이크 문에 관한 표준에서 찾을 수 있는 것은 다음과 같습니다.

중단문은 행, 행, 또는 전환 중에 가장 안쪽의 엔클로저 뒤에 있는 문으로 컨트롤을 전달합니다.구문은 단순하게 깨져 있습니다.

이 이 하면 하면입니다.for루프, 왜 브레이크 스테이트먼트는 루프에 대한 말에서 벗어나지 않는 거지?또한 기능을 먼저 반환하지 않고 이와 같은 것을 실현할 수 있습니까?

.break;으로인몸.for

몇 가지 방법이 있지만 둘 다 권장되지 않습니다.

  • 를 할 수 있습니다.exit()기능. 으로부터 작동하기 에.main()그 후에 아무것도 하지 않고, 원하는 것을 이런 식으로 성취하는 것은 가능하지만, 특별한 경우입니다.

  • 하고 할 수 .for함수 호출 후 루프를 선택합니다.전역 변수를 사용하는 것은 일반적으로 권장되지 않습니다.

  • 합니다.setjmp()그리고.longjmp() 것과 도 모릅니다.. 다른 것들을 부수고 파리를 완전히 놓칠지도 모릅니다.저는 이 방법을 추천하지 않습니다.가,합니다를 합니다.jmpbuf전역 변수로서 함수 또는 액세스에 전달해야 합니다.

가능한 은 입니다.status추가 인수로서 variable: 함수는 루프에서 끊어야 할 필요성을 나타내도록 설정할 수 있습니다.

그러나 지금까지 C에서 가장 좋은 접근법은 연속성을 테스트하기 위해 값을 반환하는 이며, 가장 판독 가능합니다.

은 의 있지 foo()다를 할 수 할 수 .foo():longjmp()입니다 내부 내부 깊숙한 곳에서.foo(), , , 로 할 수 있습니다.setjmp()위치, 모든 중개 전화에 대해 정규 기능 종료 코드를 무시합니다. 충돌을 할,setjmp()/longjmp()는 솔루션이지만 리소스 유출, 초기화 누락, 일관되지 않은 상태 및 기타 정의되지 않은 동작 소스와 같은 다른 문제를 일으킬 수 있습니다.

의 하세요를 하세요.for됩니다를 합니다.101에.<=교환입니다. .for.for (int i = 0; i < 100; i++)상한(excluded)으로 나타나는 횟수를 정확히 반복합니다.

break,맘에 들다goto는 할 수 꼭 , 하지만, 를 할 수 setjmp그리고.longjmp:

#include <stdio.h>
#include <setjmp.h>

jmp_buf jump_target;

void foo(void)
{
    printf("Inside foo!\n");
    longjmp(jump_target, 1);
    printf("Still inside foo!\n");
}

int main(void) {
    if (setjmp(jump_target) == 0)
        foo();
    else
        printf("Jumped out!\n");
    return 0;
}

에 하는 것.longjmp입니다로 할 setjmp 콜의 입니다. 반환 값은 다음과 같습니다.setjmp점프 목표를 설정한 후에 복귀하는지 또는 점프에서 복귀하는지 여부를 보여줍니다.

출력:

Inside foo!
Jumped out!

비국소 점프는 올바르게 사용될 때 안전하지만 신중하게 생각해야 할 것이 많습니다.

  • 부터.longjmp합니다 를 " .setjmp 앤 더elongjmp호출, 현재 실행 중인 장소 이후에 추가 작업을 수행할 수 있을 것으로 예상되는 기능이 있으면 해당 작업은 수행되지 않습니다.
  • setjmp종료되었습니다. 동작이 정의되지 않았습니다.뭐든지 발생할 수 있다.
  • 한다면setjmp 전화가 요.jump_target설정되지 않고 동작이 정의되지 않습니다.
  • setjmp특정 조건에서 정의되지 않은 값을 가질 수 있습니다.
  • 한 단어: 실.
  • 소수점 수 점, 수 있는 점.setjmp러.

대부분의 경우 기계 명령과 CPU 레지스터 수준에서 비국소 점프가 무엇을 하는지 잘 이해한다면 자연스럽게 따라오지만, 만약 당신이 그것을 가지고 있지 않고 C 표준이 무엇을 하고 무엇을 보장하지 않는지 읽어본 적이 없다면, 나는 약간의 주의를 충고하고 싶습니다.

(참고: 원래 이 글을 썼을 때부터 질문이 편집되었습니다.)

C가 컴파일되는 방식 때문에 함수가 호출될 때 어디로 이동해야 하는지 알아야 합니다.서라도 휴식이 이 안 되기 나,에,다를 수 .break;당신의 기능에 진술하고 이것이 이렇게 작동하게 합니다.

를했습니다 하고 했습니다.#define또는 함수에서 멀리 점프(!)합니다.이것들은 극히 빈약한 해결책들입니다.break이런 합니다와

#include <stdbool.h>

bool checkAndDisplay(int n)
{
    printf("%d\n", n);
    return (n == 14);
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        if (checkAndDisplay(i))
            break;
    }
    return 0;
}

정확한 방법을 사용하여 동일한 결과를 얻는 대신 이와 같은 것을 달성하는 모호한 방법을 찾는 것은 유지 및 디버그하기에 악몽 같은 심각한 품질의 코드를 생성하는 확실한 방법입니다.

댓글에 숨겨져 있는 것처럼 보이드 리턴을 사용해야 합니다. 이것은 문제가 아니라 포인터로 브레이크 파라미터를 전달하십시오.

#include <stdbool.h>

void checkAndDisplay(int n, bool* wantBreak)
{
    printf("%d\n", n);
    if (n == 14)
        wantBreak = true;
}

int main(void) {
    bool wantBreak = false;
    for (int i = 0; i <= 100; i++) {
        checkAndDisplay(i, &wantBreak);
        if (wantBreak)
            break;
    }
    return 0;
}

이므로 와 중 하는 데 하는 것이 .foo(a, b, (long)&out);

break컴파일 시간 동안 해결되는 문장입니다.따라서 컴파일러는 동일한 함수 내에서 적절한 루프를 찾아야 합니다.다른 곳에서 함수를 호출할 수 없다는 보장은 없습니다.

할 수 break에 두 할 수 .for를 들어 코드프(loop. 예를 들어 다음 코드와 같은 코드:

#include <stdio.h>
#include <stdbool.h>

static bool continueLoop = true;

void foo (int a)
{
    bool doBreak = true;

    printf("a: %d",a);

    if(doBreak == true){
        continueLoop = false;
    }
    else {
        continueLoop = true;
    }
}
int main(void) {
    continueLoop = true;   // Has to be true before entering the loop
    for (int i = 0; (i <= 100) && continueLoop; i++)
    {
        foo(i);
    }
    return 0;
}

가 .break - for루프는 다른 반복을 수행하지 않습니다.만약 당신이 하고 싶다면.break다를 .if -continueLoop다로 .break:

int main(void) {
    continueLoop = true;   // Has to be true before entering the loop
    for (int i = 0; i <= 100; i++)
    {
        foo(i);
        if(!continueLoop){
            break;
        }
    }
    return 0;
}

제가 보기에 이건 어떻게 된 일인지와 관련이 있습니다.break문장은 기계 코드로 번역됩니다. 그break문은 루프 또는 스위치 바로 뒤에 있는 레이블에 무조건 분기로 번역됩니다.

mov ECX,5
label1:
  jmp <to next instruction address>  ;break
loop label1
<next instruction>

를 거는 동안.foo()입니다 .

mov ECX,5
label1:
  call <foo address>
loop label1
<next instruction>

foo주소

call <printf address>
jmp <to where?> ;break cannot translate to any address.

이것은 실현 가능성이 있을 수도 있고 없을 수도 있는 또 다른 아이디어입니다: foo를 no op으로 바꿀 수 있는 변수를 주변에 유지하는 것입니다.

int broken = 0;

void foo (int a) {
    if (broken) return;

    printf("a: %d", a);
    broken = 1; // "break"
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        foo(i);
    }
    return 0;
}

으로 동일합니다되지만 (되지만, 함)만 수행합니다).ifstatement)를 .다.thread safe)가 아니며 다단,foo재설정할 수 있습니다.broken를 사용하여 호출할 경우 변수가 0으로 바뀝니다.a필요한 경우 0과 같음).

그래서 좋지는 않지만, 아직 언급되지 않은 아이디어.

이와 같은 경우에는 for 루프 대신 &&으로 연결된 여러 조건문이 있는 while() 루프를 사용하는 것을 고려해 보십시오.setjmp, longjmp 등의 기능을 사용하여 정상적인 제어 흐름을 변경할 수 있지만, 이는 거의 모든 곳에서 나쁜 관행으로 간주됩니다.이 사이트에서 너무 열심히 검색하지 않아도 이유를 알 수 있습니다. (단순히 디버깅이나 인간의 이해에 도움이 되지 않는 복잡한 제어 흐름을 만들 수 있는 기능 때문입니다.)

또한 다음과 같은 작업을 고려해 보십시오.

int foo (int a) {
    if(a == someCondition) return 0;
    else {
        printf("a: %d", a);
        return 1;
    }
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        if(!foo(i)) break;
    }
    return 0;
}

이 경우 루프는 'foo'에서 반환되는 참값에 따라 달라지는데, 'foo' 내부의 조건이 충족되지 않으면 루프가 끊어집니다.

편집: 나는 goto, setjmp, longjmp 등의 사용을 명시적으로 반대하지 않습니다.하지만 이 경우 이러한 조치에 의존하지 않고 훨씬 더 간단하고 간결한 해결책이 있다고 생각합니다!

반환 값을 처리할 수 없는 경우, 적어도 다음 함수에 매개 변수를 추가할 수 있습니다.나는 다음과 같은 해결책을 상상할 수 있습니다.

void main (void)
{
  int a = 0;

  for (; 1 != a;)
  {
    foo(x, &a);
  } 
}

void foo( int x, int * a)
{
  if (succeeded)
  {
    /* set the break condition*/
    *a = 1;
  }
  else
  {
    *a = 0;
  }
}

첫 게시물이니 포맷이 엉망이 되었다면 용서해주세요 :)

문제의 한계를 명확히 제시한 업데이트된 질문에 따라 전체 루프를 함수 내부로 이동한 다음 해당 함수 내부에 반환 값이 있는 두 번째 함수를 호출하는 것이 좋습니다.

#include <stdbool.h>

bool foo (int x)
{
    return (x==14);
}

void loopFoo(int passV, int aVal, long bVal)
{
   for (int i = 0; i <= 100; ++i)
   {
       if (foo(x))
           break;
   }
}

이것은 한계를 벗어나기 위한 극단적이고 연약한 체조를 피합니다.

글로벌 변수를 설정하고 루프에서 확인하면 됩니다.

#include <stdio.h>

int leave = 0;

void foo (int a) {
    printf("a: %d", a);
    leave = 1;
}

int main(void) {
    for (int i = 0; i <= 100; i++) {
        foo(i);
        if (leave)
          break;
    }
    return 0;
}

합니다에서 하는 것을 합니다.for 이 가 여러 정의합니다.

#define f()\
printf("a: %d", a);\
break;

루프 안에서 함수에 오류를 던져 루프 밖에서 해당 오류를 잡을 수 있습니다.

#include <stdio.h>

void foo (int a) {
    printf("a: %d", a);
    if (a == 50)
    {
       throw a;
    }
}

int main(void) {
    try {
        for (int i = 0; i <= 100; i++) {
            foo(i);
        }
    catch(int e) {
    }
    return 0;
}

이 질문은 이미 답변이 되었지만, c++에서 루프를 종료하기 위해 가능한 모든 옵션을 검토할 가치가 있다고 생각합니다.기본적으로 다섯 가지 가능성이 있습니다.

  • 루프 조건 사용
  • A 를 .break
  • A 를 .return
  • 예외 사용
  • 으로.goto

아래에서는 c++14를 사용하여 이러한 옵션의 사용 사례를 설명하겠습니다.그러나 이전 버전의 c++에서는 이 모든 작업을 수행할 수 있습니다(예외의 경우는 제외).간단히 말씀드리면 포함과 주요 기능은 생략하겠습니다.좀 더 명확한 부분이 필요하다고 생각되면 댓글을 달아주세요.

1. 루프 조건 사용

루프를 나가는 표준 방법은 루프 조건입니다.다.for에, a를는합니다.while문:

for(something; LOOP CONDITION; something) {
    ... 
}
while (LOOP CONDITION)
    ... 
}
do {
    ... 
} while (LOOP CONDITION);

루프 조건에 따라 루프를 입력해야 하는지 여부와 루프를 반복해야 하는지 여부가 결정됩니다.에, 합니다.true되는 순환을 , .

예를 들어 0에서 2 사이의 숫자를 출력하려면 루프와 루프 조건을 사용하여 코드를 작성할 수 있습니다.

for (auto i = 0; i <= 2; ++i)
    std::cout << i << '\n';
std::cout << "done";

서 조건은 i <= 2이 로 한.true 있어요 합니다.

대안적인 구현은 다음과 같이 조건을 변수에 넣는 것입니다.

auto condition = false;

for (auto i = 0; !condition; ++i) {
    std::cout << i << '\n';
    condition = i > 2;
}
std::cout << "done";

두 버전에 대한 출력을 확인하면 원하는 결과를 얻을 수 있습니다.

0
1
2
done

실제 응용 프로그램에서 루프 조건을 어떻게 사용하시겠습니까?

두 버전 모두 c++ 프로젝트 내에서 널리 사용됩니다.첫 번째 버전은 더 콤팩트하고 이해하기 쉽다는 점에 유의해야 합니다.그러나 두 번째 버전은 상태가 더 복잡하거나 평가해야 할 여러 단계가 필요한 경우 일반적으로 사용됩니다.

예를 들어,

auto condition = false;
for (auto i = 0; !condition; ++i)
    if (is_prime(i))
        if (is_large_enough(i)) {
            key = calculate_cryptographic_key(i, data);
            if (is_good_cryptographic_key(key))
                condition = true;
        }

로. A를 사용.break

또를입니다.break키워드. 내에서 할 경우 됩니다.루프 내에서 사용할 경우 실행이 중지되고 루프 본체 이후에도 계속됩니다.

for (auto i = 0; true; ++i) {
    if (i == 3)
        break;
    std::cout << i << '\n';
}
std::cout << "done";

되고 1 합니다.i값이 3에 도달합니다.eif성명서는 우리의 것입니다.break조건.조건이 다음과 같습니다.true ()).!및 ) 됩니다.done.

테스트를 수행하면 실제로 예상되는 결과를 얻을 수 있습니다.

0
1
2
done

코드에서 가장 안쪽의 루프만 정지시키는 것이 중요합니다.따라서 여러 루프를 사용할 경우 원치 않는 동작이 발생할 수 있습니다.

for (auto j = 0; true; ++j)
    for (auto i = 0; true; ++i) {
        if (i == 3)
            break;
        std::cout << i << '\n';
    }
std::cout << "done";

로 우리는 와 같은 루프를 . break합니다를 통한 합니다.i 그 에 있는 것이 , ㅇj!

테스트 수행:

0
1
2
0
1
2
...

A를 어떻게 사용하겠습니까?break실제 응용프로그램의 조건?

은.break내부 루프의 일부를 건너뛰거나 추가 루프 출구를 추가하는 데만 사용됩니다.

예를 들어 소수에 대한 함수 테스트에서는 현재 숫자가 소수가 아닌 경우를 발견하는 즉시 나머지 실행을 건너뛸 수 있습니다.

auto is_prime = true;
for (auto i = 0; i < p; ++i) {
    if (p%i == 0) { //p is dividable by i!
        is_prime = false;
        break; //we already know that p is not prime, therefore we do not need to test more cases!
    }

또는 문자열 벡터를 검색하는 경우 일반적으로 루프 헤드에 데이터의 최대 크기를 넣고 실제로 검색 중인 데이터를 발견한 경우 추가 조건을 사용하여 루프를 종료합니다.

auto j = size_t(0);
for (auto i = size_t(0); i < data.size(); ++i)
    if (data[i] == "Hello") { //we found "Hello"!
        j = i;
        break; //we already found the string, no need to search any further!
    }

으로. A를 사용.return

return키워드는 현재 범위를 종료하고 호출 기능으로 돌아갑니다.따라서 루프를 종료할 때 사용할 수 있으며, 또한 발신자에게 번호를 반환할 수도 있습니다.일반적인 경우는 다음과 같습니다.return루프(및 그 함수)를 종료하고 결과를 반환합니다.

를 들면 를 쓸 수 .is_prime위에서부터의 기능:

auto inline is_prime(int p) {
    for (auto i = 0; i < p; ++i)
        if (p%i == 0) //p is dividable by i!
            return false; //we already know that p is not prime, and can skip the rest of the cases and return the result
    return true; //we didn't find any divisor before, thus p must be prime!
}

return키워드를 사용하여 여러 루프를 종료할 수도 있습니다.

auto inline data_has_match(std::vector<std::string> a, std::vector<std::string> b) {
    for (auto i = size_t(0); i < a.size(); ++i)
        for (auto j = size_t(0); j < a.size(); ++j)
            if (a[i] == b[j])
                return true; //we found a match! nothing to do here
    return false; //no match was found
}

A를 어떻게 사용하겠습니까?return실제 응용프로그램의 조건?

에서,return루프를 종료하고 결과를 직접 반환하는 데 자주 사용됩니다.더 큰 return는 코드를 선명하고 가독성 있게 유지하는 데 도움이 됩니다.

for (auto i = 0; i < data.size(); ++i) {
    //do some calculations on the data using only i and put them inside result
    if (is_match(result,test))
        return result;
    for (auto j = 0; j < i; ++j) {
        //do some calculations on the data using i and j and put them inside result
        if (is_match(result,test))
            return result;
    }
}
return 0; //we need to return something in the case that no match was found

다음과 같은 것보다 훨씬 이해하기 쉽습니다.

auto break_i_loop = false;
auto return_value = 0;
for (auto i = 0; !break_i_loop; ++i) {
    //do some calculations on the data using only i and put them inside result
    if (is_match(result,test)) { //a match was found, save the result and break the loop!
        return_value = result;
        break;
    }
    for (auto j = 0; j < i; ++j) {
        //do some calculations on the data using i and j and put them inside result
        if (is_match(result,test)) { //a match was found, save the result, break the loop, and make sure that we break the outer loop too!
            return_value = result;
            break_i_loop = true;
            break;
        }
    }
    if (!break_i_loop) //if we didn't find a match, but reached the end of the data, we need to break the outer loop
        break_i_loop = i >= data.size();
}
return return_value; //return the result

4. 예외 사용

예외는 코드에 예외적인 이벤트를 표시하는 방법입니다.예를 들어, 파일에서 데이터를 읽고 싶지만 어떤 이유로 파일이 존재하지 않는 경우!예외는 루프를 종료하는 데 사용될 수 있지만, 컴파일러는 보통 예외가 처리될 경우 프로그램을 안전하게 계속하기 위해 많은 보일러플레이트 코드를 생성합니다.따라서 값을 반환하는 데는 예외를 사용해서는 안 됩니다. 매우 비효율적이기 때문입니다.

실제 응용프로그램에서 예외를 어떻게 사용하시겠습니까?

예외는 정말 예외적인 경우를 처리하는 데 사용됩니다.예를 들어, 만약 우리가 데이터의 역수를 계산하고자 한다면, 우리가 0으로 나누려고 할 수도 있습니다.그러나 이는 계산에 도움이 되지 않으므로 다음과 같이 적습니다.

auto inline inverse_data(std::vector<int>& data) {
    for (auto i = size_t(0); i < data.size(); ++i)
        if (data[i] == 0)
            throw std::string("Division by zero on element ") + std::to_string(i) + "!";
        else
            data[i] = 1 / data[i];
}

호출 기능 내부에서 이 예외를 처리할 수 있습니다.

while (true)
    try {
        auto data = get_user_input();
        inverse = inverse_data(data);
        break;
    }
    catch (...) {
        std::cout << "Please do not put zeros into the data!";
    }

한다면data 그 은 0합니다를 포함합니다.inverse_data입니다는 둘 break는 실행되지 않으며, 사용자는 다시 데이터를 입력해야 합니다.

이러한 유형의 오류 처리를 위한 사전 옵션이 훨씬 더 많이 제공되며, 오류 유형도 추가됩니다. ... 하지만 이 문제는 앞으로 논의할 주제입니다.

** 절대로 해서는 안되는 일! *

앞서 언급한 바와 같이, 예외는 상당한 런타임 오버헤드를 발생시킬 수 있습니다.따라서 이들은 정말 예외적인 경우에만 사용되어야 합니다.아래의 기능을 쓸 수 있지만, 하지 말아주세요!

auto inline next_prime(int start) {
    auto p = start;
    try {
        for (auto i = start; true; ++i)
            if (is_prime(i)) {
                p = i;
                throw;
            }
   }
   catch (...) {}
   return p;
 }

5. 하기.goto

goto키워드는 코드를 읽기 어렵게 만들고 의도하지 않은 부작용을 일으킬 수 있기 때문에 대부분의 프로그래머들이 싫어합니다.그러나 다음과 같이 여러 루프를 종료할 때 사용할 수 있습니다.

for (auto j = 0; true; ++j)
    for (auto i = 0; true; ++i) {
        if (i == 3)
            goto endloop;
        std::cout << i << '\n';
    }
endloop:
std::cout << "done";

이 루프는 (2부의 루프와 달리) 종료되고 다음과 같이 출력됩니다.

0
1
2
done

A를 어떻게 사용하겠습니까?goto실제 응용 프로그램에서?

.9를 .%의 경우에는goto키워드. 같은 매우 코드만 입니다.유일한 예외는 아두이노와 같은 임베디드 시스템이나 매우 높은 성능의 코드입니다. 둘 중다를 할 수 goto더 빠르고 효율적인 코드를 생성할 수 있습니다. 수 있는 이 훨씬 더 큽니다.goto.

중하더라도 가 0.1%가 . .goto실제로 실행력이 향상됩니다.를가 많습니다.break아니면return다를 하는 데 을 겪기 이 더 goto.

언급URL : https://stackoverflow.com/questions/35404220/breaking-out-of-a-loop-from-within-a-function-called-in-that-loop