Programing

const 포인터의 요점은 무엇입니까?

crosscheck 2020. 6. 15. 21:45
반응형

const 포인터의 요점은 무엇입니까?


const 값에 대한 포인터가 아니라 const 포인터 자체에 대해 이야기하고 있습니다.

나는 매우 기본적인 것들을 넘어서 C와 C ++을 배우고 있으며 오늘까지 포인터가 함수에 가치에 의해 전달된다는 것을 깨달았습니다. 이것은 함수 내에서 호출자의 원래 포인터에 영향을 미치지 않고 복사 된 포인터가 다른 값을 가리 키도록 할 수 있음을 의미합니다.

따라서 함수 헤더가 갖는 요점은 무엇입니까?

void foo(int* const ptr);

그러한 함수 안에서는 ptr이 const이기 때문에 다른 것을 가리킬 수 없으며 수정하기를 원하지 않지만 다음과 같은 함수는 다음과 같습니다.

void foo(int* ptr);

작업도 마찬가지입니다! 어쨌든 포인터가 복사되고 사본을 수정하더라도 호출자의 포인터는 영향을받지 않기 때문입니다. const의 장점은 무엇입니까?


const 매우 중요한 C ++ 개념을 추구하는 데 사용해야하는 도구입니다.

컴파일러가 의미하는 바를 적용하도록하여 런타임이 아닌 컴파일 타임에 버그를 찾으십시오.

기능이 변경되지 않더라도 추가를 const하면 의도하지 않은 작업을 수행 할 때 컴파일러 오류가 발생합니다. 다음 오타를 상상해보십시오.

void foo(int* ptr)
{
    ptr = 0;// oops, I meant *ptr = 0
}

을 사용 int* const하면 값을로 변경하기 때문에 컴파일러 오류가 발생합니다 ptr. 일반적으로 구문을 통해 제한을 추가하는 것이 좋습니다. 너무 많이 들지 마십시오. 예를 들어 대부분의 사람들이 귀찮게하지 않는 경우 const입니다.


더 많은 컴파일러 검사를 가능하게하기 때문에 인수 사용한다는 점을 지적 const합니다. 실수로 함수 내부에서 인수 값을 실수로 다시 할당하면 컴파일러가 물립니다.

나는 거의 변수를 재사용하지 않고 새로운 값을 보유하기 위해 새로운 변수를 만드는 것이 더 깨끗합니다. 따라서 본질적으로 모든 변수 선언은 const( const코드가 작동하지 못하게하는 루프 변수와 같은 일부 경우 제외 )입니다.

이것은 함수 정의 에서만 의미 가 있습니다. 그것은 사용자에게 보이는 선언 에 속하지 않습니다 . 그리고 사용자는 const함수 내부의 매개 변수에 사용할지 신경 쓰지 않습니다 .

예:

// foo.h
int frob(int x);
// foo.cpp
int frob(int const x) {
   MyConfigType const config = get_the_config();
   return x * config.scaling;
}

인수와 지역 변수가 모두 어떻게되는지 확인하십시오 const. 없는 둘 필요 하지만, 조금이라도 큰 기능이 반복적으로 실수에서 저를 저장하고있다.


당신의 질문은 좀 더 일반적인 것을 만집니다 : 함수 인수는 const이어야합니까?

포인터와 같은 값 인수의 constness는 구현 세부 사항 이며 함수 선언의 일부를 형성 하지 않습니다 . 이것은 당신의 기능이 항상 이것임을 의미합니다 :

void foo(T);

함수 범위 인수 변수를 가변 또는 일정한 방식으로 사용하고자하는지 여부 는 전적으로 함수 구현 자 에게 달려 있습니다.

// implementation 1
void foo(T const x)
{
  // I won't touch x
  T y = x;
  // ...
}

// implementation 2
void foo(T x)
{
  // l33t coding skillz
  while (*x-- = zap()) { /* ... */ }
}

따라서 간단한 규칙 const에 따라 선언 (헤더)에 넣지 말고 변수를 수정하지 않으려는 경우 정의 (구현)에 넣으십시오.


최상위 const 한정자는 선언에서 삭제되므로 질문의 선언은 정확히 동일한 함수를 선언합니다. 반면, 정의 (구현)에서 컴파일러는 포인터를 const로 표시하면 함수 본문 내에서 수정되지 않았는지 확인합니다.


const키워드 에는 많은 것이 있으며 다소 복잡한 키워드입니다. 일반적으로 프로그램에 많은 const를 추가하는 것은 좋은 프로그래밍 관행으로 간주되고 웹에서 "const correctness"를 검색하면 이에 대한 많은 정보를 찾을 수 있습니다.

const 키워드는 소위 "유형 한정자"이고 다른 키워드는 volatileand restrict입니다. 최소한 휘발성은 const와 같은 (혼란스러운) 규칙을 따릅니다.


먼저 const 키워드는 두 가지 목적을 제공합니다. 가장 확실한 방법은 데이터 (및 포인터)를 읽기 전용으로 만들어서 의도적이거나 우발적 인 오용으로부터 보호하는 것입니다. const 변수를 수정하려는 시도는 컴파일 타임에 컴파일러에 의해 발견됩니다.

그러나 읽기 전용 메모리가있는 시스템의 또 다른 목적도 있습니다. 즉, 특정 변수가 이러한 메모리 내에 할당되도록하는 것입니다. 예를 들어 EEPROM이거나 플래시 일 수 있습니다. 이를 비 휘발성 메모리 (NVM)라고합니다. NVM에 할당 된 변수는 여전히 const 변수의 모든 규칙을 따릅니다.

const키워드 를 사용하는 방법에는 여러 가지가 있습니다 .

상수 변수를 선언하십시오.

이것은 다음과 같이 수행 할 수 있습니다

const int X=1; or
int const X=1;

이 두 형식은 완전히 같습니다 . 후자의 스타일은 잘못된 스타일로 간주되므로 사용하지 않아야합니다.

두 번째 행이 나쁜 스타일로 간주되는 이유는, 아마 때문에 "저장소 클래스 지정자"정적으로와 통근도 선언 할 수 있습니다 , 실제 타입 int static사용되지 않는 기능으로 표시되는 등 그러나 저장소 클래스 지정자에 대해 이렇게 C위원회에 의해 (ISO 9899 N1539 초안, 6.11.5). 따라서 일관성을 유지하기 위해 형식 한정자를 해당 방식으로 작성해서는 안됩니다. 독자를 혼란스럽게하는 것 외에는 다른 목적이 없습니다.

상수 변수에 대한 포인터를 선언하십시오.

const int* ptr = &X;

이는 'X'의 내용을 수정할 수 없음을 의미합니다. 이것은 "const correctness"에 대한 함수 매개 변수의 일부로 이와 같은 포인터를 선언하는 일반적인 방법입니다. 'X'는 실제로 const로 선언 될 필요가 없으므로 변수 일 수 있습니다. 즉, 변수를 항상 "업그레이드"하여 const로 만들 수 있습니다. 기술적으로 C는 명시 적 타입 캐스트에 의해 const에서 일반 변수로 다운 그레이드 할 수 있지만 그렇게하는 것은 나쁜 프로그래밍으로 간주되며 컴파일러는 일반적으로 경고를합니다.

상수 포인터 선언

int* const ptr = &X;

이것은 포인터 자체 가 일정 하다는 것을 의미합니다 . 가리키는 내용은 수정할 수 있지만 포인터 자체는 수정할 수 없습니다. 이것은 많은 용도가 없으며, 포인터가 가리키는 포인터 (pointer-to-pointer)가 매개 변수로 함수에 전달되는 동안 주소가 변경되지 않도록하는 것과 같은 몇 가지가 있습니다. 다음과 같이 읽을 수없는 것을 작성해야합니다.

void func (int*const* ptrptr)

많은 C 프로그래머가 const와 *를 얻을 수 있을지 의문입니다. 나도 내가 할 수 없습니다 - 나는 GCC 확인했다. 이것이 좋은 프로그래밍 관행으로 간주되지만 포인터 대 포인터의 구문을 거의 보지 못하는 이유입니다.

상수 포인터를 사용하여 포인터 변수 자체가 읽기 전용 메모리에 선언되도록 할 수도 있습니다. 예를 들어 포인터 기반 조회 테이블을 선언하여 NVM에 할당 할 수 있습니다.

물론 다른 답변에서 알 수 있듯이 상수 포인터를 사용하여 "정확도"를 강화할 수도 있습니다.

상수 데이터에 대한 상수 포인터 선언

const int* const ptr=&X;

이것은 위에서 설명한 두 가지 포인터 유형이며 모든 속성이 있습니다.

읽기 전용 멤버 함수 선언 (C ++)

이것은 C ++ 태그가 붙어 있기 때문에 클래스의 멤버 함수를 const로 선언 할 수 있다고 언급해야합니다. 이것은 함수가 호출 될 때 클래스의 다른 멤버를 수정할 수 없다는 것을 의미합니다. 이는 클래스의 프로그래머가 실수로 오류가 발생하는 것을 막을뿐 아니라 멤버 함수의 호출자에게 아무것도 엉망이되지 않음을 알려줍니다 그것을 호출하여. 구문은 다음과 같습니다.

void MyClass::func (void) const;

당신이 옳습니다. 발신자에게는 전혀 차이가 없습니다. 그러나 함수 작성자에게는 안전망이 될 수 있습니다. "알겠습니다.이 점을 잘못 지적하지 않아야합니다." 매우 유용하지는 않지만 쓸모가 없습니다.

기본적으로 int const the_answer = 42프로그램에 있는 것과 같습니다 .


... 오늘 포인터가 함수에 값으로 전달된다는 것을 깨달았습니다.

(imo) 실제로 기본값으로 이해가되지 않습니다. 더 합리적인 기본값은 재 할당 할 수없는 포인터 ( int* const arg) 로 전달하는 것 입니다. 즉, 인수로 전달 된 포인터가 암시 적으로 const로 선언 된 것을 선호했을 것입니다.

const의 장점은 무엇입니까?

장점은 인수가 가리키는 주소를 수정할 때 충분히 쉽고 때로는 명확하지 않다는 것입니다. 따라서 버그가 쉽게 구성되지 않을 때 버그가 발생할 수 있습니다. 주소를 변경하는 것은 비정형입니다. 주소를 수정하려는 경우 로컬 변수를 만드는 것이 더 명확합니다. 또한 원시 포인터 조작은 버그를 쉽게 도입 할 수있는 방법입니다.

따라서 불변 주소로 전달하고 인수가 가리키는 주소를 변경하려고 할 때 (비정상적인 경우) 사본을 만드는 것이 더 명확합니다.

void func(int* const arg) {
    int* a(arg);
    ...
    *a++ = value;
}

로컬을 추가하면 거의 무료이며, 가독성을 높이면서 오류 가능성을 줄입니다.

더 높은 수준에서 : 인수를 배열로 조작하는 경우 일반적으로 클라이언트가 인수를 컨테이너 / 컬렉션으로 선언하는 것이 더 명확하고 오류가 적습니다.

일반적으로 컴파일러가 행복하게 적용하는 부작용을 항상 인식하지는 못하므로 값, 인수 및 주소에 const를 추가하는 것이 좋습니다. 따라서 다른 여러 경우에 사용되는 const만큼 유용합니다 (예 : '왜 const를 값으로 선언해야합니까?'와 같은 질문입니다). 운 좋게도 우리는 또한 재 할당 할 수없는 참조를 가지고 있습니다.


메모리에 매핑 된 장치가있는 임베디드 시스템 또는 장치 드라이버 프로그래밍을 수행하는 경우 두 가지 형식의 'const'가 종종 사용됩니다 (하나는 고정 하드웨어 주소를 가리 키기 때문에). 포인터가 재 할당되지 않도록합니다. 레지스터가 읽기 전용 하드웨어 레지스터를 가리키면 다른 const가 런타임보다 컴파일 타임에 많은 오류를 감지합니다.

읽기 전용 16 비트 주변 장치 칩 레지스터는 다음과 같습니다.

static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;

그러면 어셈블리 언어에 의존하지 않고도 하드웨어 레지스터를 쉽게 읽을 수 있습니다.

input_word = *peripheral;


int iVal = 10; int * const ipPtr = & iVal;

일반 const 변수와 마찬가지로 const 포인터는 선언시 값으로 초기화되어야하며 값을 변경할 수 없습니다.

이것은 const 포인터가 항상 같은 값을 가리킴을 의미합니다. 위의 경우 ipPtr은 항상 iVal의 주소를 가리 킵니다. 그러나 가리키는 값이 여전히 일정하지 않기 때문에 포인터 역 참조를 통해 가리키는 값을 변경할 수 있습니다.

* ipPtr = 6; // pnPtr이 비 const int를 가리 키므로 허용


포인터뿐만 아니라 다른 유형에 대해서도 동일한 질문을 할 수 있습니다.

/* Why is n const? */
const char *expand(const int n) {
    if (n == 1) return "one";
    if (n == 2) return "two";
    if (n == 3) return "three";
    return "many";
}

귀하의 질문은 실제로 변수를 const 포인터 매개 변수가 아닌 const로 정의하는 이유에 대한 것입니다. 함수 또는 멤버 변수 또는 로컬 변수에 대한 매개 변수 인 경우 변수를 상수로 정의 할 때와 동일한 규칙이 여기에 적용됩니다.

특정 경우에는 기능적으로 로컬 변수를 const로 선언 할 때 다른 많은 경우와 마찬가지로 차이가 없지만이 변수를 수정할 수 없다는 제한이 있습니다.


const 포인터를 함수에 전달하는 것은 어쨌든 값에 의해 전달되므로 의미가 없습니다. 일반적인 언어 디자인에서 허용되는 것 중 하나 일뿐입니다. 이해가되지 않기 때문에 금지하면 언어 사양이됩니다. 더 큰.

함수 안에 있다면 물론 다른 경우입니다. 가리키는 것을 변경할 수없는 포인터를 갖는 것은 코드를 더 명확하게하는 주장입니다.


컴파일러 가이 포인터를 변경할 수 없다는 것을 알고 함수 내에서보다 적극적인 최적화를 수행 할 수 있다는 이점이 있다고 생각합니다.

또한 예를 들어 피한다. 이 포인터를 const가 아닌 포인터 참조를 허용하는 하위 함수에 전달하면 포인터가와 같이 변경 될 수 void f(int *&p)있지만이 경우 유용성이 다소 제한적입니다.


const 포인터가 매우 적용 가능한 예가 이와 같이 설명 될 수있다. 내부에 동적 배열이있는 클래스가 있고 사용자에게 포인터 변경 권한을 부여하지 않고 배열에 대한 액세스 권한을 전달하려고합니다. 치다:

#include <new>
#include <string.h>

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const GetArray(){ return Array; }
};

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
    Temp.GetArray()[1] = ' '; 
    printf("%s\n",Temp.GetArray());
}

어느 생산 :

입력 데이터
넣기 데이터

그러나 우리가 이것을 시도하면 :

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}

우리는 얻는다 :

오류 : 할당의 왼쪽 피연산자로 lvalue가 필요합니다.

So clearly we can modify the array's contents, but not the array's pointer. Good if you want to make sure the pointer has a consistent state when passing it back to the user. There is one catch, though:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    delete [] Temp.GetArray(); //Bwuahaha this actually works!
}

We can still delete the pointer's memory reference, even if we can't modify the pointer itself.

So if you want the memory reference to always point to something (IE never be modified, similar to how a reference currently works), then it's highly applicable. If you want the user to have full access and modify it, then non-const is for you.

Edit:

After noting okorz001 comment of not being able to assign due to GetArray() being a right-value operand, his comment is entirely correct, but the above still applies if you were to return a reference to the pointer (I suppose I assumed GetArray was referring a reference), for example:

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; } //Note & reference operator
        char * &GetNonConstArray(){ return Array; } //Note non-const
};

int main()
{
    TestA Temp;
    Temp.GetArray() = NULL; //Returns error
    Temp.GetNonConstArray() = NULL; //Returns no error
}

Will return in the first resulting in an error:

error: assignment of read-only location 'Temp.TestA::GetArray()'

But the second will occur merrily despite potential consequences on the underneath.

Obviously, the question will be raised 'why would you want to return a reference to a pointer'? There are rare instances where you need to assign memory (or data) directly to the original pointer in question (for example, building your own malloc/free or new/free front-end), but in those instances it's a non-const reference. A reference to a const pointer I've not come across a situation that would warrant it (unless maybe as declared const reference variables rather than return types?).

Consider if we have a function that takes a const pointer (versus one that doesn't):

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; }

        void ModifyArrayConst(char * const Data)
        {
            Data[1]; //This is okay, this refers to Data[1]
            Data--; //Produces an error. Don't want to Decrement that.
            printf("Const: %c\n",Data[1]);
        }

        void ModifyArrayNonConst(char * Data)
        {
            Data--; //Argh noo what are you doing?!
            Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
            printf("NonConst: %c\n",Data[1]);
        }
};

int main()
{
    TestA Temp;
    Temp.ModifyArrayNonConst("ABCD");
    Temp.ModifyArrayConst("ABCD");
}

The error in the const produces thus message:

error: decrement of read-only parameter 'Data'

Which is good as we probably don't want to do that, unless we want to cause the problems denoted in the comments. If we edit out the decrement in the const function, the following occurs:

NonConst: A
Const: B

Clearly, even though A is 'Data[1]', it's being treated as 'Data[0]' because the NonConst pointer permitted the decrement operation. With the const implemented, as another person writes, we catch the potential bug before it occurs.

One other main consideration, is that a const pointer can be used as a pseudo reference, in that the thing the reference points to cannot be changed (one wonders, if perhaps this was how it was implemented). Consider:

int main()
{
    int A = 10;
    int * const B = &A;
    *B = 20; //This is permitted
    printf("%d\n",A);
    B = NULL; //This produces an error
}

When attempting to compile, produces the following error:

error: assignment of read-only variable 'B'

Which is probably a bad thing if a constant reference to A was wanted. If B = NULL is commented out, the compiler will happily let us modify *B and therefore A. This might not seem useful with ints, but consider if you had a single stance of a graphical application where you wanted an unmodifiable pointer that referred to it that you could pass around.

It's usage is variable (excuse the unintended pun), but used correctly, it is another tool in the box to assist with programming.


포인터가 const가되기를 원하지 않는 포인터에는 특별한 것이 없습니다. 클래스 멤버 상수 int값을 가질 수있는 것과 마찬가지로 유사한 이유로 상수 포인터를 가질 수도 있습니다. 가리키고있는 것을 변경하는 사람이 없도록하십시오. C ++ 참조는이 문제를 다소 해결하지만 포인터 동작은 C에서 상속됩니다.


이것이 코드가 함수 본문 내에서 포인터를 늘리거나 줄이는 것을 막을 것이라고 생각합니다.



(1) 상수 변수 선언과 같은 변수 선언 유형
DataType const varibleName;

 int const x;
    x=4; //you can assign its value only One time
(2) 상수 변수에 대한 포인터를 선언하십시오.
const dataType* PointerVaribleName=&X;
 const int* ptr = &X;
     //Here pointer variable refer contents of 'X' that is const Such that its cannot be modified
dataType* const PointerVaribleName=&X;
 int* const ptr = &X;
     //Here pointer variable itself is constant  Such that value of 'X'  can be modified But pointer can't be modified

참고 URL : https://stackoverflow.com/questions/7715371/whats-the-point-of-const-pointers

반응형