code

C ++에서 const char *에 const char을 할당 할 수있는 이유는 무엇입니까?!

codestyles 2020. 11. 1. 18:09
반응형

C ++에서 const char *에 const char을 할당 할 수있는 이유는 무엇입니까?!


놀랍게도 이것은 다음과 같이 컴파일됩니다.

const char* c_str()
{
    static const char nullchar = '\0';
    return nullchar;
}

내 코드에 버그가 생겼습니다. 고맙게도 잡았습니다.

이것은 C ++의 의도입니까, 아니면 컴파일러 버그입니까? 데이터 유형이 적극적으로 무시되는 이유가 있습니까?
Visual C ++ 2010 및 GCC 에서 작동했지만 명백한 데이터 유형 불일치를 감안할 때 작동해야하는 이유를 이해할 수 없습니다. ( static그것도 필요하지 않습니다.)


정의한대로 nullchar는 값이 0 인 정수 상수 표현식입니다.

C ++ 03 표준은 널 포인터 상수를 다음과 같이 정의합니다. "널 포인터 상수는 0으로 평가되는 정수 유형의 정수 상수 표현식 (5.19) rvalue입니다." 긴 이야기를 짧게 만들려면 nullcharnull 포인터 상수입니다. 즉, 암시 적으로 변환되고 본질적으로 모든 포인터에 할당 될 수 있습니다.

암시 적 변환이 작동하려면 이러한 모든 요소가 필요합니다. 예를 들어, 당신이 사용했던 '\1'대신에 '\0', 또는 당신이했다면 하지 지정된 const대한 규정 nullchar, 당신은 암시 적 변환을 얻을 것이다 - 당신의 임무가 실패했을 것이다.

이 변환의 포함은 의도적이지만 바람직하지 않은 것으로 널리 알려져 있습니다. 0은 null 포인터 상수로 C에서 상속되었습니다. Bjarne과 대부분의 C ++ 표준위원회 (및 일반적으로 대부분의 C ++ 커뮤니티)가이 특정 암시 적 변환을 제거하는 것을 정말 좋아할 것이라고 확신합니다. 많은 C 코드와의 호환성을 파괴 할 수 있습니다 (아마 모두에 가깝습니다).


이것은 오래된 역사입니다. C로 거슬러 올라갑니다.

nullC 에는 키워드 가 없습니다 . C의 널 포인터 상수는 다음 중 하나입니다.

  • 값이 0과 같은 상수 일체 식 0, 0L, '\0'(즉 기억 char일체형이다)(2-4/2)
  • 이러한 표현 캐스트에 void*같은 (void*)0, (void*)0L, (void*)'\0',(void*)(2-4/2)

NULL 매크로 (안 키워드!) 같은 널 포인터 상수로 확장됩니다.

첫 번째 C ++ 디자인에서는 정수 상수 표현식 만 널 포인터 상수로 허용되었습니다. 최근 std::nullptr_tC ++에 추가되었습니다.

C ++에서, 그러나 C에서는 const정수 상수 식으로 초기화 된 정수 유형 변수는 정수 상수 식입니다.

const int c = 3;
int i;

switch(i) {
case c: // valid C++
// but invalid C!
}

따라서 const char표현식 으로 초기화 된 '\0'것은 널 포인터 상수입니다.

int zero() { return 0; }

void foo() {
    const char k0 = '\0',
               k1 = 1,
               c = zero();
    int *pi;

    pi = k0; // OK (constant expression, value 0)
    pi = k1; // error (value 1)
    pi = c; // error (not a constant expression)
}

그리고 이것이 건전한 언어 디자인이 아니라고 생각하십니까?


C99 표준의 관련 부분을 포함하도록 업데이트 됨 ... §6.6.6에 따라 ...

정수 상수 표현은 정수형을 가진다 만 상수, 열거 상수, 문자 상수, 정수 피연산자 가진다 sizeof그 결과 상수 정수 표현하고, 캐스트의 즉각적인 피연산자 정수를 부동. 정수 상수 표현식의 캐스트 연산자는 연산자에 대한 피연산자의 일부를 제외하고 산술 유형을 정수 유형으로 만 변환해야합니다 sizeof.

C ++ 전용 프로그래머를위한 몇 가지 설명 :

  • C는 C ++ 프로그래머가 "리터럴"로 알고있는 것을 "상수"라는 용어를 사용합니다.
  • C ++에서는 sizeof항상 컴파일 시간 상수입니다. 그러나 C에는 가변 길이 배열이 있으므로 sizeof때로는 컴파일 시간 상수가 아닙니다 .

그러면 §6.3.2.3.3 상태가 표시됩니다.

값이 0 인 정수 상수 표현식 또는 type으로 캐스트 된 표현식을 널 포인터 상수void * 라고합니다 . 널 포인터 상수는 포인터 형식으로 변환되는 경우라는 결과 포인터, 널 포인터는 어떤 객체 나 함수에 대한 포인터에 불평등 비교하기 위해 보장됩니다.


이 기능이 얼마나 오래되었는지 확인하려면 C99 표준 의 동일한 미러링 부품을 참조하십시오 .

§6.6.6

정수 상수 표현은 정수형을 가진다 만 상수, 열거 상수, 문자 상수, 정수 피연산자 가진다 sizeof그 결과 상수 정수 표현하고, 캐스트의 즉각적인 피연산자 정수를 부동. 정수 상수 표현식의 캐스트 연산자는 연산자에 대한 피연산자의 일부를 제외하고 산술 유형을 정수 유형으로 만 변환해야합니다 sizeof.

§6.3.2.3.3

값이 0 인 정수 상수 표현식 또는 type으로 캐스트 된 표현식을 널 포인터 상수void * 라고합니다 . 널 포인터 상수는 포인터 형식으로 변환되는 경우라는 결과 포인터, 널 포인터는 어떤 객체 나 함수에 대한 포인터에 불평등 비교하기 위해 보장됩니다.


nullchar 은 값이 0 인 (컴파일 시간) 상수 표현식입니다. 따라서 널 포인터로의 암시 적 변환을위한 공정한 게임입니다.

더 자세히 : 1996 년 표준 초안 에서 인용하고 있습니다.

char정수 유형입니다. nullcharconst이므로 섹션 5.19.1에 따라 (컴파일 시간) 정수 상수 표현식입니다.

5.19 상수 표현식 [expr.const]

1 여러 곳에서 C ++에는 정수 또는 열거 형 상수로 평가되는식이 필요합니다 ... 정수 상수 식에는 ... const 변수가 포함될 수 있습니다.

또한 nullchar섹션 4.10.1에 따라 암시 적으로 포인터로 변환 될 수 있도록 0으로 평가됩니다.

4.10 포인터 변환 [conv.ptr]

1 An integral constant expression (expr.const) rvalue of integer type that evaluates to zero (called a null pointer constant) can be con- verted to a pointer type.

Perhaps an intuitive reason "why" this might be allowed (just off the top of my head) is that pointer width isn't specified, and so conversion from any size integral constant expression to a null pointer is allowed.


Updated with the relevant parts of the (newer) C++03 standard... According to §5.19.1...

An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type template parameters of integral or enumeration types, and sizeof expressions.

Then, we look to §4.10.1...

A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type. Two null pointer values of the same type shall compare equal.


It compiles for the very same reason this compiles

const char *p = 0; // OK

const int i = 0;
double *q = i; // OK

const short s = 0;
long *r = s; // OK

Expressions on the right have type int and short, while the object being initialized is a pointer. Does this surprise you?

In C++ language (as well as in C) integral constant expressions (ICEs) with value 0 have special status (although ICEs are defined differently in C and C++). They qualify as null-pointer constants. When they are used in pointer contexts, they are implicitly converted to null pointers of the appropriate type.

Type char is an integral type, not much different from int in this context, so a const char object initialized by 0 is also a null-pointer constant in C++ (but not in C).

BTW, type bool in C++ is also an integral type, which means that a const bool object initialized by false is also a null-pointer constant

const bool b = false;
float *t = b; // OK

A later defect report against C++11 has changed the definition of null-pointer constant. After the correction, null pointer constant can only be "an integer literal with value zero or a prvalue of type std::nullptr_t". The above pointer initializations are no longer well-formed in C++11 after the correction.


It is not ignoring the data type. It's not a bug. It's taking advantage of the const you put in there and seeing that its value is actually an integer 0 (char is an integer type).

Integer 0 is a valid (by definition) null pointer constant, which can be converted to a pointer type (becomes the null pointer).

The reasons why you'd want the null pointer is to have some pointer value which "points to nowhere" and can be checkable (i.e. you can compare a null pointer to an integer 0, and you will get true in return).

If you drop the const, you will get an error. If you put double in there (as with many other non integer types; I guess the exceptions are only types that can be converted to const char* [through overloading of the conversion operators]), you will get an error (even w/o the const). And so forth.

The whole thing is that, in this case, your implementation sees that you're returning a null ptr constant; which you can convert to a pointer type.


It seems that a lot of the real answer to this question has ended up in the comments. To summarize:

  • The C++ standard allows const variables of integral type to be considered "integral constant expressions." Why? Quite possibly to bypass the issue that C only allows macros and enums to hold the place of integral constant expression.

  • Going (at least) as far back as C89, an integral constant expression with value 0 is implicitly convertible to (any type of) null pointer. And this is used often in C code, where NULL is quite often #define'd as (void*)0.

  • Going back to K&R, the literal value 0 has been used to represent null pointers. This convention is used all over the place, with such code as:

    if ((ptr=malloc(...)) {...} else {/* error */}
    

there is a auto cast. if you well run this program:

#include <stdio.h>
const char* c_str()
{
    static const char nullchar = '\0';
    return nullchar;
}

int main()
{
    printf("%d" , sizeof(c_str()));
    return 0;
}

the out-put well be 4 on my computer -> the size of a pointer.

the compiler auto casts. notice, at least gcc gives a warning (i don't know about VS)


I think it might be the fact the null character is common between the types. What you are doing is setting a null pointer when you return the null character. This would fail if any other character was used because you are not passing the address of the character to the pointer, but the value of the character. Null is a valid pointer and character value so a null character can be set as pointer.

In short, null can be used by any type to set an empty value, regardless to if it is an array, a pointer, or a variable.

참고URL : https://stackoverflow.com/questions/12024706/why-is-c-allowing-me-to-assign-a-const-char-to-a-const-char

반응형