[c] 조건부 연산자를 사용할 때 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));
}

이것이 컴파일되지 않는 이유가 명확해야합니다. 삼항 연산자 ?는 “문자열”이 더 이상 존재하지 않고 포인터에 char의해 참조되는 단순한 배열 로만 존재하는 컴파일 타임이 아닌 런타임에 평가됩니다 char*. 인접한 달리 문자열 리터럴 , 인접한 문자 포인터는 단순히 구문 오류가 있습니다.


답변

두 분기 모두 런타임에 선택할 컴파일 시간 문자열 상수를 생성하도록하려면 매크로가 필요합니다.

#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;
}


답변

그 이유는 무엇입니까?

삼항 연산자를 사용하는 코드는 조건부로 두 문자열 리터럴 중에서 선택합니다. 알려 지거나 알려지지 않은 조건에 관계없이 컴파일 타임에 평가할 수 없으므로 컴파일 할 수 없습니다. 이 문장조차 printf("Hi" (1 ? "Bye" : "Goodbye"));컴파일되지 않습니다. 그 이유는 위의 답변에 자세히 설명되어 있습니다. 의 또 다른 가능성 컴파일 할 삼항 연산자가 유효 사용하여 이러한 문을은 , 또한 포함 할 형식의 태그 와 같은 포맷 된 삼항 연산자 문의 결과 추가 인수 로를 printf. 그럼에도 불구하고, printf()출력물은 이러한 문자열을 런타임시 에만 “연결된”느낌을줍니다 .

#include <stdio.h>

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


답변

printf("Hi" "Bye"); 당신은 컴파일러가 하나의 배열로 만들 수있는 문자의 두 개의 연속적인 배열을 가지고있다.

에서는 printf("Hi" (test ? "Bye" : "Goodbye"));당신이 가진 하나 개의 배열 (배열의 첫 번째 요소에 대한 포인터로 변환) 문자 포인터 하였다. 컴파일러 는 배열과 포인터를 병합 할 수 없습니다 .