2009-10-26 2 views
1

다음 코드 행에서 해당 필드 중 하나에서 바이트로 오프셋 pm 포인터를 조정해야합니다. 포인터 연산이 여전히 작동하도록 char *PartitionMap *에서 끊임없이 앞뒤로 캐스팅하는 것보다 더 쉽고/더 쉬운 방법이 있습니까? 코드에서 grok 수없는 것과 들어 바이트 오프셋을 사용한 조작을위한 클리너 포인터 산술 구문

PartitionMap *pm(reinterpret_cast<PartitionMap *>(partitionMaps)); 
for (; index > 0 ; --index) 
{ 
    pm = (PartitionMap *)(((char *)pm) + pm->partitionMapLength); 
} 
return pm; 

, 그것은 PartitionMap 상속 버퍼에 가변 길이 디스크립터 통해 반복있다.

해당되는 경우 partitionMapLength는이 시스템이 실행되는 시스템에서 지원하는 길이를 항상 반환합니다. 가로 지르는 데이터는 UDF 사양을 따릅니다.

+0

실제로 루프가 무엇입니까? pm이 동일한 값을 갖는 'index'시간을 할당받은 것처럼 보입니다 –

+1

pm -> partitionMapLength에서 발견 된 값으로 pm의 값을 변경 한 다음, 다시 그리고 다시합니다 ... 아마 운율과 이유가 있습니다 데이터 구조는 조금 이상하게 보입니다. 그것은 데이터를 통해 가변 크기의 단계를 허용합니다. –

+0

네, 맞습니다. 조나단입니다. 저는 장치에서 읽은 섹터 크기의 청크 바이트를 단계별로 처리하기 위해이 작업을 수행하고 있습니다. –

답변

5

나는 종종이 이러한 템플릿을 사용하면 왜

T *x = add_pointer(x, 1); // increments x by one byte, regardless of the type of x 
+3

일반적으로 구조 포인터에 1 바이트를 추가하는 것은 SIGBUS 오류 (코어 덤프)를위한 방법입니다. 적어도 구조와 포인터를 적절하게 정렬해야하는 컴퓨터에서 수행해야합니다. 그렇다고해서 기능이 유용하지 않게되는 것은 아닙니다. 그러나 그 예가 어느 정도 바람직한 것으로 남았습니다. –

+0

좋은 아이디어, 나는 종종 다른 물건을 위해 이런 식으로 템플릿 래퍼를 작성합니다. C++는 때때로 PITA입니다. –

+0

하지만, 조나단, 나는 네가하는 일을 혼란스러워한다. – GManNickG

5

캐스팅은 char * 또는 intptr_t 또는 다른 유형과 상관없이 마지막 유형으로 캐스팅하는 유일한 방법입니다.

+0

+1. 그 코드의 외관을 정리하는 매크로를 작성할 수 있습니다. –

+1

또는 Evan이 입증 한 기능. 그리고 기능은 대개 매크로보다 선호됩니다. – GManNickG

0

캐스팅이 완료되어야하지만 코드를 거의 읽을 수 없게 만듭니다. 가독성을 위해 static inline 함수로 분리하십시오. 예를 들어,

template<typename T> 
    T *add_pointer(T *p, unsigned int n) { 
      return reinterpret_cast<T *>(reinterpret_cast<char *>(p) + n); 
    } 

    template<typename T> 
    const T *add_pointer(const T *p, unsigned int n) { 
      return reinterpret_cast<const T *>(reinterpret_cast<const char *>(p) + n); 
    } 

그들은 유형을 유지하지만, 그들에게 하나의 바이트를 추가합니다 :

0

무엇 나를 당혹 것은 ' partitionMapLength '(바이트)?

어쨌든 캐스트했기 때문에 'partitionMap'단위에 있다면 더 좋지 않겠습니까? char * 버퍼를 통해 단계와 PartitionMap *가 액세스 할 수 :

PartitionMap *pmBase(reinterpret_cast<PartitionMap *>(partitionMaps)); 
PartitionMap *pm; 
... 
pm = pmBase + index; // just guessing about your 'index' variable here 
+0

아니요, 길이가 다릅니다. 당신이 마술 적으로 길이를 알아낼 방법을 알지 못한다면 :) –

+0

나는 파티션 맵을위한 스펙을 작성하지 않았고, 나는 그것을 단지 C++ 구조체로 변환했다. 그리고 지금 나는 값들을 가지고있다. –

+2

구조체가 가변 길이 데이터로 끝나는 것은 그리 특이하지 않습니다. 이 코드는 그러한 구조체의 '압축 된 배열'을 따라 걸을 것입니다. –

3

당신은 물론 단지 주변에 두 개의 변수를 유지할 수 있습니다. 무슨 일이 일어나는지 좀 더 분명하게 해줍니다.

for (char *ptr = ??, pm = (PartitionMap *)ptr ; index > 0 ; --index) 
{ 
    ptr += pm->partitionMapLength; 
    pm = (PartitionMap *)ptr; 
} 
return pm; 
+0

당신이 준 코드가 잘못되었다는 것을 제외하고는, 이것은 내가 가지고있는 것보다 조금 더 정돈 된 것입니다. –

+1

예, 수정되었습니다. – caf

1

다른 사람들이 언급했듯이 캐스트가 필요하지만 매크로 나 함수에서 추악함을 숨길 수 있습니다. 그러나 염두에 두어야 할 또 다른 사항은 정렬 요구 사항입니다. 대부분의 프로세서에서 임의의 바이트 수만큼 형식에 대한 포인터를 단순히 증가시키고 정렬을 잘못하여 새로운 포인터를 통해 구조체에 액세스하는 데 문제없이 원래 형식에 대한 포인터로 결과를 다시 캐스트 할 수 없습니다.

x86 아키텍처는 몇 가지 아키텍처 중 하나입니다 (가장 인기가 있다고하더라도). 그러나 Windows 용으로 작성한 경우에도이 문제를 고려해야합니다. Win64에서는 정렬 요구 사항을 적용합니다.

포인터를 통해 partitionMapLength 멤버에 액세스하더라도 프로그램이 중단 될 수 있습니다.

쉽게 Windows에서 __unaligned 같은 컴파일러 확장을 사용하여이 문제를 해결할 수 있습니다

PartitionMap __unaliged *pm(reinterpret_cast<PartitionMap *>(partitionMaps)); 
for (; index > 0 ; --index) 
{ 
    pm = (PartitionMap __unaligned *)(((char *)pm) + pm->partitionMapLength); 
} 
return pm; 

또는 당신은 제대로 정렬 구조체에 잠재적으로 정렬되지 않은 데이터를 복사 할 수 있습니다 :

PartitionMap *pm(reinterpret_cast<PartitionMap *>(partitionMaps)); 

char* p = reinterpret_cast<char*>(pm); 

ParititionMap tmpMap; 
for (; index > 0 ; --index) 
{ 

    p += pm->partitionMapLength; 

    memcpy(&tmpMap, p, sizeof(newMap)); 
    pm = &tmpMap; 
} 

// you may need a more spohisticated copy to return something useful 
size_t siz = pm->partitionMapLength; 
pm = reinterpret_cast<PartitionMap*>(malloc(siz)); 
if (pm) { 
    memcpy(pm, p, siz); 
} 
return pm; 
+0

Windows는 기본적으로 x64에 대해 알고 있습니다. 아이태니엄에서 그렇지만 꽤 드문 경우입니다. 정렬 검사는 x86과 x64 모두에서 시행 될 수 있으며, 물론 정렬되지 않은 액세스로 인해 상당한 성능 저하가 발생할 수 있습니다. – jalf

+0

당신이 옳은 것처럼 보입니다. 나는 얼마 전에 Itanium에 물건을 이식 할 때 코드 정렬을 깨끗하게하는 법을 배웠습니다. 나는 MS가 x64에서 기본적으로 정렬 요구 사항을 엄격하게 유지한다고 생각했지만 거기에서 오해 된 것 같습니다. –

0

#include <iostream> 

int[] arry = { 0, 1, 2, 3 }; 
int* ptr = arry; 
while (*ptr != 3) { 
    std::cout << *ptr << '\n'; 
    ++ptr; 
} 
:

C와 C++ 모두 당신이 포인터와 ++를 통해 배열을 반복 할 수 있습니다

이 기능을 사용하려면 포인터에 추가하는 것은 포인터에 저장된 메모리 주소를 가져온 다음 추가되는 값의 형식에 상관없이 sizeof를 추가하도록 정의됩니다. 예를 들어,이 예에서 ++ptrptr에 저장된 메모리 주소에 1 * sizeof(int)을 추가합니다.

형식에 대한 포인터가 있고 해당 지점에서 특정 바이트 수를 앞당기려면 char*으로 캐스팅하는 것이 유일한 방법입니다 (sizeof(char)이 1로 정의 되었기 때문에).