Programing

삼항 연산자를 사용할 때 C에서 문자열 연결을 허용하지 않는 이유는 무엇입니까?

crosscheck 2020. 8. 26. 07:23
반응형

삼항 연산자를 사용할 때 C에서 문자열 연결을 허용하지 않는 이유는 무엇입니까?


다음 코드는 문제없이 컴파일됩니다.

int main() {
    printf("Hi" "Bye");
}

그러나 이것은 컴파일되지 않습니다.

int main() {
    int test = 0;
    printf("Hi" (test ? "Bye" : "Goodbye"));
}

그 이유는 무엇입니까?


C 표준 (5.1.1.2 번역 단계)에 따름

1 번역의 구문 규칙 중 우선 순위는 다음 단계로 지정됩니다 .6)

  1. 인접한 문자열 리터럴 토큰이 연결됩니다.

그리고 그 후에야

  1. 토큰을 구분하는 공백 문자는 더 이상 중요하지 않습니다. 각 전처리 토큰은 토큰으로 변환됩니다. 결과 토큰은 구문 및 의미 론적으로 분석되고 번역 단위로 번역됩니다 .

이 건설에서

"Hi" (test ? "Bye" : "Goodbye")

인접한 문자열 리터럴 토큰이 없습니다. 따라서이 구성은 유효하지 않습니다.


C11 표준, §5.1.1.2 장에 따라 인접한 문자열 리터럴 연결 :

인접한 문자열 리터럴 토큰이 연결됩니다.

번역 단계 에서 발생합니다 . 반면에 :

printf("Hi" (test ? "Bye" : "Goodbye"));

런타임에 평가되는 조건부 연산자가 포함됩니다 . 따라서 컴파일 타임에 번역 단계 중에 인접한 문자열 리터럴이 없으므로 연결이 불가능합니다. 구문이 잘못되어 컴파일러에서보고합니다.


이유 부분 에 대해 좀 더 자세히 설명하기 위해 전처리 단계에서 인접한 문자열 리터럴이 연결되어 단일 문자열 리터럴 (토큰)로 표시됩니다. 그에 따라 스토리지가 할당되고 연결된 문자열 리터럴은 단일 엔티티 (하나의 문자열 리터럴) 로 간주됩니다 .

반면에 런타임 연결의 경우 대상에 연결된 문자열 리터럴 을 보유하기에 충분한 메모리가 있어야합니다. 그렇지 않으면 예상되는 연결된 출력에 액세스 할 수 없습니다. 이제 문자열 리터럴의 경우 에는 이미 컴파일 타임에 메모리가 할당 되었으며 더 이상 들어오는 입력 맞게 확장 하거나 원본 콘텐츠에 추가 할 수 없습니다 . 즉, 연결된 결과를 단일 문자열 리터럴 로 액세스 (표시) 할 수있는 방법이 없습니다 . 따라서이 구조는 본질적으로 잘못되었습니다.

참고로, 런타임 문자열 ( 리터럴 아님) 연결의 경우 strcat()문자열 을 연결 하는 라이브러리 함수 있습니다 . 설명은 다음을 언급합니다.

char *strcat(char * restrict s1,const char * restrict s2);

strcat()함수는에서 가리키는 문자열 s2의 끝에 (종료 널 문자 포함)가 가리키는 문자열의 사본을에서 가리키는 문자열 의 끝에 추가 합니다s1 . 의 초기 문자 s2는 끝에있는 null 문자를 덮어 씁니다 s1. [...]

그래서 우리는 문자열 리터럴s1 이 아니라 문자열 임을 알 수 있습니다 . 그러나의 내용은 어떤 식 으로든 변경되지 않으므로 문자열 리터럴 일 수 있습니다.s2


문자열 리터럴 연결은 컴파일 타임에 전처리기에 의해 수행됩니다. 이 연결 test은 프로그램이 실제로 실행될 때까지 알려지지 않은 의 값을 인식 할 방법 이 없습니다. 따라서 이러한 문자열 리터럴은 연결할 수 없습니다.

일반적인 경우는 컴파일 타임에 알려진 값에 대해 이와 같은 구성이 없기 때문에 C 표준은 자동 연결 기능을 가장 기본적인 경우로 제한하도록 설계되었습니다. 리터럴이 문자 그대로 서로 나란히있을 때 .

그러나이 제한을 그런 식으로 표현하지 않았거나 제한이 다르게 구성되었다하더라도 연결을 런타임 프로세스로 만들지 않고서는 예제를 실현할 수 없습니다. 그리고이를 위해 strcat.


C에는 string유형 이 없기 때문 입니다. 문자열 리터럴은 포인터로 char참조되는 배열 로 컴파일됩니다 char*.

C는 첫 번째 예제에서와 같이 인접 리터럴컴파일 타임에 결합 할 수 있도록 합니다. C 컴파일러 자체에는 문자열에 대한 지식이 있습니다. 그러나이 정보는 런타임에 존재하지 않으므로 연결이 발생할 수 없습니다.

컴파일 과정에서 첫 번째 예제는 다음과 같이 "번역"됩니다.

int main() {
    static const char char_ptr_1[] = {'H', 'i', 'B', 'y', 'e', '\0'};
    printf(char_ptr_1);
}

프로그램이 실행되기 전에 컴파일러에서 두 문자열을 단일 정적 배열로 결합하는 방법에 유의하십시오.

그러나 두 번째 예는 다음과 같이 "번역"됩니다.

int main() {
    static const char char_ptr_1[] = {'H', 'i', '\0'};
    static const char char_ptr_2[] = {'B', 'y', 'e', '\0'};
    static const char char_ptr_3[] = {'G', 'o', 'o', 'd', 'b', 'y', 'e', '\0'};
    int test = 0;
    printf(char_ptr_1 (test ? char_ptr_2 : char_ptr_3));
}

It should be clear why this does not compile. The ternary operator ? is evaluated at runtime, not compile-time, when the "strings" no longer exist as such, but only as simple char arrays, referenced by char* pointers. Unlike adjacent string literals, adjacent char pointers are simply a syntax error.


If you really want to have both branches produce compile-time string constants to be chosen at runtime, you'll need a macro.

#include <stdio.h>
#define ccat(s, t, a, b) ((t)?(s a):(s b))

int
main ( int argc, char **argv){
  printf("%s\n", ccat("hello ", argc > 2 , "y'all", "you"));
  return 0;
}

What is the reason for that?

Your code using ternary operator conditionally chooses between two string literals. No matter condition known or unknown, this can't be evaluated at compile time, so it can't compile. Even this statement printf("Hi" (1 ? "Bye" : "Goodbye")); wouldn't compile. The reason is in depth explained in the answers above. Another possibility of making such a statement using ternary operator valid to compile, would also involve a format tag and the result of the ternary operator statement formatted as additional argument to printf. Even then, printf() printout would give an impression of "having concatenated" those strings only at, and as early as runtime.

#include <stdio.h>

int main() {
    int test = 0;
    printf("Hi %s\n", (test ? "Bye" : "Goodbye")); //specify format and print as result
}

In printf("Hi" "Bye"); you have two consecutive arrays of char which the compiler can make into a single array.

In printf("Hi" (test ? "Bye" : "Goodbye")); you have one array followed by a pointer to char (an array converted to a pointer to its first element). The compiler cannot merge an array and a pointer.


To answer the question - I would go to the definition of printf. The function printf expects const char* as argument. Any string literal such as "Hi" is a const char*; however an expression such as (test)? "str1" : "str2" is NOT a const char* because the result of such expression is found only at run-time and hence is indeterminate at compile time, a fact which duly causes the compiler to complain. On the other hand - this works perfectly well printf("hi %s", test? "yes":"no")


This does not compile because the parameter list for the printf function is

(const char *format, ...)

and

("Hi" (test ? "Bye" : "Goodbye"))

does not fit the parameter list.

gcc tries to make sense of it by imagining that

(test ? "Bye" : "Goodbye")

is a parameter list, and complains that "Hi" is not a function.

참고URL : https://stackoverflow.com/questions/37259472/why-does-c-not-allow-concatenating-strings-when-using-the-ternary-operator

반응형