programing

&errno는 합법 C입니까?

randomtip 2022. 8. 10. 19:44
반응형

&errno는 합법 C입니까?

7.5에 따라

[errno]는 타입 int를 가진 수정 가능한 lvalue175)로 확장되며, 이 값은 여러 라이브러리 함수에 의해 양의 오류 번호로 설정됩니다.errno가 매크로인지, 외부 링크에서 선언된 식별자인지 지정되지 않았습니다.실제 객체에 접근하기 위해 매크로 정의가 억제되거나 프로그램이 errno라는 이름의 식별자를 정의하면 동작은 정의되지 않습니다.

175) 매크로 errno는 오브젝트의 식별자일 필요는 없습니다.함수 호출에 의해 변경 가능한 l값(예를 들어 *errno())으로 확장될 수 있습니다.

이 정도면 충분한지 아닌지는 잘 모르겠습니다.&errno제약 위반이 아닙니다.C 언어에는 lvalues(register-storage-class 변수 등)가 있습니다.다만, 이러한 값은 자동적으로 설정할 수 있기 때문에,errno정의하지 못했습니다).&연산자는 제약 조건 위반입니다.

한다면&errno법적 C입니까? 일정해야 합니까?

따라서 '6.5.3.2p1'은

unary & operator의 피연산자는 함수 지정자, [] 또는 unary * 연산자의 결과 또는 비트필드가 아닌 레지스터 스토리지 클래스 지정자로 선언되지 않은 객체를 지정하는 l값이어야 합니다.

제 생각에 그건 곧 그런 의미로 받아들여질 수 있을 것 같아요.&lvalue이 두 가지 카테고리에 포함되지 않은 모든 l값에는 문제가 없습니다.그리고 말씀하신 것처럼errno레지스터 스토리지 클래스 지정자로 선언할 수 없습니다.또한 (지금 확인하기 위해 참조를 추적하고 있지는 않지만) 플레인 타입의 비트필드는 사용할 수 없다고 생각합니다.

그래서 저는 스펙이&(errno)법적 C가 됩니다.

&errno가 법적 C인 경우 일정해야 합니까?

내가 알기로는, 그 요지의 일부는errno매크로가 되는 것(그리고 그것이 glibc에 있는 이유)은 스레드 로컬 스토리지에 대한 참조가 되도록 하기 위함입니다.이 경우 스레드 전체에서 일정하지 않습니다.그리고 나는 그것이 반드시 지속되어야 한다고 기대할 이유가 없다고 본다.의 가치가 있는 한errno지정된 의미를 유지합니다.비뚤어진 C 라이브러리가 변경할 수 없는 이유는 없습니다.&errno예를 들어, 프로그램을 설정할 때마다 백업 저장소를 해제하고 재할당하여 프로그램 진행 중에 다른 메모리 주소를 참조합니다.errno.

라이브러리에 의해 설정된 마지막 N개의 errno 값의 링 버퍼를 유지하고, 그 값을&errno항상 최신 정보를 가리킵니다.특별히 유용하다고는 생각하지 않지만, 사양에 어긋나는 것은 전혀 보이지 않습니다.

아직 아무도 C11 사양을 인용하지 않은 것이 놀랍습니다.긴 인용에 대해 사과드립니다만, 저는 그것이 관련이 있다고 생각합니다.

7.5 오류

헤더는 여러 매크로를 정의합니다.

...그리고.

errno

이 값은 타입을 가진 수정 가능한 l값(201)으로 확장됩니다.int및 스레드 로컬 저장 기간. 이 값은 여러 라이브러리 함수에 의해 양의 오류 수로 설정됩니다.실제 객체에 접근하기 위해 매크로 정의가 억제되거나 프로그램이 이름으로 식별자를 정의하는 경우errno동작은 정의되어 있지 않습니다.

가치errno초기 스레드는 프로그램 시작 시 0입니다(초기값).errnoerrno 값은 라이브러리 함수에 의해 0으로 설정되지 않습니다.(202) 에러 유무에 관계없이 라이브러리 함수 호출에 의해 0이 아닌 값으로 설정될 수 있습니다.errno는 본 국제표준의 기능 설명에 기재되어 있지 않다.

(201) 매크로errno개체 식별자일 필요는 없습니다.함수 호출에 의해 변경 가능한 l값으로 확장될 수 있습니다(예를 들어,*errno()).

(202) 따라서 다음 사항을 사용하는 프로그램errno에러 체크를 실시하려면 , 라이브러리 함수를 호출하기 전에, 에러 체크를 제로로 설정해, 다음의 라이브러리 함수를 호출하기 전에 검사합니다.물론 라이브러리 함수는 다음과 같은 가치를 저장할 수 있습니다.errno입력 후 0으로 설정합니다.단, 다음과 같은 경우 원래 값이 복원되는 한errno반환 직전 값은 아직0 입니다.

'스레드 로컬'은register나왔습니다.유형int비트필드가 아웃(IMO)되었음을 의미합니다.그렇게&errno합법적으로 보이는데요

"it" 및 "value"와 같은 단어를 지속적으로 사용하는 것은 표준의 저자들이 고려하지 않았다는 것을 의미한다.&errno비인공적인 것.내 생각엔 어떤 사람은 그 시행을 상상할 수 있을 것 같아&errno특정 스레드 내에서 일정하지는 않지만 각주가 말하는 방식(0으로 설정 후 라이브러리 함수를 호출한 후 확인)으로 사용하려면 의도적으로 적대적이어야 하며, 단지 적대적이 되기 위해서는 전문 컴파일러 지원이 필요할 수 있습니다.

즉, 규격이 불변수를 허용하는 경우&errno나는 그것이 의도적이었다고 생각하지 않는다.

[갱신]

R.가 댓글로 훌륭한 질문을 합니다.생각해 본 결과, 그의 질문에 대한 정답과 원래의 질문에 대한 정답을 알게 되었다고 생각합니다.내가 당신을 설득할 수 있는지 알아보죠, 친애하는 독자여.

R.는 GCC가 최상위 수준에서 다음과 같은 것을 허용한다고 지적합니다.

register int errno asm ("r37");  // line R

이렇게 하면errno전지구적 가치로서r37분명히 스레드 로컬 수정 가능 lvalue가 됩니다.따라서, C의 준거 구현이 선언할 수 있는가?errno이렇게요?

대답은 '아니오'입니다.여러분이나 저나 선언이라는 단어를 사용할 때 우리는 보통 구어적이고 직관적인 개념을 염두에 두고 있습니다.그러나 이 기준은 구어체나 직감적으로 말하는 것이 아니라 정확하게 말하는 이며 명확한 용어만을 사용하는 것을 목표로 하고 있다."선언"의 경우, 표준 자체가 용어를 정의하며, 이 용어를 사용할 때는 자체 정의를 사용합니다.

스펙을 읽으면 선언이 무엇이고 무엇이 아닌지를 정확하게 알 수 있습니다.바꿔 말하면, 표준에는 "C"라는 언어가 기술되어 있습니다."C가 아닌 일부 언어"는 설명하지 않습니다.표준에 관한 한 "C with extensions"는 "C가 아닌 일부 언어"일 뿐입니다.

따라서 표준의 관점에서 라인 R은 선언이 아닙니다.해석도 안 돼!다음과 같이 표시됩니다.

long long long __Foo_e!r!r!n!o()blurfl??/**

사양에 관한 한, 이것은 라인 R만큼 "선언"입니다. 즉, 전혀 아닙니다.

따라서 C11 사양이 섹션 6.5.3.2에 기재되어 있는 경우:

단항 피연산자&연산자는 기능 지정자 중 하나여야 한다.[]또는 단항*연산자 또는 레지스터 스토리지 클래스 지정자로 선언되지 않은 비트 파일이 아닌 개체를 지정하는 l값.

...이것은 라인 R과 같은 어떤 것도 참조하지 않는 매우 정확한 것을 의미합니다.

이제 의 선언을 고려하겠습니다.int 반대하다errno참조. (주:나는 그 선언을 의미하는 것이 아니다.errno 이름, 물론 만약 그렇다면 그런 선언은 없을 수도 있기 때문이다.errno예를 들어, 매크로입니다.내 말은, 기초가 되는 사람들의 선언을 말하는 것이다.int object.)

The above language says you can take the address of an lvalue unless it designates a bit-field or it designates an object "declared" register. And the spec for the underlying errno object says it is a modifiable int lvalue with thread-local duration.

Now, it is true that the spec does not say that the underlying errno object must be declared at all. Maybe it just appears via some implementation-defined compiler magic. But again, when the spec says "declared with the register storage-class specifier", it is using its own terminology.

So either the underlying errno object is "declared" in the standard sense, in which case it cannot be both register and thread-local; or it is not declared at all, in which case it is not declared register. Either way, since it is an lvalue, you may take its address.

(Unless it is a bit-field, but I think we agree that a bit field is not an object of type int.)

The original implementation of errno was as a global int variable that various Standard C Library components used to indicate an error value if they ran into an error. However even in those days one had to be careful about reentrant code or with library function calls that could set errno to a different value as you were handling an error. Normally one would save the value in a temporary variable if the error code was needed for any length of time due to the possibility of some other function or piece of code setting the value of errno either explicitly or through a library function call.

So with this original implementation of a global int, using the address of operator and depending on the address to remain constant was pretty much built into the fabric of the library.

However with multi-threading, there was no longer a single global because having a single global was not thread safe. So the idea of having thread local storage perhaps using a function that returns a pointer to an allocated area. So you might see a construct something like the following entirely imaginary example:

#define errno (*myErrno())

typedef struct {
    // various memory areas for thread local stuff
    int  myErrNo;
    // more memory areas for thread local stuff
} ThreadLocalData;

ThreadLocalData *getMyThreadData () {
    ThreadLocalData *pThreadData = 0;   // placeholder for the real thing
    // locate the thread local data for the current thread through some means
    // then return a pointer to this thread's local data for the C run time
    return pThreadData;
}

int *myErrno () {
    return &(getMyThreadData()->myErrNo);
}

Then errno would be used as if it were a single global rather than a thread safe int variable by errno = 0;또는 체크하는 것처럼if (errno == 22) { // handle the error심지어 이런 것들도int *pErrno = &errno;이 모든 것은 스레드 로컬 데이터 영역이 할당되어 그대로 유지되며 이동하지 않기 때문에, 그리고 매크로 정의로 인해 다음과 같이 동작합니다.errno처럼 보이다extern int실제 구현의 배관을 숨깁니다.

우리가 원하지 않는 한가지는 주소입니다.errno값에 액세스하는 동안 동적 할당, 복제, 삭제 시퀀스를 사용하여 스레드의 타임 슬라이스 간에 갑자기 이동합니다.타임슬라이스가 올라가면 타임슬라이스가 올라가기 때문에 동기나 타임슬라이스가 만료된 후에도 CPU를 유지할 수 있는 방법이 없는 한 스레드 로컬 영역을 이동하는 것은 매우 어려운 제안이라고 생각합니다.

즉, 특정 스레드에 대해 일정한 값을 제공하는 연산자의 주소에 의존할 수 있습니다.단, 스레드마다 상수 값이 다릅니다.주소를 사용하면 도서관이 잘 보인다.errno라이브러리 함수가 호출될 때마다 일종의 스레드 로컬 룩업을 수행하는 오버헤드를 줄이기 위해서입니다.

주소를 가지고 있다errno또한 스레드 내의 상수로서 errno.h include 파일을 사용한 오래된 소스 코드와의 하위 호환성을 제공합니다(사용하지 말라고 명시적으로 경고하는 errno에 대해서는 Linux의 이 man 페이지를 참조하십시오).extern int errno;옛날에는 흔히 그랬듯이)

표준을 읽는 방법은 이전과 유사한 의미와 구문을 유지하면서 이러한 종류의 스레드 로컬 스토리지를 허용하는 것입니다.extern int errno;언제errno멀티 스레딩을 지원하지 않는 임베디드 디바이스의 크로스 컴파일러와 같은 종류의 크로스 컴파일러에 대해 오래된 사용을 허용합니다.그러나 매크로 정의의 사용으로 인해 구문이 비슷할 수 있으므로 이전 스타일의 단축 선언은 실제 선언과 다르므로 사용하지 마십시오.errno정말 그래요.

반례를 찾을 수 있습니다.비트필드는 타입을 가질 수 있기 때문입니다.int,errno비트필드일 수 있습니다.그런 경우에는&errno무효가 됩니다.표준 동작은 여기서 명시적으로 글을 쓸 수 있다고 말하지 않습니다.&errno정의되지 않은 동작의 정의는 여기에 적용됩니다.

C11 (n1570), § 4. 준거
정의되지 않은 행동은 이 국제표준에서 "정의되지 않은 행동"이라는 단어 또는 행동의 명시적 정의를 생략하여 달리 표시된다.

이것은 유효한 실장이라고 생각됩니다.&errno제약 위반이 될 수 있습니다.

struct __errno_struct {
    signed int __val:12;
} *__errno_location(void);

#define errno (__errno_location()->__val)

그래서 아마 대답은 '아니오'일 거예요

언급URL : https://stackoverflow.com/questions/12945486/is-errno-legal-c

반응형