C에서 'char**'를 'const char* const*'로 변환할 수 없는 이유는 무엇입니까?
-
09-06-2019 - |
문제
다음 코드 조각은 (정확하게) C에서 경고를 제공하고 C++에서 오류를 제공합니다(각각 gcc & g++ 사용, 버전 3.4.5 및 4.2.1에서 테스트됨).MSVC는 신경 쓰지 않는 것 같습니다):
char **a;
const char** b = a;
나는 이것을 이해하고 받아들일 수 있다.
이 문제에 대한 C++ 해결책은 b를 const char * const *로 변경하는 것입니다. 이렇게 하면 포인터 재할당을 허용하지 않고 const 정확성을 우회하는 것을 방지할 수 있습니다(C++ FAQ).
char **a;
const char* const* b = a;
그러나 순수 C에서는 수정된 버전(const char * const * 사용)이 여전히 경고를 표시하며 그 이유를 이해할 수 없습니다.캐스트를 사용하지 않고 이 문제를 해결할 수 있는 방법이 있나요?
명확히 하기 위해:
1) C에서 경고가 생성되는 이유는 무엇입니까?완전히 const로부터 안전해야 하며 C++ 컴파일러는 이를 그렇게 인식하는 것 같습니다.
2) 이 char**를 매개변수로 받아들이는 동시에 그것이 가리키는 문자를 수정하지 않을 것이라고 말하면서(그리고 컴파일러가 시행하도록 하는) 올바른 방법은 무엇입니까?예를 들어, 함수를 작성하고 싶다면 다음과 같습니다.
void f(const char* const* in) {
// Only reads the data from in, does not write to it
}
그리고 char**에서 이를 호출하고 싶었습니다. 매개변수의 올바른 유형은 무엇입니까?
편집하다:답변을 주신 분들께 감사드립니다. 특히 질문에 답변을 주거나 제 답변에 후속 조치를 취해주신 분들께 감사드립니다.
내가 하고 싶은 일은 가능 여부를 떠나 캐스트 없이는 할 수 없다는 답변을 받아들였습니다.
해결책
나는 몇 년 전에 이와 같은 문제를 겪었고 그것은 나를 끝없이 짜증나게 했습니다.
C의 규칙은 더 간단하게 설명됩니다(예:변환과 같은 예외는 나열하지 않습니다. char**
에게 const char*const*
).결과적으로 그것은 허용되지 않습니다.C++ 표준에는 이와 같은 경우를 허용하는 더 많은 규칙이 포함되었습니다.
결국 C 표준의 문제일 뿐입니다.다음 표준(또는 기술 보고서)에서 이 문제가 해결되기를 바랍니다.
다른 팁
호환 가능한 것으로 간주하려면 소스 포인터가 바로 앞 간접 참조 수준에서 const여야 합니다.따라서 GCC에서는 다음과 같은 경고가 표시됩니다.
char **a;
const char* const* b = a;
하지만 다음은 그렇지 않습니다.
const char **a;
const char* const* b = a;
또는 다음과 같이 캐스팅할 수 있습니다.
char **a;
const char* const* b = (const char **)a;
언급한 대로 f() 함수를 호출하려면 동일한 캐스트가 필요합니다.내가 아는 한, 이 경우 암시적 변환을 수행할 수 있는 방법은 없습니다(C++ 제외).
> 그러나 순수 C에서는 여전히 경고가 표시되며 왜 그런지 모르겠습니다.
이미 문제를 확인했습니다. 이 코드는 const가 정확하지 않습니다."Const 올바른"은 const_cast 및 const를 제거하는 C 스타일 캐스트를 제외하고는 해당 const 포인터나 참조를 통해 const 개체를 수정할 수 없음을 의미합니다.
const-correctionness의 가치 - const는 대부분 프로그래머 오류를 감지하기 위해 존재합니다.무언가를 const로 선언하면 수정해서는 안 된다고 생각하는 것입니다. 또는 적어도 const 버전에만 액세스할 수 있는 사용자는 이를 수정할 수 없어야 합니다.고려하다:
void foo(const int*);
선언된 대로 foo에는 없습니다. 허가 인수가 가리키는 정수를 수정합니다.
게시한 코드가 왜 const-올바르지 않은지 확실하지 않은 경우 HappyDude의 코드와 약간 다른 다음 코드를 고려해 보세요.
char *y;
char **a = &y; // a points to y
const char **b = a; // now b also points to y
// const protection has been violated, because:
const char x = 42; // x must never be modified
*b = &x; // the type of *b is const char *, so set it
// with &x which is const char* ..
// .. so y is set to &x... oops;
*y = 43; // y == &x... so attempting to modify const
// variable. oops! undefined behavior!
cout << x << endl;
비-const 유형은 명시적인 캐스트 없이 데이터 유형에서 'const'를 우회하는 것을 방지하기 위해 특정 방식으로 const 유형으로만 변환할 수 있습니다.
처음에 const로 선언된 객체는 특히 특별합니다. 컴파일러는 객체가 절대 변경되지 않는다고 가정할 수 있습니다.그러나 형변환 없이 'b'에 'a' 값을 할당할 수 있으면 실수로 const 변수를 수정하려고 시도할 수 있습니다.이렇게 하면 컴파일러에 요청한 검사가 중단되어 해당 변수 값을 변경할 수 없게 될 뿐만 아니라 컴파일러 최적화도 중단될 수 있습니다!
일부 컴파일러에서는 '42'가 인쇄되고, 일부 컴파일러에서는 '43'이 인쇄되며, 다른 컴파일러에서는 프로그램이 중단됩니다.
편집-추가:
행복한 친구:귀하의 의견이 정확합니다.C 언어나 사용 중인 C 컴파일러는 const char * const *를 C++ 언어가 처리하는 것과 근본적으로 다르게 처리합니다.아마도 이 소스 라인에 대해서만 컴파일러 경고를 침묵시키는 것을 고려해 보십시오.
편집-삭제: 오타가 제거됨
이는 짜증나는 일이지만 다른 수준의 리디렉션을 추가하려는 경우 포인터 간 포인터로 푸시다운하기 위해 다음을 수행할 수 있는 경우가 많습니다.
char c = 'c';
char *p = &c;
char **a = &p;
const char *bi = *a;
const char * const * b = &bi;
약간 다른 의미가 있지만 일반적으로 실행 가능하며 캐스트를 사용하지 않습니다.
적어도 MSVC 14(VS2k5) 및 g++ 3.3.3에서는 char**를 const char * const *로 암시적으로 캐스팅할 때 오류가 발생할 수 없습니다.GCC 3.3.3에서는 경고가 표시되는데, 이것이 올바른지 확실하지 않습니다.
테스트.c:
#include <stdlib.h>
#include <stdio.h>
void foo(const char * const * bar)
{
printf("bar %s null\n", bar ? "is not" : "is");
}
int main(int argc, char **argv)
{
char **x = NULL;
const char* const*y = x;
foo(x);
foo(y);
return 0;
}
C 코드로 컴파일하여 출력:cl /TC /W4 /Wp64 테스트.c
test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter
C++ 코드로 컴파일하여 출력:cl /TP /W4 /Wp64 테스트.c
test.c(8) : warning C4100: 'argv' : unreferenced formal parameter
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter
gcc로 출력:gcc -벽 테스트.c
test2.c: In function `main':
test2.c:11: warning: initialization from incompatible pointer type
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type
g++로 출력:g++ -벽 테스트.C
출력 없음
나는 const 키워드가 데이터가 변경될 수 없거나 일정하다는 것을 의미하는 것이 아니라 데이터가 읽기 전용으로 처리된다는 것을 확신합니다.이걸 고려하세요:
const volatile int *const serial_port = SERIAL_PORT;
유효한 코드입니다.휘발성과 const가 어떻게 공존할 수 있나요?단순한.휘발성은 컴파일러에게 데이터를 사용할 때 항상 메모리를 읽도록 지시하고, const는 serial_port 포인터를 사용하여 메모리에 쓰려고 할 때 컴파일러에게 오류를 생성하도록 지시합니다.
const가 컴파일러의 최적화 프로그램에 도움이 됩니까?아니요.별말씀을요.캐스팅을 통해 데이터에 불변성을 추가하거나 제거할 수 있기 때문에 컴파일러는 불변 데이터가 실제로 상수인지 알아낼 수 없습니다(캐스트는 다른 변환 단위에서 수행될 수 있으므로).C++에는 문제를 더욱 복잡하게 만드는 가변 키워드도 있습니다.
char *const p = (char *) 0xb000;
//error: p = (char *) 0xc000;
char **q = (char **)&p;
*q = (char *)0xc000; // p is now 0xc000
실제로 읽기 전용인 메모리(예: ROM)에 쓰려고 하면 어떻게 되는지가 표준에 전혀 정의되어 있지 않을 것입니다.