programing

주소 0에 접속할 수 있을까요?

yoursource 2023. 1. 15. 13:07
반응형

주소 0에 접속할 수 있을까요?

상수 0은 C 및 C++의 늘 포인터로 사용됩니다.그러나 "특정 고정 주소에 포인터"라는 질문에서처럼 고정 주소를 할당하는 데는 몇 가지 용도가 있는 것 같습니다.어떤 시스템에서든 주소 0에 액세스하기 위한 저수준 태스크에 대해 생각할 수 있는 요구가 있습니까?

만약 있다면, 0이 늘 포인터인 상태에서 어떻게 해결하나요?

그렇지 않다면, 무엇이 그러한 필요성이 없다고 확신합니까?

와 의 쪽의 도, 주소 C 의 「C++」 의 null-pointer 에 .0를 한다는 것0 소스 코드에서 포인터를 null-supervalue로 설정하는 것은 단순한 통사설탕 조각에 지나지 않습니다.컴파일러는 특정 플랫폼에서 늘 포인터 값으로 사용되는 실제 물리 주소로 변환해야 합니다.

말하면, 「 」입니다.0물리적인 중요성은 전혀 없습니다.그랬을 수도 있어42 ★★★★★★★★★★★★★★★★★」13 를 들면 ,, , 언, 렇, 렇, 렇, 렇, 렇, 렇, 렇, 렇, 렇, authors, authors, authors, authors, authors, authors, authors, authors, authors, authors, authors, authors, authors, authors, authors, ,, ,, ,, ,, ,, ,, ,authors, ,.p = 42pnull-interminal로 하다다시 말씀드리지만, 이것은 물리 주소가42는 늘 포인터용으로 예약되어 있어야 합니다.합니다.p = 42(null-module 값을 됩니다.0x0000 ★★★★★★★★★★★★★★★★★」0xBAAD )를에 넣습니다.p 그렇게 있어요.0.

또한 C와 C++ 모두 특정 물리 주소를 포인터에 할당할 수 있는 엄밀하게 정의된 기능을 제공하지 않습니다.따라서 "어떻게 포인터에 0 주소를 할당할 것인가?"에 대한 질문은 공식적으로 답이 없습니다.C/C++에서는 특정 주소를 포인터에 할당할 수 없습니다.다만, 실장 정의 기능의 영역에서는, 명시적인 정수에서 포인터로 변환하는 것을 목적으로 하고 있습니다.그럼 이렇게 하면 되겠네요.

uintptr_t address = 0;
void *p = (void *) address;

주의: 이것은, 다음과 같이 하는 것과는 다릅니다.

void *p = 0;

null-internal-internal-internal, null-internal-internal-internal-internal-internal-internal-internal, null-internal-internal 주소로의 합니다.0이 값은 지정된 플랫폼에서의 늘 값일 수도 있고 아닐 수도 있습니다.

접선상의 주의:Microsoft의 C++ 컴파일러에서는 멤버에 대한 NULL 포인터가 32비트 머신에서 비트 패턴 0xFFFF로 표시됩니다.즉, 다음과 같습니다.

struct foo
{
      int field;
};

int foo::*pmember = 0;     // 'null' member pointer

pmber는 'all one'이라는 비트 패턴을 가집니다.이는 이 값을 이 값과 구별해야 하기 때문입니다.

int foo::*pmember = &foo::field;

여기서 비트 패턴은 실제로 '모두 0'이 됩니다.왜냐하면 우리는 구조 foo에 오프셋0을 넣고 싶기 때문입니다.

다른 C++ 컴파일러에서는 멤버에 대한 늘 포인터로 다른 비트패턴을 선택할 수 있지만 중요한 점은 이것이 예상대로 제로 비트패턴이 아니라는 것입니다.

이것은 많은 사람들을 놀라게 할 수 있지만 핵심 C 언어에는 특별한 늘 포인터 같은 것은 없습니다.물리적으로 가능한 경우 주소 0을 읽고 쓸 수 있습니다.

다음 코드는 NULL이 정의되어 있지 않기 때문에 컴파일도 되지 않습니다.

int main(int argc, char *argv[])
{
    void *p = NULL;
    return 0;
}

OTOH는 아래 코드가 컴파일되어 하드웨어/OS에서 다음 기능이 가능한 경우 주소 0을 읽고 쓸 수 있습니다.

int main(int argc, char *argv[])
{
    int *p = 0;
    *p = 42;
    int x = *p; /* let's assume C99 */
}

상기 예에는 아무것도 기재하지 않았습니다.표준 C 라이브러리에서 내용을 포함하면 NULL이 마법처럼 정의됩니다.내가 기억하는 바로는string.h.

NULL은 여전히 핵심 C 기능이 아니라 포인터의 무효를 나타내는 많은 C 라이브러리 함수의 컨벤션입니다.특정 플랫폼 상의 C 라이브러리는 액세스 할 수 없는 메모리 위치에 NULL을 정의합니다.Linux PC에서 사용해 보겠습니다.

#include <stdio.h>
int main(int argc, char *argv[])
{
        int *p = NULL;
        printf("NULL is address %p\n", p);
        printf("Contents of address NULL is %d\n", *p);
        return 0;
}

결과는 다음과 같습니다.

NULL is address 0x0
Segmentation fault (core dumped)

따라서 C 라이브러리는 주소 0에 NULL을 정의하고 있습니다.이것에 액세스 할 수 없는 것이 판명되었습니다. 함수인 C-라이브러리 C 컴파일러가 .printf()특별히 제로 주소를 처리했습니다.그들은 모두 행복하게 그것을 가지고 정상적으로 일하려고 노력했다.였습니다.printf0으로 하다

이것은 컴파일러가 대신 처리합니다(comp.lang.c FAQ).

머신이 늘 포인터에 0이 아닌 비트 패턴을 사용하는 경우 프로그래머가 요청할 때 "0" 또는 늘 포인터인 "NULL"을 작성하여 생성하는 것은 컴파일러의 책임입니다.따라서 내부 늘 포인터가 0이 아닌 머신에서 #NULL을 0으로 정의하는 것은 다른 머신과 마찬가지로 유효합니다.왜냐하면 컴파일러는 포인터 컨텍스트에 표시되는 장식되지 않은0에 대한 응답으로 머신의 올바른 늘 포인터를 생성해야 하기 때문입니다.

포인트 이외의 컨텍스트에서0 을 참조하면, 주소 0 을 취득할 수 있습니다.

Motorola HC11의 gcc를 사용하여 코드를 컴파일했습니다.MMU는 없고 0은 매우 좋은 주소입니다.주소 0에 쓰려면 그냥 쓰는 것입니다.NULL과 주소 0 사이에는 차이가 없습니다.

왜 그랬는지 알겠네요즉, 모든 메모리 위치가 유효한 아키텍처에서는 고유한 NULL을 정의할 수 없기 때문에 gcc 작성자는 유효한 주소인지 여부에 관계없이 0이면 충분하다고 말했습니다.

      char *null = 0;
; Clears 8-bit AR and BR and stores it as a 16-bit pointer on the stack.
; The stack pointer, ironically, is stored at address 0.
1b:   4f              clra
1c:   5f              clrb
1d:   de 00           ldx     *0 <main>
1f:   ed 05           std     5,x

다른 포인터와 비교하면 컴파일러는 정기적으로 비교합니다., 「 」는 고려되지 않습니다.char *null = 0특별한 NULL 포인터가 되는 것, 그리고 실제로는 주소 0에 대한 포인터와 "NULL" 포인터가 같아집니다.

; addr is a pointer stored at 7,x (offset of 7 from the address in XR) and 
; the "NULL" pointer is at 5,y (offset of 5 from the address in YR).  It doesn't
; treat the so-called NULL pointer as a special pointer, which is not standards
; compliant as far as I know.
37:   de 00           ldx     *0 <main>
39:   ec 07           ldd     7,x
3b:   18 de 00        ldy     *0 <main>
3e:   cd a3 05        cpd     5,y
41:   26 10           bne     53 <.LM7>

첫 번째 질문에 답하려면 컴파일러의 실장을 체크하고 고유한 값의 NULL을 실장하고 있는지 여부를 확인하는 것이 답입니다.그렇지 않다면 걱정할 필요가 없습니다.;)

(물론 이 답변은 표준 규격에 준거하지 않습니다.)

메모리 주소 0은 제로 페이지라고도 불립니다.이 정보는 BIOS에 의해 입력되며 시스템에서 실행 중인 하드웨어에 대한 정보가 포함되어 있습니다.모든 최신 커널이 이 메모리 영역을 보호합니다.이 메모리에 액세스 할 필요는 없습니다만, 커널 랜드내에서 액세스 할 필요가 있는 경우는, 커널 모듈이 유효하게 됩니다.

x86에서는 주소 0(또는 오히려 0000:0000)과 리얼모드에서의 그 부근이 인터럽트 벡터의 위치입니다.옛날에는 보통 인터럽트 벡터에 값을 써서 인터럽트 핸들러를 설치했습니다(또는 좀 더 규율이 높은 경우에는 MS-DOS 서비스 0x25를 사용).MS-DOS용 C 컴파일러는 NULL 또는 0을 할당하면 세그먼트 부분에서 비트 패턴 0000을 수신하고 오프셋 부분에서 0000을 수신하는 원점포인터 타입을 정의했습니다.

물론 0000:0000의 값을 가진 원점 포인터에 잘못 쓴 프로그램은 기계에서 매우 안 좋은 일이 발생하게 되며, 일반적으로 시스템이 잠기고 재부팅이 강제됩니다.

당신은 잘못된 전제에서 출발하고 있어요.값이 0인 정수 정수를 포인터에 할당하면 해당 정수는 늘 포인터 정수가 됩니다.단, 늘 포인터가 반드시 주소0을 참조하는 것은 아닙니다.반대로, C와 C++ 규격은 모두, 늘 포인터가 제로 이외의 주소를 참조할 가능성이 있는 것은 매우 명확합니다.

결론은 다음과 같습니다.늘 포인터가 참조하는 주소를 따로 확보해야 하지만 기본적으로는 임의의 주소일 수 있습니다.0 을 포인터로 변환할 때는, 선택한 주소를 참조할 필요가 있습니다만, 이것만 있으면 됩니다.예를 들어, 정수를 포인트로 변환하면 정수에 0x8000을 추가하는 것을 의미한다고 판단했을 경우, 에 대한 늘 포인터는 실제로는 주소 0이 아닌 주소 0x8000을 참조합니다.

Null 포인터를 참조하면 정의되지 않은 동작이 발생한다는 점도 유의해야 합니다.휴대용 코드로는 할 수 없지만 전혀 할 수 없는 것은 아닙니다.소형 마이크로컨트롤러 등의 코드를 작성할 때는 휴대할 수 없는 코드 조각이 포함되어 있는 것이 일반적입니다.한 주소에서 읽으면 일부 센서의 값을 얻을 수 있지만, 동일한 주소에 쓰면 스테퍼 모터를 작동할 수 있습니다(예:).다음의 디바이스(정확히 같은 프로세서를 사용하고 있는 경우라도)는 접속되어 있기 때문에, 양쪽의 주소가 통상의 RAM을 참조할 수 있습니다.

포인터가 주소 0을 참조한다고 해도, 그 주소에서 발생한 모든 것을 읽거나 쓰는 것을 막는 것은 아닙니다.그것은 단지 휴대하기 쉽게 하는 것을 방해할 뿐이지만, 그것은 그다지 중요하지 않습니다.일반적으로 주소 0이 중요한 유일한 이유는 일반 스토리지가 아닌 다른 스토리지에 연결하도록 디코딩된 경우이기 때문에 완전히 휴대할 수 없습니다.

실제로 C 컴파일러는 프로그램이 주소 0에 쓰기를 시도하도록 허용합니다.실행 시 NULL 포인터에 대한 모든 포인터 작업을 확인하는 것은 다소 비용이 많이 듭니다.컴퓨터에서는 운영체제가 금지하고 있기 때문에 프로그램이 크래시 됩니다.메모리 보호 기능이 없는 임베디드 시스템에서는 실제로 프로그램이 주소 0에 쓰기 때문에 시스템 전체가 크래시 되는 경우가 많습니다.

주소 0은 임베디드 시스템에서 유용할 수 있습니다(컴퓨터에 없는 CPU의 일반 용어입니다. 스테레오에서 디지털 카메라까지 모든 것을 실행합니다).일반적으로 시스템은 주소 0에 쓸 필요가 없도록 설계되어 있습니다.제가 아는 모든 경우에서, 일종의 특별한 주소입니다.프로그래머가 인터럽트 테이블을 셋업하기 위해 쓰기를 필요로 하는 경우에도 초기 부트 시퀀스(일반적으로 C 환경을 셋업하기 위한 어셈블리 언어의 짧은 비트) 동안만 쓰면 됩니다.

링크의 질문에서는 마이크로컨트롤러의 고정주소 설정에 대해 논의하고 있습니다.마이크로컨트롤러를 프로그래밍하면 모든 것이 훨씬 더 낮은 수준에 있습니다.

데스크탑/서버 PC에 관해서는 OS도 없고 가상 메모리도 없습니다.따라서 특정 주소의 메모리에 액세스 할 수 있고, 필요한 경우도 있습니다.최신 데스크톱/서버 PC에서는 무용지물이며 위험하기까지 합니다.

이 모든 것은 시스템에 가상 메모리가 있는지 여부에 따라 달라집니다.통상, 시스템이 거기에 기입할 수 없는 페이지가 표시됩니다.이것은 아마 익숙한 동작일 것입니다.그러나 이 기능이 없는 시스템(일반적으로 마이크로 컨트롤러이지만 예전에는 훨씬 더 일반적이었습니다)에서는 인터럽트 테이블과 같은 매우 흥미로운 것이 종종 있습니다.8비트 시스템 시절에는 이런 것들을 해킹했던 기억이 납니다.시스템을 하드 리셋하고 처음부터 다시 시작해야 할 때는 즐겁고 큰 문제가 되지 않습니다. :- )

예, 메모리 주소 0x0h에 액세스할 수 있습니다. 작업을 수행하는 이유는 플랫폼에 따라 다릅니다.프로세서는, 리셋 벡터에 기입하면 CPU가 리셋 되는 리셋 벡터에 이것을 사용할 수 있습니다.또, 인터럽트 벡터에 대해서, 일부의 하드웨어 자원(프로그램 카운터, 시스템 클럭등)에의 메모리 매핑 인터페이스로서 사용하거나, 플레인 낡은 메모리주소로서 유효하게 할 수도 있습니다.메모리 주소 0에 대해 마법 같은 것은 없습니다.그것은, 지금까지 특수한 목적(리셋 벡터등)으로 사용되고 있었을 뿐입니다.C와 같은 언어는 NULL 포인터의 주소로서0 을 사용하는 것으로, 이 전통을 따르고 있습니다만, 실제로는, 기반이 되는 하드웨어에서는 주소 0 이 특별한 것으로 인식되거나 인식되지 않는 경우가 있습니다.

주소 0 에 액세스 할 필요가 생기는 것은, 통상, 부트로더나 드라이버등의 저레벨의 상세 정보에서만 발생합니다.이러한 경우 컴파일러는 최적화 없이 코드 섹션을 컴파일하는 옵션/프라그마를 제공하거나(제로 포인터가 NULL 포인터로 추출되는 것을 방지하기 위해) 인라인 어셈블리를 사용하여 진정한 주소 0에 액세스할 수 있습니다.

C/C++ 는, 어느 주소에도 쓸 수 없습니다.사용자가 금지된 주소에 액세스할 때 신호를 발생시킬 수 있는 OS입니다.C 와 C++ 는, 히프에서 취득한 메모리가 0 이 되도록 합니다.

사용자가 필요한 조건을 위반하여 예외를 발생시킬 수 있는 좋은 방법이 없을 경우 라이브러리 코드의 비공식적인 이름의 기호로 의도적으로 크래시하기 위해 주소 0(세그 폴트가 보장되는 알려진 플랫폼)에서 로드를 사용한 적이 있습니다."Segfault at someFunction$xWasnt16ByteAligned「」는, 다른 사람이 무엇을 잘못했는지, 그것을 어떻게 수정하는지를 경고하는 매우 효과적인 에러 메세지입니다.그렇다고 해서 그런 습관을 들이는 것은 추천하지 않습니다.

주소 제로 기입은 가능하지만 OS, 타깃 아키텍처, MMU 구성 등 여러 요소에 따라 달라집니다.실제로 유용한 디버깅툴이 될 수 있습니다(항상 도움이 되는 것은 아닙니다).

예를 들어 몇 년 전 임베디드 시스템에서 작업할 때(사용 가능한 디버깅 툴이 거의 없음) 문제가 발생하여 웜 재부팅이 발생하였습니다.문제를 특정하기 위해 sprintf(NULL, ...) 및 9600 보시리얼 케이블을 사용하여 디버깅을 실시했습니다.이미 말씀드렸듯이 사용 가능한 디버깅툴은 거의 없습니다.셋업에서는 웜리부트를 해도 첫 256바이트의 메모리가 파손되지 않는다는 것을 알았습니다.따라서 웜 리부트 후 로더를 일시 정지하고 메모리 내용을 덤프하여 리부트 전에 무슨 일이 일어났는지 확인할 수 있습니다.

일반적인 경우라면, 실제로는 특정의 주소가 표시되지 않는 경우가 있습니다.메모리를 할당할 때 OS는 메모리 청크의 주소를 제공합니다.

변수를 참조할 때 변수는 시스템에 의해 결정된 주소로 이미 할당되어 있습니다.

따라서 주소 0에 액세스하는 것은 문제가 되지 않습니다. 왜냐하면 포인터를 팔로우할 때 포인터가 가리키는 주소가 무엇인지는 상관하지 않고 유효하기 때문입니다.

int* i = new int(); // suppose this returns a pointer to address zero
*i = 42; // now we're accessing address zero, writing the value 42 to it

주소 0에 액세스 할 필요가 있는 경우는, 일반적으로 정상적으로 동작합니다.

어떤 이유로 물리적 메모리에 직접 액세스하는 경우에만 0 == null이 문제가 됩니다.아마 직접 OS 커널 같은 것을 쓰고 있을 것입니다.이 경우 특정 메모리 주소(특히 하드웨어 레지스터에 매핑된 주소)에 쓸 수 있으므로 주소 0에 쓸 필요가 있을 수 있습니다.그러나 실제로는 C++를 무시하고 컴파일러와 하드웨어 플랫폼에 의존하게 됩니다.

물론 주소 0에 쓸 필요가 있는 경우는 가능합니다.상수만 0는 늘 포인터를 나타냅니다.정수 값 0이 일정하지 않은 경우 포인터에 할당되어도 늘 포인터가 생성되지 않습니다.

따라서 다음과 같은 작업을 수행할 수 있습니다.

int i = 0;
int* zeroaddr = (int*)i;

0addr은 주소 0을 가리킵니다). 단, 엄밀히 말하면 제로 값이 일정하지 않기 때문에 늘포인터가 되지 않습니다

(*): 완전히 사실이 아닙니다.C++ 표준은 정수와 주소 사이의 "실장 정의 매핑"만 보장합니다.0을 주소 0x1633de20' 또는 원하는 다른 주소로 변환할 수 있습니다.단, 일반적으로 매핑은 직관적이고 명백한 것으로 정수0이 주소0에 매핑됩니다).

내 기억이 정확하다면, AVR 마이크로 컨트롤러에서는 레지스터 파일이 RAM의 주소 공간에 매핑되고 레지스터 R0은 주소 0x00에 있습니다.그것은 분명히 의도적인 것이었고, Atmel은 R0을 명시적으로 쓰는 것이 아니라 주소 0x00에 접근하는 것이 편리할 때 상황이 있다고 생각하는 것 같습니다.

프로그램 메모리의 주소 0x0000에는 리셋 인터럽트 벡터가 있으며, 이 주소는 칩을 프로그래밍할 때 액세스하도록 되어 있습니다.

언급URL : https://stackoverflow.com/questions/2761360/could-i-ever-want-to-access-the-address-zero

반응형