programing

C99에서 f () + g ()는 정의되지 않았습니까 아니면 단순히 지정되지 않았습니까?

minecode 2021. 1. 16. 09:27
반응형

C99에서 f () + g ()는 정의되지 않았습니까 아니면 단순히 지정되지 않았습니까?


나는 기능의 부작용하더라도, C99에서 그렇게 생각하는 데 사용 f하고 g방해하고, 표현이 있지만, f() + g()일련의 점을 포함하지 않는, f그리고 g행동이 지정 될 수 있도록, 일부 포함됩니다 :) 중 F를 (전에 호출 될 것이다 f () 앞에 g () 또는 g ().

더 이상 확실하지 않습니다. 컴파일러가 함수를 인라인 한 다음 (함수가 선언되지 않은 경우에도 컴파일러가 수행하도록 결정할 수 있음 inline) 명령을 다시 정렬하면 어떻게 될까요? 위의 두 가지 결과가 다를 수 있습니까? 즉, 정의되지 않은 동작입니까?

이것은 내가 이런 종류의 것을 작성하려고하기 때문이 아니라 정적 분석기에서 그러한 명령문에 가장 적합한 레이블을 선택하는 것입니다.


표현식 f() + g()에는 최소 4 개의 시퀀스 포인트가 포함됩니다. 호출 전 하나 f()(인수가 모두 0이 평가 된 후); 호출 전 하나 g()(인수가 모두 0이 평가 된 후); f()반환에 대한 호출로 하나 ; 그리고 하나는 g()반환에 대한 호출로 . 또한와 연관된 두 개의 시퀀스 포인트는와 연관된 두 시퀀스 포인트의 f()앞 또는 뒤에 모두 발생합니다 g(). 당신이 말할 수없는 것은 f- 포인트가 g- 포인트보다 먼저 발생하는지 또는 그 반대로 발생하는지에 관계없이 시퀀스 포인트가 발생하는 순서입니다.

컴파일러가 코드를 인라인하더라도 'as if'규칙을 따라야합니다. 코드는 함수가 인터리브되지 않은 것처럼 동일하게 동작해야합니다. 이는 손상의 범위를 제한합니다 (버그가없는 컴파일러 가정).

따라서 f()g()평가 되는 순서 는 지정되지 않습니다. 그러나 다른 모든 것은 꽤 깨끗합니다.


댓글에서 supercat 은 다음 같이 묻습니다.

컴파일러가 자체적으로 인라인하기로 결정하더라도 소스 코드의 함수 호출이 시퀀스 포인트로 남아있을 것으로 예상합니다. "인라인"으로 선언 된 함수의 경우에도 마찬가지입니까? 아니면 컴파일러가 추가 관용을 얻습니까?

나는 'as if'규칙이 적용되고 컴파일러는 명시적인 inline함수를 사용하기 때문에 시퀀스 포인트를 생략 할 수있는 여유를 얻지 않는다고 생각합니다 . (표준에서 정확한 문구를 찾기에는 너무 게으르다) 생각하는 주된 이유는 컴파일러가 규칙에 따라 함수를 인라인하거나 인라인하지 않을 수 있지만 프로그램의 동작은 변경되어서는 안된다는 것입니다. 공연).

또한 시퀀싱에 대해 무엇을 말할 수 (a(),b()) + (c(),d())있습니까? 이 가능한가 c()및 / 또는 d()동안 실행 a()하고 b(), 또는 대 a()또는 b()동안 실행 c()하고 d()?

  • 분명히 a는 b보다 먼저 실행되고 c는 d보다 먼저 실행됩니다. 나는 c와 d가 a와 b 사이에서 실행되는 것이 가능하다고 믿지만 컴파일러가 그런 코드를 생성 할 가능성은 거의 없다. 유사하게, a와 b는 c와 d 사이에서 실행될 수 있습니다. 그리고 'c와 d'에서 'and'를 사용했지만 'or'가 될 수 있습니다. 즉, 이러한 작업 시퀀스 중 하나가 제약 조건을 충족합니다.

    • 확실히 허용됨
    • abcd
    • cdab
    • 허용 가능 (a ≺ b, c ≺ d 주문 유지)
    • acbd
    • acdb
    • cadb
    • 택시

     
    가능한 모든 시퀀스를 포함한다고 생각합니다. Jonathan Leffler와 AnArrayOfFunctions 간의 대화 도 참조하십시오 . 요점은 AnArrayOfFunctions 가 '가능한 경우 허용되는'시퀀스가 전혀 허용되지 않는다고 생각한다는 것입니다.

그런 일이 가능하다면 인라인 함수와 매크로 사이에 상당한 차이가 있음을 의미합니다.

인라인 함수와 매크로 사이에는 큰 차이가 있지만 표현식의 순서가 그중 하나라고 생각하지 않습니다. 즉, 함수 a, b, c 또는 d 중 하나를 매크로로 대체 할 수 있으며 매크로 본문의 동일한 순서가 발생할 수 있습니다. 가장 큰 차이점은 인라인 함수를 사용하면 쉼표 연산자뿐만 아니라 주요 답변에 설명 된대로 함수 호출에 보장 된 시퀀스 지점이 있다는 것입니다. 매크로를 사용하면 기능 관련 시퀀스 포인트를 잃게됩니다. (그러므로 아마도 그것은 중요한 차이 일 것입니다 ...) 그러나 많은면에서 문제는 얼마나 많은 천사가 핀 머리 위에서 춤을 출 수 있는지에 대한 질문과 같습니다. 실제로는 그다지 중요하지 않습니다. 누군가 내게 표현을 제시했다면(a(),b()) + (c(),d()) 코드 검토에서 명확하게 코드를 다시 작성하라고 말합니다.

a();
c();
x = b() + d();

그리고 그것은 b()에 대한 중요한 시퀀싱 요구 사항이 없다고 가정합니다 d().


시퀀스 포인트 목록은 부록 C를 참조하십시오. 함수 호출 (평가되는 모든 인수와 함수로 전달되는 실행 사이의 지점)은 시퀀스 지점입니다. 말했듯이 어떤 함수가 먼저 호출되는지는 지정되지 않았지만 두 함수 각각은 다른 함수의 모든 부작용을 보거나 전혀 표시하지 않습니다.


@dmckee

글쎄, 그것은 주석에 맞지 않을 것이지만 여기에 있습니다.

먼저 올바른 정적 분석기를 작성합니다. 이 맥락에서 "정확하다"는 것은 분석 된 코드에 대해 의심스러운 점이 있으면 침묵하지 않는다는 것을 의미하므로이 단계에서 정의되지 않은 동작과 지정되지 않은 동작을 즐겁게 결합합니다. 그것들은 중요한 코드에서 나쁘고 받아 들일 수 없으며, 두 가지 모두에 대해 올바르게 경고합니다.

그러나 가능한 버그 하나에 대해 한 번만 경고하고 싶을뿐 아니라 다른 분석기 (아마도 정확하지 않을 수 있음)와 비교할 때 "정밀도"및 "재현율"측면에서 벤치 마크에서 평가 될 것임을 알고 있으므로 그렇게하지 않아야합니다. 하나의 동일한 문제에 대해 두 번 경고합니다. 참 또는 거짓 경보인지 여부를 알 수 있습니다 (어느 것인지 알 수 없습니다. 그렇지 않으면 너무 쉬울 것입니다).

따라서 하나의 경고를 내고 싶습니다.

*p = x;
y = *p;

p첫 번째 명령문에서 유효한 포인터가되는 즉시 두 번째 명령문에서 유효한 포인터라고 가정 할 수 있기 때문입니다. 그리고 이것을 추론하지 않으면 정밀도 메트릭에 대한 점수가 낮아집니다.

따라서 p위 코드에서 처음으로 경고를하자마자 이것이 유효한 포인터 라고 가정하도록 분석기를 가르치고 두 번째로 경고하지 않도록합니다. 보다 일반적으로 이미 경고 한 내용에 해당하는 값 (및 실행 경로)을 무시하는 방법을 배웁니다.

그런 다음 중요한 코드를 작성하는 사람이 많지 않다는 것을 알게되었으므로 초기의 올바른 분석 결과를 기반으로 나머지 사람들에 대해 간단한 다른 분석을 수행합니다. 예를 들어 C 프로그램 슬라이서입니다.

그리고 "그들"에게 말합니다. 첫 번째 분석에서 발생하는 모든 (아마도, 종종 거짓) 경보를 확인할 필요가 없습니다. 슬라이스 된 프로그램은 트리거되지 않는 한 원래 프로그램과 동일하게 작동합니다. 슬라이서는 "정의 된"실행 경로에 대한 슬라이싱 기준에 해당하는 프로그램을 생성합니다.

그리고 사용자는 경보를 무시하고 슬라이서를 사용합니다.

And then you realize that perhaps there is a misunderstanding. For instance, most implementations of memmove (you know, the one that handles overlapping blocks) actually invoke unspecified behavior when called with pointers that do not point to the same block (comparing addresses that do not point to the same block). And your analyzer ignore both execution paths, because both are unspecified, but in reality both execution paths are equivalent and all is well.

So there shouldn't be any misunderstanding on the meaning of alarms, and if one intends to ignore them, only unmistakable undefined behaviors should be excluded.

And this is how you end up with a strong interest in distinguishing between unspecified behavior and undefined behavior. No-one can blame you for ignoring the latter. But programmers will write the former without even thinking about it, and when you say that your slicer excludes "wrong behaviors" of the program, they will not feel as they are concerned.

And this is the end of a story that definitely did not fit in a comment. Apologies to anyone who read that far.

ReferenceURL : https://stackoverflow.com/questions/3951017/in-c99-is-fg-undefined-or-merely-unspecified

반응형