programing

포인터(주소)가 음수가 될 수 있습니까?

i4 2023. 6. 15. 21:33
반응형

포인터(주소)가 음수가 될 수 있습니까?

실패 및 초기화되지 않은 특수 값을 반환할 수 있는 기능이 있습니다(성공 시 포인터를 반환함).

는 현재반환다니됩다니▁returns를 반환합니다.NULL, 실의경우고, 그리패고.-1초기화되지 않은 상태에서, 그리고 이것은 효과가 있는 것처럼 보입니다..., 주소항긍죠이적정상죠그 렇는▁(? (-1로 할 수 은 이상하게 .)(비록 컴파일러가 주소를 -1로 설정하는 것을 허용하고 있기 때문에, 이것은 이상하게 보입니다.)

[업데이트]

(-) 생각한 또 는 또다아는어른이디제같다이(-1경우니습한)▁another▁to▁i-입니다.malloc 한 자@전역 범위 및 해당 주소를 감시자로 사용합니다.

아니요, 주소가 항상 긍정적인 것은 아닙니다. x86_64에서는 포인터가 부호 확장되고 주소 공간이 0을 중심으로 대칭적으로 클러스터링됩니다("부정적" 주소가 커널 주소인 경우가 일반적이지만).

그러나 C는 단지 의미를 정의하기 때문에, 그 점은 대부분 무트입니다.<그리고.>동일한 개체의 일부 또는 배열의 끝을 지나는 포인터 간의 포인터 비교.C - 완 다 른 객 체 대 에 포 한 C - 서 정 확 한 동 일 없 수 다 니 습 비 될 교 있 의 게 미 는 전 에 이 히 외 에 것 위 한 성 을 적 인 어 터 표 준 는 , ▁other ▁pointers ▁c ▁equality ▁than ▁for ▁standard ▁exact ▁cannot ▁in ▁least ▁at ▁objects ▁compared ▁be ▁meaning 다 ▁complete if (p < NULL)제대로 정의된 의미론이 없습니다.

인 저장 을 가진 더미 객체를 의 주소를 당신의 "로 .unintialised설정:

extern char uninit_sentinel;
#define UNINITIALISED ((void *)&uninit_sentinel)

프로그램 전체에 걸쳐 하나의 고유한 주소가 있어야 합니다.

포인터의 유효한 값은 완전히 구현에 따라 다르므로 포인터 주소가 음수일 수 있습니다.

그러나 더 중요한 것은 (가능한 구현 선택의 예로) 32비트 포인터 크기를 가진 32비트 플랫폼에 있는 경우를 고려해야 한다는 것입니다.32비트 값으로 나타낼 수 있는 모든 값은 유효한 포인터일 수 있습니다.null 포인터를 제외한 모든 포인터 값은 개체에 대한 유효한 포인터일 수 있습니다.

특정 사용 사례의 경우 상태 코드를 반환하고 포인터를 함수의 매개 변수로 사용하는 것을 고려해야 합니다.

일반적으로 특별한 값을 반환 값에 다중화하려는 것은 좋지 않은 설계입니다.당신은 하나의 가치로 너무 많은 것을 하려고 합니다.반환 값보다 인수를 통해 "성공 포인터"를 반환하는 것이 더 깨끗합니다.그러면 설명하려는 모든 조건에 대한 반환 값에 충돌하지 않는 공간이 많이 남습니다.

int SomeFunction(SomeType **p)
{
    *p = NULL;
    if (/* check for uninitialized ... */)
        return UNINITIALIZED;
    if (/* check for failure ... */)
        return FAILURE;

    *p = yourValue;
    return SUCCESS;
}

일반적인 인수 검사도 수행해야 합니다('p'가 NULL이 아닌지 확인).

C 언어는 포인터에 대한 "부정성"의 개념을 정의하지 않습니다."음이 되는 것"의 특성은 주로 산술적인 것이며, 포인터 유형의 값에는 어떤 방식으로도 적용되지 않습니다.

할 수 .-1그 기능으로부터.C 언어에서 정수 값(0 이외)은 암시적으로 포인터 유형으로 변환할 수 없습니다. 반환 시도-1포인터 감지 기능은 진단 메시지를 발생시키는 즉각적인 제약 조건 위반입니다.간단히 말해서, 그것은 오류입니다.컴파일러가 이를 허용하면 해당 제약 조건을 너무 엄격하게 적용하지 않는다는 의미입니다(대부분의 경우 사전 표준 코드와의 호환성을 위해 수행함).

로지경우의 하면,-1명시적 캐스트에 의한 포인터 유형에 대해 캐스트의 결과는 구현 정의됩니다.언어 자체는 그것에 대해 보장하지 않습니다.이 값은 다른 유효한 포인터 값과 동일한 것으로 쉽게 입증될 수 있습니다.

값을 하려면 " " " " 을 생성할 .malloc 주소를 할 수 .원하는 유형의 글로벌 변수를 선언하고 해당 주소를 예약 값으로 사용할 수 있습니다.그것은 고유한 것임이 보장됩니다.

부호 없는 정수가 음수인 것처럼 포인터가 음수일 수 있습니다.즉, 두 개의 보완적 해석에서 가장 유의한 비트가 켜져 있기 때문에 숫자 값을 음수로 해석할 수 있습니다.

실패와 단일화의 차이점은 무엇입니까?초기화가 다른 종류의 실패가 아닌 경우에는 이러한 두 조건을 분리하기 위해 인터페이스를 재설계할 수 있습니다.

가장 좋은 방법은 매개 변수를 통해 결과를 반환하는 것이므로 반환 값은 오류를 나타낼 뿐입니다.예를 들어 다음과 같이 적을 수 있습니다.

void* func();

void* result=func();
if (result==0)
  /* handle error */
else if (result==-1)
  /* unitialized */
else
  /* initialized */

변경할 내용

// sets the *a to the returned object
// *a will be null if the object has not been initialized
// returns true on success, false otherwise
int func(void** a);

void* result;
if (func(&result)){
  /* handle error */
  return;
}

/*do real stuff now*/
if (!result){
  /* initialize */
}
/* continue using the result now that it's been initialized */

@물론 제임스가 옳지만, 저는 포인터가 항상 절대적인 메모리 주소를 나타내는 것은 아니며, 이론적으로 항상 긍정적인 것이라고 덧붙이고 싶습니다.포인터는 또한 메모리의 특정 지점(종종 스택 또는 프레임 포인터)에 대한 상대 주소를 나타내며, 이러한 주소는 양수 및 음수일 수 있습니다.

따라서 함수가 포인터에 대한 포인터를 매개 변수로 받아들이고 실제 함수의 결과 코드를 반환하는 동안 성공 시 유효한 포인터 값으로 해당 포인터를 채우는 것이 가장 좋습니다.

James의 답변이 옳을 수도 있지만, 물론 사용자가 선택할 수 있는 선택이 아닌 구현 선택을 설명합니다.

개인적으로, 저는 주소가 "직관적으로" 서명되지 않은 것이라고 생각합니다.비교 대상이 null 포인터보다 작은 포인터를 찾는 것은 잘못된 것 같습니다.그렇지만~0그리고.-1동일한 정수 유형에 대해 동일한 값을 지정합니다..~0좀 더 직관적인 특수 케이스 값을 만들 수 있습니다. 오류가 발생한 경우 서명되지 않은 의미에 사용하는 경우가 많습니다.실제로 다르지 않습니다(0은 기본적으로 int이므로~0이라-1캐스팅하기 전까지는) 하지만 다르게 보입니다.

32비트 시스템의 포인터는 모든 32비트 BTW를 사용할 수 있습니다.-1또는~0실제 실제 실제 할당에 대해 발생할 가능성이 매우 낮은 포인터입니다.또한 플랫폼별 규칙도 있습니다. 예를 들어 32비트 Windows에서는 프로세스에 2GB의 주소 공간만 있을 수 있으며 균형 잡힌 이진 트리에서 플래그를 균형 있게 조정하기 위해 어떤 종류의 플래그를 포인터의 맨 위 비트로 인코딩하는 많은 코드가 있습니다.

실제로(적어도 x86에서) NULL 포인터 예외는 NULL 포인터를 다시 참조하는 것뿐만 아니라 더 큰 범위의 주소(예: 처음 65kb)에 의해 생성됩니다.이는 다음과 같은 오류를 파악하는 데 도움이 됩니다.

int* x = NULL;
x[10] = 1;

따라서 참조되지 않을 때 NULL 포인터 예외를 생성하기 위해 허용되는 주소가 더 많습니다.이제 이 코드(AndreyT용으로 컴파일 가능)를 고려합니다.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define ERR_NOT_ENOUGH_MEM (int)NULL
#define ERR_NEGATIVE       (int)NULL + 1
#define ERR_NOT_DIGIT      (int)NULL + 2

char* fn(int i){
    if (i < 0)
        return (char*)ERR_NEGATIVE;
    if (i >= 10)
        return (char*)ERR_NOT_DIGIT;
    char* rez = (char*)malloc(strlen("Hello World ")+sizeof(char)*2);
    if (rez)
        sprintf(rez, "Hello World %d", i);
    return rez;
};

int main(){
    char* rez = fn(3);
    switch((int)rez){
        case ERR_NOT_ENOUGH_MEM:    printf("Not enough memory!\n"); break;
        case ERR_NEGATIVE:          printf("The parameter was negative\n"); break;
        case ERR_NOT_DIGIT:         printf("The parameter is not a digit\n"); break;
        default:                    printf("we received %s\n", rez);
    };
    return 0;
};

경우에 따라 유용할 수 있습니다.일부 하버드 건축물에서는 작동하지 않지만, 폰 노이만 건축물에서는 작동할 것입니다.

을 사용하지 .malloc불필요. (이미 되고 있을 때 메모리가 사용되고 )malloc호출되고 센티널이 높은 주소로 할당됩니다. 예를 들어, 메모리 디버거/디버깅 디텍터를 혼동합니다. "" " " " " " " "로하면 됩니다.static const char 으로 얻을 수 있는 합니다.이 포인터는 프로그램이 다른 방법으로 얻을 수 있는 어떤 포인터와도 결코 비교되지 않을 것이며, 단지 1바이트의 bss만 낭비합니다.

포인터는 구현이 정의되어 있으므로 포인터의 신호성에 대해 신경 쓸 필요가 없습니다.여기서 진짜 질문은 "함수 반환 포인터에서 특별한 값을 반환하는 방법?"이며, 이는 다양한 플랫폼의 포인터 주소 범위에 대한 답변에서 자세히 설명했습니다.

요약하자면, 올원 비트 패턴(-1)은 (거의) 항상 안전합니다. 왜냐하면 그것은 이미 스펙트럼의 끝에 있고 데이터는 첫 번째 주소로 감싸여 저장될 수 없기 때문입니다.malloc가족은 절대 돌아오지 않습니다 -1.실제로 이 값은 많은 Linux 시스템 호출과 Win32 API에 의해 반환되어 포인터의 다른 상태를 나타냅니다.따라서 실패하고 초기화를 해제해야 하는 경우에는 이를 선택하는 것이 좋습니다.

그러나 변수가 올바르게 정렬되어야 한다는 사실을 활용하면 훨씬 더 많은 오류 상태를 반환할 수 있습니다(다른 옵션을 지정하지 않은 경우).를 들어, 들어에포의경우터인대한예에 대한 에서.int32_t낮은 2비트는 항상 0입니다. 즉, 가능한 값 중 ⁄₄만 유효한 주소이며 나머지 비트 패턴은 모두 사용할 수 있습니다.를 확인하는 것입니다.

int* result = func();
if (!result)
    error_happened();
else if ((uintptr_t)result & 1)
    uninitialized();

이 경우 유효한 포인터와 일부 추가 데이터를 동시에 반환할 수 있습니다.

또한 높은 비트를 64비트 시스템에 데이터를 저장하는 데 사용할 수 있습니다.ARM에는 CPU가 주소의 높은 비트를 무시하도록 알리는 플래그가 있습니다.x86에는 유사한 것이 없지만 참조를 취소하기 전에 표준으로 설정하면 이러한 비트를 계속 사용할 수 있습니다.64비트 포인터에서 추가 16비트 사용을 참조하십시오.

참고 항목

NULL는 이 경우 유일하게 유효한 오류 반환이며, 포인터와 같은 부호 없는 값이 반환될 때마다 해당됩니다.경우에 따라 포인터가 부호 비트를 데이터 비트로 사용하기에 충분히 크지 않을 수도 있지만, 포인터는 프로그램이 아닌 OS에 의해 제어되기 때문에 이 동작에 의존하지 않을 것입니다.

포인터는 기본적으로 32비트 값입니다. 이것이 음수일 수 있는지 항상 양수일 수 있는지 여부는 해석의 문제입니다(즉, 32비트가nd 부호 비트로 해석되는지 데이터 비트로 해석되는지 여부).따라서 0xFFFF를 부호 있는 숫자로 해석하면 -1이 되고 부호 없는 숫자로 해석하면 4294967295가 됩니다.기술적으로 포인터가 이렇게 클 가능성은 낮지만 어쨌든 이 경우를 고려해야 합니다.

다른 방법으로 추가 출력 매개 변수(모든 오류에 대해 NULL 반환)를 사용할 수 있지만, 이를 위해서는 클라이언트가 특정 오류를 구별할 필요가 없는 경우에도 값을 생성하고 전달해야 합니다.

다른 은 또다대다사것는입다니용하음을안은른▁the다것니를 사용하는 것입니다.GetLastError/SetLastError추가 오류 정보를 제공하거나(이것은 Windows에만 해당되며 문제인지 여부를 알 수 없음) 오류에 대한 예외를 발생시키는 메커니즘입니다.

양수 또는 음수는 포인터 유형의 의미 있는 측면이 아닙니다.부호 있는 char, short, int 등 부호 있는 정수와 관련이 있습니다.

포인터의 reinterpret_cast<intptr_t>(ptr)이 경우, 그들은 실제로 포인터 자체가 아니라 캐스팅 정수에 대해 이야기하고 있습니다.

어떤 시나리오에서는 포인터가 본질적으로 서명되지 않은 것이라고 생각합니다. 우리는 주소에 대해 아래 또는 위의 용어로 이야기합니다. 0xFFFF.FFFF 이상입니다.0x0AAAA.0000직관적으로 인간을 위한 것입니다.비록 ~일지라도0xFFFF.FFFF인 반면, 제는부 "음"입니다.0x0AAA.0000양성입니다.

빼기와 에서는 그나포감산같다시른는서에오나리은러과인터▁but.(ptr1 - ptr2)유형이 다음과 같은 서명된 값을 생성합니다.ptrdiff_t하면 일치하지 않습니다.signed_int_a - signed_int_b서명된 int 유형이 생성됩니다.unsigned_int_a - unsigned_int_b부호 없는 유형을 생성합니다.그러나 포인터 빼기의 경우, 부호 있는 유형을 생성합니다. 의미론은 두 포인터 사이의 거리이고 단위는 요소의 수이기 때문입니다.

요약하면 포인터 유형을 독립 실행형 유형으로 취급하는 것이 좋습니다. 모든 유형에는 작업 집합이 있습니다. pointer,pointer, member function pointer 의 경우, " " " " (function pointer, member function pointer, 함포인의및경우터터인포")void *):

  1. 리스트 항목
  2. +,+=

    ptr + any_backet_type

  3. -,-=

    ptr - any_sys_type

    ptr1 - ptr2

  4. ++둘 다

  5. --둘 다

로 "니다없"가 ./ * %또한 유사한 " 하도록 int와 됩니다.또한 포인터는 "int와 유사한 유형" 또는 "int와 유사하도록 기본 유형이 int인 유형" 대신 독립형 유형으로 처리되어야 합니다.

언급URL : https://stackoverflow.com/questions/3304795/can-a-pointer-address-ever-be-negative

반응형