2009-06-22 4 views
32

malloc 구현을위한 void 포인터의 사용법을 이해합니다.void 포인터를 언제 사용합니까?

누구나 다른 이유를 제안하거나 실제로 유용한 시나리오를 제공 할 수 있습니까? 포인터를 검색 할 때

감사

답변

11

당신이 C 코드와 인터페이스와 C++ 객체를 통과해야하지만 C 라이브러리는 일반 포인터를 취할 것하는 경우에, 당신은 그것을 캐스트 다시 할 필요가 적절한 유형.

void 포인터는 자주 사용하면 안되지만 임의 포인터로 작동하는 라이브러리 함수를 사용하려고 할 때 도움이 될 수 있으며 실제로 해당 메모리가 나타내는 데이터가 신경 쓰이지는 않습니다.

3

void * 및 기타 C 주제에 대해 모두 배우는 훌륭한 방법은 iTunes-U에서 환상적인 스탠포드 "프로그래밍 패러다임"의 전반부를 보는 것입니다. 사실 void * (C generics)와 포인터를 일반적으로 환상적으로 설명합니다! 그것은 확실히 내가 C 언어를 배우는 것을 도왔습니다 ...

가장 큰 용도 중 하나는 함수에서 다른 유형의 데이터를 받아 들일 수 있기를 원할 때 void *를 사용하는 것입니다. (를 heres 예 : http://142.132.30.225/programming/node87.html) : 당신은 무료 스탠포드 클래스를보고 싶어하지 않는 경우

int i; 
    char c; 
    void *the_data; 

    i = 6; 
    c = 'a'; 

    the_data = &i; 
    printf("the_data points to the integer value %d\n", *(int*) the_data); 

    the_data = &c; 
    printf("the_data now points to the character %c\n", *(char*) the_data); 

것은, 내가 인터넷 검색을 무효을 권하고 싶습니다 여기

은 당신이 사용할 수있는의 또 다른 예입니다 포인터와 모든 자료를 거기에서 읽는 것.

+0

그렇습니다.하지만 적절한 포인터 유형으로 변수를 만들면 어떨까요? void *에 저장된 데이터 유형을 추적해야하는 번거 로움을 겪는 이유는 무엇입니까? 왜 그냥 int * 또는 char *를 사용하지 않는가? – Kekoa

+0

void *는 제네릭 함수 (다른 형식을 사용하거나 반환하는 함수)를 만들 때 가장 유용합니다. 당신은 각각을위한 함수를 가질 필요없이 int, double 또는 long을 취할 수있는 함수를 가질 수 있습니다! – micmoo

1

void 포인터는 여러 운영 체제에서 실행해야하는 코드를 작성하고 기본 프레임 워크 API를 상당히 불가지론해야 할 때 유용합니다.

예를 들어, OS X, Windows 및 Linux는 모두 창 객체의 기본 개념을 가지고 있지만 모두 매우 다릅니다. 그래서 void *와 네이티브 타입 (HWND 등)으로 void *를 캐스팅하는 플랫폼 특정 구현물을 전달하는 공통 코드가 있습니다.

하지만 다른 사람들이이 글에서 말했듯이 이런 종류의 일은 필연적으로 필요한 경우를 제외하고는 피할 수 있습니다.

8

void 데이터 블록의 내용이 중요하지 않을 때마다 포인터를 사용해야합니다. 예를 들어 데이터를 복사 할 때 메모리 영역의 내용이 복사되지만 데이터 형식은 중요하지 않습니다.

void 포인터를 사용하여 내용을 이해할 필요없이 메모리 블록에서 작동하는 함수의 경우 사용자가 디자인을 명확하게하여 함수가 데이터 형식을 고려하지 않음을 알 수 있습니다. 종종 함수는 실제로 함수에 컨텐트가 불가지론 할 때을 사용하여 메모리 블록을 처리하도록 코딩되었습니다.

1

sqlite3_exec()을 확인하십시오. SQL 쿼리를 시작하고 어떤 방식으로 결과를 처리하고자합니다 (컨테이너에 저장). sqlite3_exec()를 호출하고 원하는 객체 (컨테이너 포함)에 콜백 포인터와 void * 포인터를 전달합니다. sqlite3_exec()가 실행될 때 검색된 각 행에 대한 콜백을 호출하고 해당 void * 포인터를 전달하여 콜백이 포인터를 캐스팅하고 의도 한대로 수행 할 수 있도록합니다.

중요한 것은 sqlite3_exec()가 콜백이하는 일과 전달하는 포인터에 신경 쓰지 않는다는 것입니다. void *는 그러한 포인터를위한 것입니다.

29

하나의 좋은 시나리오 은 어떤 유형의 데이터를 유지하고 처리할지 모르기 때문에 일반 ADT를 구현하고자 할 때 사용합니다. 예를 들어, 다음과 같은 링크 된 목록 :

여기
typedef struct node_t node; 
struct 
{ 
    void* data; 
    node* prev, next; 
} node_t; 

typedef struct list_t list; 
typedef void* (func)(void*) cpy_func; 
typedef void (func)(void*) del_func; 
struct 
{ 
    node* head, tail, curr; 
    cpy_func copy; 
    del_func delete; 
} list_t; 

initializeLinkedList(cpy_func cpy, del_func del); 
//here you keep going defining an API 

는 예를 들어, 당신은 당신의 목록에 데이터 유형을 복사하고 나중에 그것을 확보 할 수있을 것입니다 다른 기능에 초기화 함수 포인터로 전달됩니다. 따라서 void*을 사용하면 목록을보다 일반적인 것으로 만들 수 있습니다.

내 생각에 void*은 C++에서 이전 버전과의 호환성 때문에 남아 있습니다. C++에서는 템플릿, 펑터 등과 같은 동일한 결과를 얻을 수있는보다 안전하고 정교한 방법이 있기 때문에 프로그래밍 할 때 malloc을 사용할 필요가 없기 때문입니다. C++.

C++와 관련하여 특별한 유용한 예제가 없습니다.

double find_root(double x0, double (*f)(double, void*), void* params) 
{ 
/* stuff */ 
y = f(x, params); 
/* other stuff */ 
} 

params이 그것을 알고있는 몇 가지 구조에 f에 의해 캐스팅되어 있지만, find_root하지 않습니다

+0

감사합니다 Artem.Thats 문제. 또한 C의 경우 char *를 사용하여 동일한 것을 구현할 수 있습니다. char *를 대신 사용할 수없는 시나리오를 생각할 수 없었습니다. (malloc에 ​​적용됨). – Mac13

+0

C++ 관용구는 템플릿을 사용하고 표준 라이브러리는 유용한 목록 (링크 된 목록 포함)을 제공합니다. –

+2

'char * '대신'void *'를 사용하면 타입 안전성을 확실하게 측정 할 수 있습니다. 당신은 컴파일러에게 "나는이 포인터들 중 하나를 참조 해제해서는 안된다."라고 말하고있다. – Novelocrat

2

은 일반적으로 예를 들어 C 루트 솔버 기능처럼 보일 수 있습니다, 숫자 코드에 사용됩니다. INT, 긴, 더블, 숯불 * 또는 일부 : 당신은 어떤 유형의 배열을 정렬 할 수 있습니다

void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); 

:

+3

좀 더 일반적으로,'void *'는 콜백 함수와 누군가가 함수를 인자로 넘겨 줄 필요가있는 다양한 것들에 좋은 인자를 만듭니다 , 그러나 그 논쟁의 유형을 알 필요는 없어야한다. – Novelocrat

3

무효 *로 구현 같은 C "제네릭"의 또 다른 예는, 표준를 qsort 함수이다 구조체 포인터 ...

2

다음으로 C 인터페이스를 사용하기 위해 일부 코드를 디버깅/추적해야하고 특정 포인터의 주소를 알고 싶을 때만 void 포인터를 사용합니다.

SomeClass * myInstance; 
// ... 
std::clog << std::hex << static_cast< void* >(myInstance) << std::endl; 

0x42A8C410 

처럼 뭔가를 인쇄하고, 내 의견으로는, 잘 나는 (포인터 주소, 인스턴스에 대해 아무것도하지 알고)

+2

디버거를 사용해 보셨습니까? – Eva

-3
int (*f) (void); 
f =(void*) getprocaddress(dll,"myfunction"); 
을 할 노력하고있어 문서화 것인가

컴파일러를 행복하게 만들 수 있습니다.

+5

이것은 컴파일되지 않습니다. 컴파일러를 행복하게 만드는 방법은 완전히 불분명합니다. – AnT

1

은 실제로 C-ism이며 C가 몇 가지 작업을 수행 할 수 있습니다. 그것은 합리적으로 달리 할 수 ​​없다는 것입니다.

char *은 다른 플랫폼에서 다른 유형의 포인터를 만들 수 있으므로 이식 할 수 없습니다. char *은 반드시 void *과 동일하게 (또는 동일한 크기로) 처리 할 필요가 없습니다.

C에서 데이터 유형을 알 수 없거나 다형성 또는 동적 인 경우 void *을 사용하면 올바른 포인터 유형 (올바르게 가리킬 수있는 포인터 유형)을 생성 할 수 있습니다.

C++에서 void *은 일반적으로 레거시 C 코드와 인터페이스하는 것을 제외하고 절대로 발생해서는 안됩니다.

1

무효 포인터를 사용하면 큰 이점이 있습니다. 포인터 변수는 다른 변수의 주소를 저장하는 변수입니다. 예 :

int a; 
int *x= &a; 

이제 'X'정수 변수의 주소를 저장한다.

는하지만이 사람은 실패 정수 포인터 변수는 정수 변수 주소를 저장할 수

float f; 
int *x = &f; 

때문입니다. 같은 방식으로 다른 데이터 유형에도 적용됩니다.

void * pointer를 사용하면 모든 TYPE 변수의 주소를 저장할 가장자리가 생깁니다.

void *pointer = &i; 
void *pointer = &f; 

검색 중에는이를 참조해야합니다.

*((int*)pointer) 

그래서주의해서 무효 포인터를 사용하십시오.

감사의 인사로 도움이 될 것입니다.

7

C++에서는 void * 포인터에 대한 가장 강력한 사용 사례가 코드에 이미 "사용중인 개체에 임의의"사용자 데이터를 저장하는 옵션을 제공한다는 것을 알았습니다.

Car 객체 (교통 시뮬레이션, 렌터카 재고 등)와 함께 유용한 것들을 수행하는 소프트웨어에서 사용하기 위해 Car을 나타내는 클래스를 작성했다고 가정 해 보겠습니다. 이제 응용 프로그램이 Car 트렁크의 임의 내용을 추적하려고하는 상황에서 자신을 발견했다고 가정 해 봅시다. 트렁크에 저장된 내용의 세부 사항은 Car 클래스에서 중요하지 않으며 무엇이든 될 수 있습니다. 실제로는 Car 클래스를 사용하는 응용 프로그램의 용도에 따라 달라집니다. void * 포인터를 입력하십시오.

class Car 
{ 
    public: 

     // Existing methods of your Car class 

     void setContentsOfTrunk(void* contentsOfTrunk); 
     void* contentsOfTrunk() const; 

    private: 

     void* m_contentsOfTrunk; 
} 

지금, 당신의 Car 클래스를 사용하는 응용 프로그램은이 Car 목적이있는 코드에서 얻을 수 있도록 기존 Car 객체에 임의의 데이터 객체를 첨부 할 수있는 옵션이 있습니다. 트렁크의 내용은 코드에서 언제든지 Car 개체와 함께 이동합니다.

이 경우 void *를 사용하는 데는 두 가지 방법이 있습니다.

첫 번째는 트렁크 내용의 유형에 따라 클래스를 템플릿하는 오브젝트 :이 불필요하게 침습적 인 것 같다

template <class TrunkContentsType> 
class Car 
{ 
    public: 

     // Existing methods of your Car class 

     void setContentsOfTrunk(TrunkContentsType contentsOfTrunk); 
     TrunkContentsType contentsOfTrunk() const; 

    private: 

     TrunkContentsType m_contentsOfTrunk; 
} 

. 트렁크의 내용 유형은 응용 프로그램에서만 중요합니다. Car 객체로 작업하는 알고리즘 및 데이터 구조는 트렁크의 내용을 신경 쓰지 않습니다. 클래스 템플릿을 작성하면 클래스를 사용하는 응용 프로그램이 트렁크 내용의 유형을 선택하도록 강요하지만 대부분의 경우 응용 프로그램은 트렁크 내용을 신경 쓰지 않습니다.

번째 대안은 트렁크 컨텐츠에 대한 데이터 부재 접근 추가 자동차로부터 새로운 클래스를 유도하는 것이다 :

class Car 
{ 
    public: 

     // Existing methods of your Car class 
     // No methods having anything to do with trunk contents. 

    private: 

     // No data member representing trunk contents. 
} 

class CarWithTrunkContents 
{ 
    public: 

     // Existing methods of your Car class 

     void setContentsOfTrunk(TrunkContentsType contentsOfTrunk); 
     TrunkContentsType contentsOfTrunk() const; 

    private: 

     TrunkContentsType m_contentsOfTrunk; 
} 

새로운 CarWithTrunkContents 클래스 것은의 데이터 멤버를 추가하는 애플리케이션 특정 클래스 트렁크 내용을 자동차에 저장해야하는 응용 프로그램을 입력하십시오. 이것은 또한 불필요하게 중량급 인 것처럼 보입니다. 왜 클래스의 동작에 영향을주지 않는 추가 데이터를 추가하기 위해 완전히 새로운 클래스를 파생해야합니까? 그리고 Car 클래스를 사용하는 응용 프로그램이 트렁크 내용을 저장하려는 경우 일반적으로 각 응용 프로그램이 특정 유형의 트렁크 내용에 대해 새 클래스를 유도하도록하는 이유는 무엇입니까? 트렁크 내용의 내 인위적인 예 아마 연습에 Car 객체와 함께 여행하는 임의의 트렁크 내용의 생생한 그림을 그린다 동안

마지막으로, 당신은 가능성이 Car에 응용 프로그램 별 데이터를 부착 더욱 일반적인 메커니즘을 제공 할 것이다 :

이렇게하면 응용 프로그램에서 트렁크 내용을 나타내는 개체 나 운전 면허증과 등록을 나타내는 개체 또는 임대 계약을 나타내는 개체 등을 첨부 할 수 있습니다. 나는 이런 종류의 void * pointer를 "userData"(클래스의 사용자가 이해할 수있는), "blindData"(즉, 클래스가 전달하는 객체의 내용을 보지 못함) 또는 "applicationData" 즉 응용 프로그램에서 정의한 유형 및 목적의 데이터).

관련 문제