문제

다음 선언이 있습니다.

void qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int strcmp(char *s, char *t);

그런 다음 프로그램 어딘가에 다음 호출이 있습니다.

  qsort((void**) lineptr, 0, nlines-1, 
                    (int (*)(void*,void*))(numeric ? numcmp : strcmp));

(처음 세 가지 인수를 무시하십시오 numeric).

나는 이것이 무엇인지 묻습니다.

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

나는 이해 qsort "포인터가 2 개를 얻는 포인터"를 기대하고 있습니다. void 포인터와 반환 int"그것은 네 번째 논쟁이지만 위에 쓰여진 것이 어떻게 그것을 만족 시키는가? 그것은 두 개의 괄호로 만들어 졌기 때문에 어떤 종류의 캐스트처럼 보이지만, 그것은 매우 이상한 캐스트 일 것입니다. 그것은 기능을 취하고이 기능을 만들기 때문에" 두 가지를 얻는 기능에 대한 포인터 void 포인터와 반환 int". 의미가 없습니다.
(나는 여기서 유형의 규칙을 따랐다 type 변수가 변수를 해당 유형으로 촉진하기 전에 괄호 안에서).

그래서 난 그냥 잘못 생각한다고 생각합니다. 누군가가 이것을 읽는 방법을 말해 줄 수 있습니다. 주문은 무엇입니까?

도움이 되었습니까?

해결책

여기서 일어나는 일은 실제로 캐스트입니다. 제 3 초 동안 2 초 동안 무시하고 NUMCMP가 항상 사용되는 척하십시오. 이 질문의 목적을 위해 기능은 C의 기능 포인터 역할을 할 수 있습니다. 따라서 숫자의 유형을 보면 실제로는입니다.

(int (*)(int*,int*))

QSORT에서 올바르게 사용하려면 무효 매개 변수가 있어야합니다. 여기의 유형은 모두 매개 변수 및 반환 유형과 관련하여 동일한 크기를 가지므로 다른 사람을 대체 할 수 있습니다. 필요한 것은 컴파일러를 행복하게 만드는 캐스트입니다.

(int (*)(void*,void*))(numcmp )

다른 팁

당신은 여기서 트릭을 놓쳤습니다 - 부분

(numeric ? numcmp : strcmp)

Ternary 운영자를 사용하여 선택합니다 어느 QSORT 내부에서 기능이 호출되고 있습니다. 데이터가 숫자 인 경우 NUMCMP를 사용합니다. 그렇지 않은 경우 strcmp를 사용합니다. 보다 읽기 쉬운 구현은 다음과 같습니다.

int (*comparison_function)(void*,void*) = 
    (int (*)(void*,void*))(numeric ? numcmp : strcmp);
qsort((void**) lineptr, 0, nlines-1, comparison_function);

기능 포인터 캐스트없이 수행 할 수 있습니다. 여기에 있습니다 어떻게. 내 경험상, 대부분의 장소에서 캐스트를 사용하는 경우 잘못하고 있습니다.

표준 정의에 주목하십시오 qsort() 포함 const:

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

문자열 비교기에 두 가지가 주어집니다.char **'값,'char *'가치.

호출 코드에서 캐스트가 불필요하게 비교기를 작성합니다.

#include <stdlib.h>    /* qsort() */
#include <string.h>    /* strcmp() */

int num_cmp(const void *v1, const void *v2)
{
    int i1 = *(const int *)v1;
    int i2 = *(const int *)v2;
    if (i1 < i2)
        return -1;
    else if (i1 > i2)
        return +1;
    else
        return 0;
}

int str_cmp(const void *v1, const void *v2)
{
    const char *s1 = *(const char **)v1;
    const char *s2 = *(const char **)v2;
    return(strcmp(s1, s2));
}

사람들이 당신의 기능을 사용하여 코드에 캐스트를 작성하도록 강요하는 것은 추악합니다. 하지 않다.

내가 쓴 두 가지 기능은 표준에 필요한 기능 프로토 타입과 일치합니다. qsort(). 괄호 안에 없을 때 함수의 이름은 함수에 대한 포인터와 동일합니다.

구형 코드 또는 이전 컴파일러에 자란 사람들이 작성한 코드에서 찾을 수 있으며, 기능에 대한 포인터는 다음 표기법을 사용하여 사용됩니다.

result = (*pointer_to_function)(arg1, arg2, ...);

현대 스타일로는 다음과 같습니다.

result = pointer_to_function(arg1, arg2, ...);

개인적으로, 나는 명백한 불일치가 더 명확하지만 모든 사람이 동의하는 것은 아닙니다.

그 코드 스 니펫이 너무 영리하려고했던 사람은. 그의 마음에, 그는 아마도 자신이 영리한 "하나의 라이너"를 만들어서 훌륭한 프로그래머라고 생각할 것입니다. 실제로, 그는 읽기 쉬운 코드를 만들고 있으며 장기적으로 함께 일하는 것이 불쾌합니다. Harper Shelby의 코드와 유사한보다 명백한 형태로 다시 작성해야합니다.

Brian Kernighan의 속담을 기억하십시오.

디버깅은 처음에 코드를 작성하는 것보다 두 배나 어렵습니다. 따라서 코드를 최대한 영리하게 작성하면 정의상 디버깅 할만 큼 똑똑하지 않습니다.


나는 실시간 마감일로 많은 성능 크리티컬 코딩을하고 있습니다 ... 그리고 여전히 울창한 1 라이너가 적절한 곳을 보지 못했습니다.

ASM을 컴파일하고 확인하여 One-Liner가 더 나은 컴파일 된 ASM 구현을 가지고 있는지 확인하기 위해 ASM을 확인했습니다.

다른 사람들이 지적했듯이

(int (*)(void*,void*))(numeric ? numcmp : strcmp)

다음은 유형 캐스트입니다

(int (*)(void*,void*))

그리고 표현은입니다

(numeric ? numcmp : strcmp)

C 선언은 읽기가 매우 어려울 수 있지만 배울 수 있습니다. 이 방법은 내부 부분에서 시작한 다음 오른쪽으로 한 단계로 이동 한 다음 한 단계 왼쪽으로, 오른쪽, 왼쪽, 오른쪽, 왼쪽 등을 바깥쪽으로 끝내는 것입니다. 내부의 모든 것이 평가되기 전에 괄호 안에 교차하지 않습니다. 예를 들어 위의 유형에 대해서는 (*) 이것이 포인터임을 나타냅니다. 포인터는 괄호 안에 유일한 것이 었으므로 우리는 바깥 쪽 오른쪽으로 평가합니다. (void*,void*) 두 개의 포인터 인수가있는 함수에 대한 포인터임을 나타냅니다. 드디어 int 함수의 리턴 유형을 나타냅니다. 외부 괄호는 이것을 유형 캐스트로 만듭니다. 업데이트 : 두 가지 자세한 기사 : 시계 방향/나선형 규칙 그리고 C 선언 읽기 : 신비한 가이드.

그러나 좋은 소식은 위의 내용이 아는 것이 매우 유용하지만 속이는 매우 간단한 방법이 있습니다: cdecl 프로그램은 C에서 영어로 변환 할 수 있으며 그 반대도 마찬가지입니다.

cdecl> explain (int (*)(void*,void*))
cast unknown_name into pointer to function (pointer to void, pointer to void) returning int
cdecl> declare my_var as array 5 of pointer to int
int *my_var[5]
cdecl>

운동 : 어떤 종류의 변수입니다 i?

int *(*(*i)[])(int *)

답변 rot13 컴퓨터에 CDECL이 설치되어 있지 않은 경우 (그러나 실제로해야합니다!) :

pqrpy> rkcynva vag *(*(*v)[])(vag *)
qrpyner v nf cbvagre gb neenl bs cbvagre gb shapgvba (cbvagre gb vag) ergheavat cbvagre gb vag
pqrpy>

아마 이것을 읽을 것입니다.

typedef int (*PFNCMP)(void *, void *);

PFNCMP comparison_function;

if (numeric)
{
    comparison_function =  numcmp;
}
else
{
    comparison_function = strcmp;
}

qsort((void**) lineptr, 0, nlines-1, comparison_function);

문제의 예에는 명시적인 사례가 있습니다.

당신의 논리가 맞습니다. 실제로 메소드 서명에 의해 필요한 유형 인 "두 개의 공간 포인터를 가져오고 int를 반환하는 기능을위한 포인터"로 캐스팅됩니다.

둘 다 numcmp 그리고 strcmp 두 가지를 취하는 기능에 대한 포인터입니다 char* 매개 변수로서 반환합니다 int. 그만큼 qsort 루틴은 2가 취하는 함수에 대한 포인터를 기대합니다 void* 매개 변수로서 반환합니다 int. 따라서 캐스트. 이것은 안전합니다 void* 일반적인 포인터 역할을합니다. 이제 선언을 읽을 때 : 당신의 strcmp의 선언 :

 int strcmp(char *, char *);

컴파일러는 다음과 같이 읽습니다 strcmp 실제로 :

 int (strcmp)(char *, char *)

기능 (대부분의 경우 함수에 대한 포인터로 부패)가 char * 논쟁. 포인터의 유형 strcmp 따라서 :

 int (*)(char *, char *)

따라서 호환되기 위해 다른 기능을 시전해야 할 때 strcmp 당신은 위를 유형 캐스팅.

마찬가지로, 그 이후로 qsort비교기 인수는 두 가지가 필요합니다 void *S와 이상한 캐스트!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top