programing

C/C++에서 함수 포인터와 데이터 포인터가 호환되지 않는 이유는 무엇입니까?

projobs 2022. 12. 10. 14:09
반응형

C/C++에서 함수 포인터와 데이터 포인터가 호환되지 않는 이유는 무엇입니까?

함수 포인터를 데이터 포인터로 변환하거나 그 반대로 변환하는 것은 대부분의 플랫폼에서 동작하지만 동작하는 것은 보증되지 않습니다.왜 그럴까?둘 다 단순히 메인 메모리로 주소만 지정되므로 호환성이 있어야 하지 않을까요?

아키텍처는 코드와 데이터를 같은 메모리에 저장할 필요가 없습니다.하버드 아키텍처에서는 코드와 데이터가 완전히 다른 메모리에 저장됩니다.대부분의 아키텍처는 코드와 데이터가 동일한 메모리에 있는 Von Neumann 아키텍처이지만 C는 가능하면 특정 유형의 아키텍처에만 제한을 두지 않습니다.

MS-DOS, Windows 3.1 및 그 이전 버전을 기억하는 사람들에게는 답은 매우 쉽다.이 모든 것은 코드와 데이터 포인터의 특성을 다양하게 조합하여 여러 다른 메모리 모델을 지원하는 데 사용됩니다.

예를 들어 콤팩트 모델(작은 코드, 큰 데이터)의 경우:

sizeof(void *) > sizeof(void(*)())

중형 모델(대형 코드, 소형 데이터):

sizeof(void *) < sizeof(void(*)())

이 경우 코드와 날짜를 따로 저장할 수 없지만 두 포인터 간에 변환할 수 없습니다(비표준 __near 및 __far 수식자 사용 제외).

게다가 포인터의 사이즈가 같다고 해도, 같은 것을 가리킨다는 보장은 없습니다.- DOS Small 메모리 모델에서는, 포인터 근처에서 사용되는 코드와 데이터 모두, 그러나 그것들은 다른 세그먼트를 가리켰습니다.따라서 함수 포인터를 데이터 포인터로 변환해도 함수와 전혀 관련이 없는 포인터가 제공되지 않으므로 이러한 변환은 필요하지 않습니다.

void에 대한 포인터는 모든 종류의 데이터에 대한 포인터를 수용할 수 있어야 하지만 반드시 함수에 대한 포인터일 필요는 없습니다.일부 시스템은 데이터에 대한 포인터와 함수에 대한 포인터에 대한 요구 사항이 다릅니다(예를 들어, 데이터 대 코드에 대한 주소가 다른 DSP가 있습니다. MS-DOS의 중간 모델은 코드에 32비트 포인터를 사용하지만 데이터에 대한 포인터만 사용합니다).

할 수 있는 유일한 은 정정 the the the the is is is 를 사용하지 입니다.dlsym하세요.dlsym함수 포인터를 포함하는 데이터에 대한 포인터를 가져옵니다.「 」 「 」 、 「 」

struct module foo_module = {
    .create = create_func,
    .destroy = destroy_func,
    .write = write_func,
    /* ... */
};

다음으로 어플리케이션에서 다음을 수행합니다.

struct module *foo = dlsym(handle, "foo_module");
foo->create(/*...*/);
/* ... */

이며, 붙붙, incident것 dynamic dynamic via via via via를 통한 동적 를 모두 쉽게 할 수 .또한 이 방법을 통해 동적 로드를 모두 간단하게 서포트할 수 있습니다.dlopen동적 링크를 지원하지 않는 시스템 또는 사용자/시스템 인테그레이터가 동적 링크를 사용하지 않는 시스템의 모든 모듈을 정적 링크합니다.

은 C++11의 C/C++ POSIX에 C 있습니다.dlsym()·······················를 사용할 수 있다reinterpret_cast구현이 이 기능을 지원하는 한 데이터 포인터 간에 함수 포인터를 변환합니다.

표준 5.2.10단락 8에서 "함수 포인터를 객체 포인터 타입으로 변환하거나 그 반대로 변환하는 것은 조건부로 지원된다." 1.3.5는 "실장이 지원할 필요가 없는 프로그램 구성"으로 정의한다.

함수 포인터의 크기가 데이터 포인터와 다를 수 있는 최신 예제입니다.C++ 클래스 멤버 함수 포인터

https://blogs.msdn.microsoft.com/oldnewthing/20040209-00/?p=40713/에서 직접 인용

class Base1 { int b1; void Base1Method(); };
class Base2 { int b2; void Base2Method(); };
class Derived : public Base1, Base2 { int d; void DerivedMethod(); };

두 이 있다.this포인터

함수에 .Base1 함수의 할 수 .Derivedthis, 멤버 함수에 입니다.Base2로는 사용할 수 로서 사용할 수 없습니다.Derived 이후로는this포인터를 조정해야 합니다.

이것을 해결하는 방법은 여러 가지가 있다.Visual Studio 컴파일러가 이를 처리하는 방법은 다음과 같습니다.

다중 상속 클래스의 멤버 함수에 대한 포인터는 실제로 구조입니다.

[Address of function]
[Adjustor]

에 대한 포인터 에 a의 입니다.size_t.

tl;dr: 다중 상속을 사용하는 경우 멤버 함수에 대한 포인터는 실제로 다음과 같이 저장됩니다(컴파일러, 버전, 아키텍처 등에 따라 다름).

struct { 
    void * func;
    size_t offset;
}

이은 a a a a a a a a a .void *.

일부 컴퓨터에는 코드와 데이터를 위한 별도의 주소 공간이 있습니다.이러한 하드웨어에서는 동작하지 않습니다.

이 언어는 현재 데스크톱 애플리케이션뿐만 아니라 대규모 하드웨어 세트에 구현할 수 있도록 설계되었습니다.


는 C 언어 사용을 의도한 적이 것 .void*그들은 단지 사물에 대한 일반적인 포인터를 원했을 뿐입니다.

C99의 근거는 다음과 같습니다.

6.3.2.3
C는 현재 광범위한 아키텍처에 구현되어 있습니다.이러한 아키텍처 중 일부는 어떤 정수 타입의 크기인 균일한 포인터를 특징으로 하지만, 최대 휴대용 코드는 다른 포인터 타입과 정수 타입 사이에 필요한 어떤 대응도 가정할 수 없다.구현에 따라서는 포인터가 정수형보다 넓을 수도 있습니다.

「 」의 void*')void이 유형의 채택은 임의의 포인터를 조용히 변환하는 함수 프로토타입 인수를 지정하려는 욕구에 의해 자극되었다.fread(「」의 경우와 에을 제기합니다strcmp함수에 대한 포인터에 대해서는 아무것도 언급하지 않습니다.이것은 오브젝트 포인터나 정수와 일치하지 않을 수 있습니다.

참고 마지막 단락에서 함수에 대한 포인터에 대해서는 아무 말도 하지 않았습니다.그것들은 다른 지적들과 다를 수 있고 위원회는 그것을 알고 있다.

여기에 이미 언급되어 있는 것 외에 POSIX를 살펴보는 것도 흥미롭습니다.

ISO C 표준은 함수에 대한 포인터를 데이터에 대한 포인터로 앞뒤로 캐스트할 수 있도록 요구하지 않습니다.실제로 ISO C 표준은 void * 유형의 객체가 함수에 대한 포인터를 보유할 수 있도록 요구하지 않습니다.단, XSI 확장을 지원하는 구현에서는 void* 타입의 객체가 함수에 대한 포인터를 유지할 수 있어야 합니다.그러나 함수에 대한 포인터를 다른 데이터 유형(void * 제외)에 대한 포인터로 변환한 결과는 아직 정의되지 않았습니다.ISO C 표준에 준거한 컴파일러는 다음과 같이 보이드* 포인터에서 함수 포인터로 변환하려고 하면 경고를 생성해야 합니다.

 fptr = (int (*)(int))dlsym(handle, "my_function");

여기서 언급된 문제 때문에, 미래 버전은 함수 포인터를 반환하는 새로운 함수를 추가하거나, 현재의 인터페이스는 데이터 포인터를 반환하는 것과 함수 포인터를 반환하는 것의 두 가지 새로운 함수를 위해 폐지될 수 있습니다.

또 다른 솔루션:

POSIX가 함수와 데이터 포인터가 동일한 크기와 표현임을 보증한다고 가정할 때(이것에 대한 텍스트를 찾을 수 없지만 OP가 인용한 예는 적어도 이 요구 사항을 만들기 위한 것임을 나타냅니다), 다음 사항이 작동해야 합니다.

double (*cosine)(double);
void *tmp;
handle = dlopen("libm.so", RTLD_LAZY);
tmp = dlsym(handle, "cos");
memcpy(&cosine, &tmp, sizeof cosine);

수 .char []모든 유형의 에일리어스를 지정할 수 있습니다.

또 다른 접근법:

union {
    double (*fptr)(double);
    void *dptr;
} u;
u.dptr = dlsym(handle, "cos");
cosine = u.fptr;

, 저는 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★를 추천합니다.memcpy정확하게 C. C를 합니다.

2012년 이후 코멘트가 없는 것은 알지만, 아키텍처에 대한 호출이 권한을 확인하고 추가 정보를 전송하기 때문에 데이터와 기능에 대한 매우 호환되지 않는 포인터를 가진 아키텍처를 알고 있다는 점을 추가하는 것이 도움이 될 것 같습니다.아무리 캐스팅해도 소용없다.제분소다.

타겟 아키텍처에 따라 코드와 데이터는 물리적으로 서로 다른 메모리 영역에 저장될 수 있습니다.

정의되지 않은 것은 반드시 허용되지 않는 것이 아니라 컴파일러 실장자가 원하는 방식으로 실행할 수 있는 자유를 더 많이 가질 수 있다는 것을 의미합니다.

예를 들어 일부 아키텍처에서는 불가능할 수 있습니다.정의되지 않은 아키텍처에서는 이를 수행할 수 없는 경우에도 적합한 'C' 라이브러리를 계속 사용할 수 있습니다.

공간 요구 사항이 서로 다른 유형일 수 있습니다.1에 할당하면 포인터의 값을 되돌릴 수 없게 슬라이스할 수 있기 때문에 다시 할당하면 다른 결과가 됩니다.

표준에서는 공간을 절약할 수 있는 구현이 불필요하거나 크기 때문에 CPU를 사용하기 위해 추가 작업을 수행해야 하는 경우를 제한하고 싶지 않기 때문에 다른 유형이 될 수 있다고 생각합니다.

대부분의 아키텍처에서 모든 일반 데이터 유형에 대한 포인터는 동일한 표현을 가지므로 데이터 포인터 유형 간의 캐스팅은 불가능합니다.

그러나 함수 포인터는 다른 포인터보다 큰 다른 표현을 필요로 할 수 있습니다.void*가 함수 포인터를 유지할 수 있는 경우 void*의 표현은 더 큰 사이즈가 되어야 합니다.또한 보이드*에 대한 모든 데이터 포인터 캐스트는 이 추가 복사를 수행해야 합니다.

누군가가 언급했듯이, 만약 당신이 이것이 필요하다면 당신은 연합을 통해 그것을 달성할 수 있습니다.그러나 void*의 대부분의 사용은 데이터만을 위한 것이므로 함수 포인터를 저장해야 할 경우에 대비하여 메모리 사용량을 늘리는 것은 부담이 됩니다.

언급URL : https://stackoverflow.com/questions/12358843/why-are-function-pointers-and-data-pointers-incompatible-in-c-c

반응형