C에서 void 포인터에 대한 포인터 산술


176

특정 유형 (예 int: char,, float..)에 대한 포인터 가 증가하면 해당 데이터 유형의 크기만큼 값이 증가합니다. 경우 void크기의 데이터에 대한 포인터하는 포인트가 x증가 점에, 어떻게 나올까요 x앞서 바이트? 컴파일러 x는 포인터의 가치 를 높이는 방법을 어떻게 알 수 있습니까?



3
질문은 컴파일러 (/ 런타임)가 포인터가 설정된 유형의 객체를 알고 포인터에 크기를 추가한다고 가정하는 것처럼 들립니다. 그것은 완전히 오해입니다. 주소 만 알고 있습니다.
PJTraill

2
" void크기의 데이터를 가리키는 포인터 x가 증가하면 어떻게 x바이트를 앞당길 수 있습니까?" 그렇지 않습니다. 그런 질문을하는 사람들이 묻기 전에 테스트 할 수없는 이유는 무엇입니까? 적어도 실제로 컴파일되는지 여부를 확인하는 최소한의 단계는 아닙니다. -1, 이것이 +100과 -0이라고 믿을 수 없습니다.
underscore_d

답변:


287

최종 결론 : a에 대한 산술 void*은 C와 C ++에서 모두 불법 입니다.

GCC는이를 확장으로 허용합니다. 산술 void및 함수 포인터를 참조하십시오 (이 섹션은 설명서의 "C 확장"장의 일부입니다). Clang과 ICC void*는 GCC와의 호환성을 위해 산술을 허용 할 것 입니다. 다른 컴파일러 (MSVC와 같은)는에 대한 산술을 허용하지 void*않으며 GCC는 -pedantic-errors플래그가 지정되거나 플래그가 지정된 경우이를 허용하지 않습니다 -Werror-pointer-arith(이 플래그는 코드베이스도 MSVC로 컴파일해야하는 경우에 유용합니다).

C 표준은 말한다

따옴표는 n1256 초안에서 가져옵니다.

추가 작업 상태에 대한 표준 설명 :

6.5.6-2 : 또한 두 피연산자 모두 산술 유형을 갖거나 한 피연산자는 객체 유형에 대한 포인터이고 다른 피연산자는 정수 유형이어야합니다.

따라서 여기서 질문 void*은 "객체 유형"에 대한 포인터인지 또는 "객체 유형"인지 void에 대한 것입니다. "객체 유형"의 정의는 다음과 같습니다.

6.2.5.1 : 유형은 객체 유형 ( 객체 를 완전히 설명하는 유형), 함수 유형 (함수를 설명하는 유형) 및 불완전한 유형 (객체를 설명하지만 크기를 결정하는 데 필요한 정보는없는 유형)으로 분할됩니다 .

그리고 표준은 다음 void과 같이 정의 됩니다.

6.2.5-19 : void유형은 빈 값 집합으로 구성됩니다. 완료 할 수없는 불완전한 유형입니다.

void불완전한 유형 이므로 객체 유형이 아닙니다. 따라서 추가 연산에 유효한 피연산자가 아닙니다.

따라서 포인터에서 포인터 산술을 수행 할 수 없습니다 void.

노트

원래 void*C 표준의 다음 섹션으로 인해 산술이 허용되는 것으로 생각 되었습니다.

6.2.5-27 : void에 대한 포인터는 문자 유형에 대한 포인터 와 동일한 표현 및 정렬 요구 사항을 가져야 합니다 .

하나,

동일한 표현 및 정렬 요구 사항은 함수에 대한 인수, 함수의 값 반환 및 공용체 멤버와의 호환성을 의미합니다.

따라서 이것은 유형 또는 유형에 printf("%s", x)관계없이 동일한 의미를 갖지만,에 대해 산술을 수행 할 수있는 것은 아닙니다 .xchar*void*void*

편집자 주 : 이 답변은 최종 결론을 반영하기 위해 편집되었습니다.


7
C99 표준에서 : (6.5.6.2) 또한, 두 피연산자 모두 산술 유형을 갖거나 하나의 피연산자는 객체 유형에 대한 포인터이고 다른 피연산자는 정수 유형이어야합니다. (6.2.5.19) 공극 유형은 빈 값 세트를 포함한다. 완료 할 수없는 불완전한 유형입니다. void*포인터 산술이 허용되지 않는다는 것을 분명히합니다 . GCC에는 이를 수행 할 수 있는 확장 이 있습니다.
Job

1
답변이 더 이상 유용하지 않다고 생각되면 삭제하면됩니다.
caf

1
이 답변은 무효 포인터가 산술을위한 것이 아니라는 결정적인 증거를 포함하고 있기 때문에 잘못되었다고하더라도 유용했습니다.
벤 플린

1
이것은 좋은 대답이며, 올바른 결론과 필요한 인용을 가지고 있지만,이 질문에 온 사람들은 답의 맨 아래를 읽지 않았기 때문에 잘못된 결론에 도달했습니다. 더 명확하게하기 위해 이것을 편집했습니다.
Dietrich Epp

1
Clang과 ICC는 void*산술을 허용하지 않습니다 (적어도 기본적으로).
Sergey Podobry

61

포인터에는 포인터 산술이 허용되지 않습니다 void*.


16
+1 포인터 산술은 (완전한) 객체 유형에 대한 포인터에 대해서만 정의 됩니다 . void이다 불완전한 유형 정의에 의해 완성 될 수 없다.
schot

1
@schot : 맞습니다. 또한 포인터 산술은 배열 객체의 요소에 대한 포인터에 대해서만 정의되며 연산 결과가 동일한 배열의 요소에 대한 포인터이거나 해당 배열의 마지막 요소를 지나는 포인터 인 경우에만 가능합니다. 이러한 조건이 충족되지 않으면 정의되지 않은 동작입니다. (C99 표준 6.5.6.8부터)
Job

1
분명히 gcc 7.3.0에서는 그렇지 않습니다. 컴파일러는 p + 1024를 허용하며, 여기서 p는 void *입니다. 결과는 ((char *) p) + 1024
zzz777와 동일합니다.

@ zzz777 GCC 확장 프로그램입니다. 가장 많이 투표 된 답변의 링크를 참조하십시오.
Ruslan

19

포인터를 x 바이트 앞으로 앞으로 증가시키는 char 포인터로 캐스트하십시오.


4
왜 귀찮게? 항상 알고 있어야하는 올바른 유형으로 캐스트하고 1 char만큼 증가 시키십시오. 다음으로 캐스팅하고 , 증가한 x다음 새 값을 해석하면 다른 유형은 의미가없고 정의되지 않은 동작입니다.
underscore_d

9
당신이 따라하는 것은 당신의 정렬 기능을 작성하는 경우 man 3 qsort을해야을 void qsort(void *base, size_t nmemb, size_t size, [snip]), 당신은 "올바른 형식"알 방법이 없다
alisianoi을

15

C 표준은 허용하지 않습니다 무효 포인터 연산을. 그러나, GNU C는 의 크기를 고려하여 허용 무효 IS를 1.

C11 표준 §6.2.5

단락-19

void유형 값의 빈 세트를 포함하고; 그것은이다 불완전한 개체 유형 을 완료 할 수 없습니다.

다음 프로그램은 GCC 컴파일러에서 정상적으로 작동합니다.

#include<stdio.h>

int main()
{
    int arr[2] = {1, 2};
    void *ptr = &arr;
    ptr = ptr + sizeof(int);
    printf("%d\n", *(int *)ptr);
    return 0;
}

다른 컴파일러가 오류를 생성 할 수 있습니다.



8

포인터 산술을 수행하기 전에 다른 유형의 포인터로 캐스트해야합니다.


7

무효 포인터는 메모리 청크를 가리킬 수 있습니다. 따라서 컴파일러는 void 포인터에서 포인터 산술을 시도 할 때 증가 / 감소 할 바이트 수를 알지 못합니다. 따라서 void 포인터는 포인터 산술에 관여하기 전에 먼저 알려진 형식으로 타입 캐스트해야합니다.

void *p = malloc(sizeof(char)*10);
p++; //compiler does how many where to pint the pointer after this increment operation

char * c = (char *)p;
c++;  // compiler will increment the c by 1, since size of char is 1 byte.

-1

컴파일러는 유형 캐스트로 알고 있습니다. 주어진 void *x:

  • x+11 바이트를 추가 x하고 포인터는 바이트로 이동x+1
  • (int*)x+1sizeof(int)바이트를 추가 하고 포인터는 바이트로 이동x + sizeof(int)
  • (float*)x+1주소 sizeof(float)바이트 등

첫 번째 항목은 이식성이 없으며 C / C ++의 Galateo에 위배되지만 C 언어가 정확합니다. 즉, 대부분의 컴파일러에서 적절한 플래그가 필요할 수있는 무언가로 컴파일됩니다 (예 : -Wpointer-arith).


2
Althought the first item is not portable and is against the Galateo of C/C++진실. it is nevertheless C-language-correct그릇된. 이것은 두 번 생각입니다! 포인터 산술 void *은 구문 상 불법이며 컴파일해서는 안되며 정의되지 않은 동작을 생성합니다. 부주의 한 프로그래머가 경고를 비활성화하여 컴파일 할 수 있다면 변명의 여지가 없습니다.
underscore_d

@underscore_d : 일부 컴파일러는 확장으로 허용하는 데 사용했다고 생각 unsigned char*합니다. 예를 들어 sizeof포인터에 값을 추가 하기 위해 캐스팅 해야하는 것보다 훨씬 편리하기 때문 입니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.