다음 코드는 문제없이 컴파일됩니다.
int main() {
printf("Hi" "Bye");
}
그러나 이것은 컴파일되지 않습니다.
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
그 이유는 무엇입니까?
다음 코드는 문제없이 컴파일됩니다.
int main() {
printf("Hi" "Bye");
}
그러나 이것은 컴파일되지 않습니다.
int main() {
int test = 0;
printf("Hi" (test ? "Bye" : "Goodbye"));
}
그 이유는 무엇입니까?
"Hi"
와 "Bye"
있는 문자열 리터럴 이 아닌 문자열 은 C 표준 라이브러리에서 사용 된 바와 같이. 로 문자열 리터럴 , 컴파일러는 연결할 것입니다 "H\0i" "B\0ye"
. sprintf(buf,"%s%s", "H\0i" "B\0ye");
a (some_condition ? + : - ) b
printf("Hi" ("Bye"));
, 그것은 삼항 연산자를 필요로하지 않습니다 - 작동하지 않습니다 괄호만으로도 충분합니다 ( printf("Hi" test ? "Bye" : "Goodbye")
컴파일되지는 않지만). 문자열 리터럴 뒤에 올 수있는 토큰의 수는 제한되어 있습니다. 쉼표 ,
, 여는 대괄호 [
, 닫는 대괄호 ]
( 1["abc"]
— 및 예, 끔찍함), 닫는 대괄호 )
, 닫는 중괄호 }
(이니셜 라이저 또는 유사한 컨텍스트에서) 및 세미콜론 ;
은 합법적입니다 (및 다른 문자열 리터럴). 다른 사람이 있는지 잘 모르겠습니다.
답변:
C 표준 (5.1.1.2 번역 단계)에 따름
1 번역의 구문 규칙 중 우선 순위는 다음 단계로 지정됩니다 .6)
- 인접한 문자열 리터럴 토큰이 연결됩니다.
그리고 그 후에야
- 토큰을 구분하는 공백 문자는 더 이상 중요하지 않습니다. 각 전처리 토큰은 토큰으로 변환됩니다. 결과 토큰은 구문 및 의미 론적으로 분석되고 번역 단위로 번역됩니다 .
이 건설에서
"Hi" (test ? "Bye" : "Goodbye")
인접한 문자열 리터럴 토큰이 없습니다. 따라서이 구성은 유효하지 않습니다.
(test ? "Bye" : "Goodbye")
문자열 리터럴 중 하나를 평가 해 본질적으로 만들기 "Hi" "Bye"
나 "Hi 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
strcat
. 대상 배열은 문자를받을 수있을만큼 길어야하며 s2
이미 존재하는 문자 뒤에 null 종결자가 있어야합니다.
문자열 리터럴 연결은 컴파일 타임에 전처리기에 의해 수행됩니다. 이 연결이 다음의 값을 인식 할 방법이 없습니다.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*
. 인접한 달리 문자열 리터럴 , 인접한 문자 포인터는 단순히 구문 오류가 있습니다.
static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
않아야 static const char *char_ptr_1 = "HiBye";
합니까?
static const char *char_ptr_1 = "HiBye";
컴파일러 를 작성할 때 라인을로 변환 static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
하므로 "문자열처럼"작성해서는 안됩니다. 답변에서 말했듯이 문자열은 문자 배열로 컴파일되며 가장 "원시"형식의 문자 배열을 할당하는 경우 쉼표로 구분 된 문자 목록을 사용합니다. 다음과 같이됩니다.static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
static const char str[] = {'t', 'e', 's', 't', '\0'};
과 동일 static const char str[] = "test";
, static const char* ptr = "test";
있다 없다 와 같은 static const char* ptr = {'t', 'e', 's', 't', '\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
형식 지정자가 필요 하지 않습니다 . 컴파일 타임에 연결 만 수행 된 경우 (그렇지 않은 경우) OP의 printf 사용이 유효합니다.
printf()
에 형식 태그가 필요한 것처럼 보이게 할 것입니다. 이것은 절대적으로 사실이 아닙니다. 수정되었습니다!
질문에 답하기 위해 printf의 정의로 이동합니다. printf 함수는 const char * 를 인수로 예상 합니다. "Hi"와 같은 문자열 리터럴은 const char *입니다. 그러나 다음 (test)? "str1" : "str2"
과 같은 표현식은 const char *가 아닙니다. 왜냐하면 그러한 표현식의 결과는 런타임에서만 발견되고 따라서 컴파일 시간에 결정되지 않기 때문에 컴파일러가 불평하게 만드는 사실입니다. 반면에 이것은 완벽하게 잘 작동합니다.printf("hi %s", test? "yes":"no")
(test)? "str1" : "str2"
은 NOT a const char*
... 물론 그렇습니다! 상수 표현식은 아니지만 유형 은 const char *
입니다. 작성하는 것이 완벽 할 것 printf(test ? "hi " "yes" : "hi " "no")
입니다. 영업 이익의 문제와는 아무 상관이없는 printf
, "Hi" (test ? "Bye" : "Goodbye")
아무리 표현의 맥락이 무엇인지 구문 오류입니다를.
printf 함수의 매개 변수 목록이 다음과 같으므로 컴파일되지 않습니다.
(const char *format, ...)
과
("Hi" (test ? "Bye" : "Goodbye"))
매개 변수 목록에 맞지 않습니다.
gcc는 다음과 같이 상상하여 이해하려고합니다.
(test ? "Bye" : "Goodbye")
매개 변수 목록이고 "Hi"가 함수가 아니라고 불평합니다.
printf()
인수 목록 과 일치하지 않는다는 것이 맞습니다 . 그러나 표현식이 printf()
인수 목록 에서만 유효하지 않기 때문 입니다. 즉, 문제에 대해 너무 전문적인 이유를 선택했습니다. 일반적인 문제는 "Hi" (
C에서 유효하지 않다는 것입니다 printf()
. 이 답변을 삭제하기 전에이 답변을 삭제하는 것이 좋습니다.