2017-04-14 8 views
1

대상 프로세스 메모리를 수정해야하는 곳에서 programm 작업을하고 있습니다.C - 포인터 산술을 할 때 uintptr_t vs char *로 캐스팅하는 이유

지금까지 내가 adresses를 저장 무효 *를 사용하고 내가 그들을 변경해야하는 경우 사람들은 * 숯불 캐스팅하고 (오프셋 추가하거나 일반적으로 수정)

내가 stdint.h에 정의 된 해당 유형 들었지만 난 더 이상 C89 친숙한 숯불 전환 (이상의 포인터 산술에 대한 사용에 차이가 표시되지 않습니다)

그럼 내 질문 : 그 두 가지 방법 중 포인터 연산에 사용해야합니까? 어쨌든 char * 이상 uintptr_t 사용을 고려해야합니까?

EDIT 1

는 기본적으로 난 그냥는 다음과 같은 가능성이 잘못되었다고 지적한다 ..이 댓글 사용자 R에서

0x00F00BAA hard coded memory adress in target 
process 
void* x = (void*)0x00F00BAA; 
char* y = (void*)0x00F00BAA; 
x = (uintptr_t)x + 0x123; 
y = (char*)y + 0x123; 

x == y? 
x == (void*)0x00F00CCD? 
y == (void*)0x00F00CCD? 
+0

나는 표준이'uintptr_t' 변수의 산술 수정과'char *'에 대한 캐스트가 당신에게'char *'에 수학을 직접 적용하는 것과 같은 결과를 가져 오는 것을 보장하지 않는다고 생각한다. 'uintptr_t'는 유래하지 않았습니다. – PSkocik

+0

@PSkocik 물론 그것은 보유하지 않으며 전혀 미치지 않습니다. –

+0

@AnttiHaapala 만약 uintptr_t를 3으로 움직이면, 대응하는 char *이 3으로 움직일 것으로 기대합니다. 왜 플랫 메모리 모델 플랫폼이 다른 것을하려고하는지 보지 못합니다. – PSkocik

답변

2

를 얻을 수 있는지 알고하는 데 필요한 코드를 다루고 주소의 경우 은 현재 프로세스 내에서 유효하지 않습니다. 나는 OP를 명확히하기 위해 요청했다.

코드 이식성에 신경 쓰면 포인터 계산에 uintptr_t을 사용하지 마십시오. uintptr_t은 정수 유형입니다. 그것에 대한 산술 연산은 모두 포인터 연산이 아닌 정수 연산입니다.

값이 이고 바이트 오프셋을 추가하려는 경우 char*으로 캐스팅하는 것이 올바른 방법입니다.

uintptr_t 값의 계산은 char* 산술과 같은 방식으로 작동하지만 가능성은 보장 할 수 없습니다. C 표준이 제공하는 유일한 보증은 void* 값을 uintptr_t으로 변환하고 다시 반환 할 수 있으며 결과는 원래 포인터 값과 동일하게 비교됩니다.

그리고이 표준은 uintptr_t이 있다는 것을 보장하지 않습니다. 정보의 손실없이 변환 된 포인터 값을 보유 할만큼 충분히 넓은 정수 유형이 없다면, 구현은 uintptr_t을 정의하지 않을 것입니다.

실제로 나는 uintptr_t의 산술 연산이 작동하지 않는 시스템 (Cray 벡터 머신)에서 작업했습니다. 하드웨어에는 64 비트 단어가 있고 컴퓨터 주소에는 단어 주소가 들어 있습니다. 유닉스 계열의 OS는 8 비트 바이트를 지원해야했기 때문에 바이트 포인터 (void*, char*)는 64 비트 워드의 상위 3 비트에 3 비트 오프셋이 저장된 워드 주소를 저장했습니다. 포인터/정수 변환은 단순히 표현을 복사했습니다. 결과는 char* 포인터에 1을 더하면 다음 바이트 (소프트웨어에서 처리 된 오프셋이 있음)를 가리 키지 만, uintptr_t으로 변환하고 1을 추가하면 다음 단어을 가리킬 수 있습니다.

결론 : 포인터 연산이 필요한 경우 포인터 연산을 사용하십시오. 그것이 바로 그 때문입니다.

(덧붙여 gcc는 void*에서 포인터 연산을 허용하는 확장명을 가지고 있습니다. 이식성 코드에서는 사용하지 말고 sizeof (void) == 1과 같은 이상한 부작용이 발생합니다.)

+0

포인터 표현을 변환하는 아키텍쳐에서'void *'에서'char *'로의 캐스팅을 가정합니다. 그러나 변환 과정에서 어떤 변화가 발생합니까? 정렬되지 않은'char *'를'void *'트랩으로 변환하겠습니까? – TrentP

+0

이 답변은 아마도 다른 질문에 맞을 수도 있지만 OP의 질문에는 완전히 틀립니다. OP는 "목표 프로세스"(디버깅/메모리 엿보기/파킹)의 주소를 저장하고 조작하기 위해 포인터 유형과 포인터 연산을 고려하고 있습니다. 이것에 대한 포인터를 사용하는 것은 유효하지 않습니다. 포인터는 프로그램 자체의 주소 공간에있는 기존 개체를 가리킬 뿐이며 다른 프로세스는 가리킬 수 없으며 해당 개체에 대한 산술은 지정된 개체 안에 있어야 유효합니다. –

+0

'uintptr_t '가 작동 할 수도 있지만 ('void *'가 종속 될 수있는 정의되지 않은 문제의 대상이 아님) 디버거와 대상 프로세스가 동일한 ISA/ABI를 사용한다고 가정합니다. 일반적으로 빈약합니다. 이러한 유형의 소프트웨어를 설계 할 때 고려해야 할 사항. 대신 대상 프로세스의 (target-implementation-defined) 포인터 표현과 일치시키기 위해 선택된 정수 유형을 사용해야합니다. ELF 시스템에서'Elf32_Addr' /'Elf64_Addr'과 같은 타입이 적절할 것입니다. –