2017-01-27 19 views
0

FATFS 라이브러리의 인터페이스 기능을 구현하려고합니다. 구현시 uint8_t *가 다른 라이브러리에서 내부 SD 카드에 쓰여야하는 데이터를 필요로합니다. 데이터는 BSP_SD_WriteBlocks (uint32_t *, uint64_t, uint32_t, uint32_t) 함수를 사용하여 라이브러리에 기록해야합니다.uint8_t *를 uint32_t *로 캐스팅하는 방법

/* 
* Write data from a specific sector 
*/ 
int SDMMC::disk_write(const uint8_t *buffer, uint32_t sector, uint32_t count) 
{ 
    int res = BSP_SD_WriteBlocks((uint32_t*)buffer, (uint64_t)sector, 512, count); 

    if (res == MSD_OK) { 
     return RES_OK; 
    } else { 
     return RES_ERROR; 
    } 
} 

당신은 내가 32 비트 메모리 주소로 8 비트 메모리 주소를 캐스팅하려고이 그렇게 할 올바른 방법이라고 생각하지 않습니다 볼 수 있듯이 (아래 참조).

유감스럽게도 disk_write 함수가 uint8_t *를 받아 들일 수 있도록 함수 인수를 변경할 수 없으며 BSP_SD_WriteBlocks는 단지 uint32_t *를 허용합니다.

최선을 다하고 금식하는 방법은 무엇입니까?

+1

유효한 캐스트입니다. 그러나'count' 인수는 아마도 쓸 바이트 수 ('uint8_t' 요소)일까요? 그리고'BSP_SD_WriteBlocks' 함수가 32 비트 단어를 쓰면'count'를 4로 나눌 필요가 있습니다. 그렇지 않으면 많이 쓰여지고 (버퍼의 범위를 벗어납니다). –

+0

가능한 endianess 문제입니다. (엄격한 앨리어싱 규칙을 위반할 수도 있음). – Jarod42

답변

1

트릭은 초기에 적절한 크기의 배열 uint32_t을 사용하는 것입니다. 동적으로 할당 할 수 있습니다 (내 첫 번째 아이디어 아래 참조). 바이트 수준에서 모든 개체에 액세스 할 수 있으므로 uint32_t *uint8_t *에 캐스팅하고 정상적으로 문자 버퍼로 처리 할 수 ​​있습니다. 엄격한 별칭 규칙에서 허용하는 바이트 수준의 원래 uint32_t 배열에 액세스하고 있습니다.

uint32_t *을 다시 캐스팅해야하는 경우. 원래 배열을 바이트 수준으로 만 액세스 했으므로 배열의 수명이 끝나지 않고 uint32_t *이 유효한 배열을 가리 킵니다.


이전 및 솔루션 너무 좋지 않다

비결이 여기 malloc에 의해 할당 된 버퍼를 가지고하는 것입니다. C 표준은 C++ 프로그램에서 명시 적으로 액세스 할 수있는 C 표준 라이브러리 (*) 참조하는 부분에 말한다 : 7.20.3 메모리 관리 기능

을 ... 포인터가 할당 적절히 있도록 정렬되어 성공하면 반환 buffer 것으로 의미

는 반환 값은 ... 객체 모든 유형에 대한 포인터를 지정하고 개체 또는 할당 된 공간에서 이러한 오브젝트의 배열 등을 액세스하는 데 사용될 수있다 malloc 호출의 경우 표준을 통해 다른 포인터 유형으로 안전하게 캐스팅 될 수 있습니다. 그렇지 않으면 uint32_t에 대한 정렬이 uint8_t보다 높고, 나쁜 정렬 된 포인터를 사용하여 명시 적으로 정의되지 않은 행동 때문에

, 당신은 정렬 문제의 위험이 있습니다.

여기서 엄격한 앨리어싱 규칙을 위반하고 있다고 주장 할 수 있습니다. 그러나 일반적인 구현은 괜찮을 것입니다 (이를 거부하기 위해 기존 코드가 너무 많이 손상 될 것입니다). 그리고 엄격하게 준수하는 방법은 버퍼의 전체 memcopy를 사용하는 것입니다 ... 바이트의 동일한 시퀀스로 끝나야합니다. 호환 가능한 정렬!


(*)가 나는 C 및 C++ 다른 언어지만 C++ 표준 참조 설명서 1.2 인용 규격 [소개 말한다 것을 알고있다.심판]

한 다음 참조 문서는이 표준의 적용을 위해 필수적이다 ...
- 9899 ISO/IEC 1999, 프로그래밍 언어 - C
...
2 라이브러리 절에 설명 ISO/IEC 9899 : 1999/ISO/IEC 9899 : 1999/Cor.1 : 2001 의 7 절과 ISO/IEC 9899 : 1999/Cor.2 : 2003의 7 절을 이하 C 표준 라이브러리라고합니다.
...

1) 제 18 ~ 30 조 및 C.3에 명시된 자격을 갖춘 C 표준 라이브러리는 C++ 스탠드의 하위 집합입니다 ard 라이브러리.

1

여기에는 잠재적으로 여러 가지 문제가있을 수 있습니다.

  1. const을 멀리 던지고 있습니다.

현실 세계에서는 정의되지 않은 동작이 발생할 수 있습니다. BSP_SD_WriteBlocks를 const 포인터로 바꾸지 못한다고 가정하면 데이터의 복사본을 만들고 비 const 포인터를 사용해야합니다. 보너스는 복사본을 만들 때 다른 모든 문제를 해결할 수 있다는 것입니다. 단점은 복사본 만들기에는 시간과 메모리가 필요하다는 것입니다.

실제로 BSP_SD_WriteBlocks가 해당 포인터를 통해 쓰려고 시도하지 않는다는 것을 알고 있다면 아마 괜찮을 것입니다. 하지만 이것은 큰 문제이므로 C++ 스타일의 const_cast<>을 사용하여 의도적으로이 작업을 수행하고 있음을 분명히 알 수 있습니다.

  1. 정렬. 현학적 인 세계에서

std::uint8_t *std::uint32_t *에의 캐스트는 원래 포인터가 적절하게 정렬 알고 여부에 플랫폼이 정렬되지 않은 액세스를 허용 여부에 따라, 적어도 이식, 안전하지 않을 수 있습니다. # 1에서 제안한 복사는 이것을 해결할 수 있습니다. 임시 버퍼가 적절히 정렬되었는지 쉽게 확인할 수 있기 때문입니다.

실용적인 세계에서 원본 버퍼가 항상 적절하게 정렬되어 있다는 것을 알고 있다면 이것은 큰 문제가되지 않습니다. 다시 말하지만 C++ 스타일의 캐스트를 제안합니다. 또한 버퍼 주소가 std :: uint32_t의 크기의 배수라는 주장을 권합니다.