2016-10-03 2 views
1
void qsort (void *a, size_t n, size_t es, int (*compare)(const void *, const void *) 

여기서 a는 배열 주소의 시작이고, n은 sizeof 배열이며, es는 sizeof 배열 요소입니다.C 코드에서이 코드의 의미는 무엇입니까?

내가 이해할 수없는 C에서 qsort의 소스 코드를 읽었습니다. 코드는 다음과 같습니다.

#define SWAPINT(a,es) swaptype = ((char*)a- (char*)0 % sizeof(long) || \ 
     es % sizeof(long) ? 2: es == sizeof(long)? 0 : 1 

나는하여이 매크로,

if(((char*)a- (char*)0)% sizeof(long))==1 || es % sizeof(long)==1) 
    swaptype = 2; 
else if(es== sizeof(long)) 
    swaptype = 0; 
else 
    swaptype = 1; 

을 해석하지만 형식 변환이 구현 된 이유는, (숯불 *)는 이해가 안 돼요.

이 줄의 의미는 무엇입니까?

(char*)a- (char*)0)% sizeof(long)==1 
+0

코드가 매우 손상된 것 같습니다. 매크로에 적어도 하나 이상의 구문 오류가 있습니다 (괄호가 누락되었습니다). 또한 누락 된 것이 없으면 (char *) a - (char *) 0은 no-op 여야합니다. 마찬가지로 (char *) 0 % sizeof (long). – jforberg

+0

'(char *) 0 % sizeof (long)'는 포인터 타입이 산술 타입이 아니기 때문에 의미가 없습니다. 이것이 무엇이든, 이것은 C를 따르지 않습니다.이 코드를 어디에서 찾았습니까? 그리고 올바르게 복사 했습니까? –

+1

'%'는'-' 때문에'(char *) a- (char *) 0 % sizeof (long)'는'(char *) a - ((char *) 0 % sizeof (long))'입니다. 확실히'((char *) a- (char *) 0) % sizeof (long)'이 필요했습니다. – chux

답변

4

해당 코드를 발견 한 곳마다 잘못 복사 한 것일 수 있습니다. 나는 libutil from Canu에서 매우 유사한 코드를 발견

c.swaptype = ((char *)a - (char *)0) % sizeof(long) || \ 
    es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; 

이 코드는 (저작권 라이센스의 조건을 위반 때문에) FreeBSD의의의 libc에서 복사 illegitimally 것 같았다 :

//__FBSDID("$FreeBSD: src/lib/libc/stdlib/qsort.c,v 1.12 2002/09/10 02:04:49 wollman Exp $"); 

그래서 내가 당신을 추측하고있어 * BSD libc 구현에서 가져 왔습니다. Indeedd의 FreeBSD의 퀵 구현은 the SWAPINIT macro (안 SWAPINT)를 포함

#define SWAPINIT(TYPE, a, es) swaptype_ ## TYPE =  \ 
     ((char *)a - (char *)0) % sizeof(TYPE) ||  \ 
     es % sizeof(TYPE) ? 2 : es == sizeof(TYPE) ? 0 : 1; 

분석 한 후, 위의 코드는 조건으로, 약

condition_one = ((char *)a - (char *)0) % sizeof(long); 
condition_two = es % sizeof(long); 
condition_three = es == sizeof(long); 
c.swaptype = (condition_one || condition_two) ? 2 : condition_three ? 0 : 1; 

참고 condition_two과 동일 함을 발견해야 es % sizeof(long) == 1과 같지만 es % sizeof(long) != 0과 같음이 아닙니다. 그 외에도 번역이 정확했습니다. a은으로 정렬 된 long하지 않을 때

  • condition_onetrue입니다 :


    이러한 조건의 목적은 다음과 같이 될 것으로 보인다.

  • condition_twoeslong의 배수가 아닌 경우 true입니다.
  • condition_threees이 정확히 long 일 때 true입니다. 당신이 교환에 대한 영리 요소에 대한 충분한 보장이 없을 때 그 결과

,

  • swaptype == 2
  • swaptype == 1
  • long 따라 정렬되는 요소와 배열을위한 것입니다입니다 경계 (참고 : 반드시 으로 정렬됩니다!)이고
  • swaptype == 0은 앞의 설명과 일치하는 배열을 대상으로하며 요소에는 long 크기가 지정되어 있습니다. avoid*, for which type arithmetic is undefined을 입력 있기 때문에

이 경우 명시 적 형식 변환이 있습니다. 그러나,도 ((char *)a - (char *)0) 미정 너무되어 있습니다 두 포인터가 감산 될 때

가 모두 동일한 배열 객체 또는 배열 객체의 마지막 요소 지난 하나의 요소를 가리한다; 결과는 두 배열 요소의 첨자의 차이입니다.

(C11 초안 N1570, 섹션 6.5.6, 페이지 93 및 94에 9 절) 그것은 정확히 C11에서 철자가 아니에요

하지만, 널 포인터는 같은 배열의 일부가 아닙니다 a이 가리키는 개체이므로 포인터 연산에 대한 기본 규칙이 위반되므로 동작이 정의되지 않습니다.

1

매크로는이 테스트를 실제로 허용하지 않는 언어 C로 이식 가능하게 검사하려고 시도합니다. 그래서 우리 포인터에서 널 포인터를 빼서 정수를 얻은 다음 계수의 크기를 long으로 취합니다. 결과가 0이면 데이터가 길게 정렬되고 long으로 액세스 할 수 있습니다. 그렇지 않은 경우 다른 방법을 시도 할 수 있습니다.

+0

이 경우 C11의'_Alignas'와'_Alignof '를 사용할 수 없습니까? –

+0

길이가 짧더라도이 답변을 더 잘 이해했다고 생각합니다. 그러나 그 정수는 무엇을 의미합니까? 배열의 시작 주소입니까? 배열이 오래 걸리는 것이 왜 중요합니까? 예를 들어 바이트 배열 인 경우? – Asoub

+0

두 개의 포인터를 빼면 정수 사이의 자리 수를 나타내는 정수 ("int"가 아님)가 표시됩니다. char *에서 NULL을 빼는 것은 아무 작업도하지 않지만, 컴파일러가 포인터를 정수로 받아들이고 그것에 계수를 허용하는 것을 속입니다. 메모리 공간이 세분화되어있는 경우에는 조금 까다 롭지 만 또 다른 이야기입니다. –

0

설명에서 언급 한대로 %의 왼쪽 피연산자가 char * 인 여기에 계산 (char*)0 % sizeof(long)이 포함되어 있기 때문에 제시 한 매크로 정의가 유효한 C 코드로 확장되지 않습니다. 이는 정수 유형이 아니지만, %의 두 피연산자 모두 정수 유형을 가져야합니다.

매크로의 확장은 불균형 한 괄호를가집니다. 그것은 이 본질적으로이 아니지만 그 매크로를 사용하는 것은 까다로울 수 있습니다. 또한 연산자 우선 순위가 합리적인 결과를내는 경우에도 괄호와 여분의 공백을 사용하면 코드를 사람이 해석 할 수 있고 실행 속도에 아무런 영향을 미치지 않으며 추가 컴파일 비용을 무시할 수 있습니다.

그래서, 내가 원하는 매크로가 더 같이있을 거라고 생각 : 나는에서 표현의 복잡성을 줄이기 위해

 : (es != sizeof(long)) 

로 끝에서 두 번째 라인을 작성하는 대신 생각 하는데요

#define SWAPINT(a,es) swaptype = (         \ 
    ((((char*)a - (char*)0) % sizeof(long)) || (es % sizeof(long))) \ 
     ? 2               \ 
     : ((es == sizeof(long)) ? 0 : 1))       \ 
) 

그것의 이해력에 약간의 비용. aes 경우 하지n 바이트의 수가 long에있는 n -byte 경계에 정렬, 또는이다

  • 2 경우 : 어떤 경우에, 의도는 swaptype로 설정하는 것으로 보인다 크기의 정수 배가 아닌 long; 그렇지 않으면
  • 1eslong의 크기와 같지 않으면; 그렇지 않으면
  • 당신의 해석, 유사하지만 동일하지의
  • 0

. 그러나이 코드에서도 (char*)a - (char*)0 때문에 정의되지 않은 동작이 있음에 유의하십시오. 그 차이를 평가하는 것은 두 포인터가 같은 객체를 가리키거나 그 끝을 지나치는 경우에만 동작을 정의하고 (char *)0은 어떤 객체의 끝 또는 끝을 가리 키지 않습니다.

당신은 구체적으로 질문 :

그러나 형 변환 (숯불 *)를 구현하는 이유를 이해하지 않습니다. 포인터 산술 연산에 의해 정의되기 때문에 수행

첨예-너무 (1), 순응 프로그램이 void *로 연산을 수행 할 수 있으며, 입력 (2) 코드는 감산 결과를 원한다 sizeof 연산자 (바이트)의 결과와 같은 단위가됩니다.

이 줄의 의미는 무엇입니까?

(char*)a- (char*)0)% sizeof(long)==1 

그 라인은 제시 매크로에 표시되지 않으며, 때문에 불균형 괄호의 완전한 표현이 아니다. an 바이트 경계보다 1을 가리키는 지 여부를 결정하려고 시도하는 것처럼 보입니다. 여기서 n은 정의 된 바와 같지만 포인터 차이를 평가하는 것은 정의되지 않은 동작을가집니다. x 정수의 경우 부울 컨텍스트에서 평가 된 x % sizeof(long) == 1은 동일한 컨텍스트에서 계산 된 x % sizeof(long)과 다른 의미를가집니다. 후자는 당신이 묘사 한 맥락에서 더 의미가 있습니다.

관련 문제