int a [] = {1,2,}; 이상한 쉼표가 허용되었습니다. 특별한 이유가 있습니까?
어쩌면 나는이 행성에서 왔지만 다음과 같은 구문 오류가 있어야합니다.
int a[] = {1,2,}; //extra comma in the end
그러나 그렇지 않습니다. 나는이 코드는 비주얼 스튜디오에서 컴파일 할 때 놀랐습니다,하지만 난 표준을 확인하고 그것이 지금까지 C ++ 규칙에 관한 한, 그래서 같이 MSVC 컴파일러를 신뢰하지 배운 되는 표준에 의해 허용뿐만 아니라. 나를 믿지 않으면 문법 규칙에 8.5.1을 볼 수 있습니다.
이것이 왜 허용됩니까? 이것은 어리석은 쓸모없는 질문 일지 모르지만 왜 내가 묻는 지 이해하고 싶습니다. 그것이 일반적인 문법 규칙의 하위 사례라면 이해할 것입니다. 초기화 목록의 끝에 중복 쉼표를 허용하지 않기 위해 일반적인 문법을 더 어렵게하지 않기로 결정했습니다. 그러나 추가 쉼표는 명시 적으로 허용됩니다. 예를 들면, (함수 취하는 함수 호출 인수리스트의 끝에서 중복 쉼표가 허용되지 않는 ...
) 정상 .
이 중복 쉼표가 명시 적으로 허용되는 특별한 이유가 있습니까?
소스 코드를 쉽게 생성하고 나중에 쉽게 확장 할 수있는 코드를 작성할 수 있습니다. 추가 항목을 추가하는 데 필요한 사항을 고려하십시오.
int a[] = {
1,
2,
3
};
... 기존 라인에 쉼표를 추가해야 하고 새 줄을 추가합니다. 세 개 뒤에 이미 쉼표가있는 경우와 비교 하면됩니다. 마찬가지로 줄을 제거하려면 마지막 줄인지 걱정하지 않고 쉼표를 사용하지 않고 줄을 다시 정렬 할 수 있습니다. 기본적으로 그것은 라인을 처리하는 방식에 균일 성이 있음을 의미합니다.
이제 코드 생성에 대해 생각해보십시오. (의사 코드)와 같은 것 :
output("int a[] = {");
for (int i = 0; i < items.length; i++) {
output("%s, ", items[i]);
}
output("};");
현재 쓰고있는 항목이 처음인지 마지막인지에 대해 걱정할 필요가 없습니다. 훨씬 간단합니다.
다음과 같이하면 유용합니다.
int a[] = {
1,
2,
3, //You can delete this line and it's still valid
};
개발자가 사용하기 편하다고 생각합니다.
int a[] = {
1,
2,
2,
2,
2,
2, /*line I could comment out easily without having to remove the previous comma*/
}
또한 어떤 이유로 든 코드를 생성하는 도구가 있다면; 이 도구는 초기화의 마지막 항목인지 여부를 신경 쓸 필요가 없습니다.
나는 항상 추가 요소를 쉽게 추가 할 수 있다고 가정했습니다.
int a[] = {
5,
6,
};
단순히 다음과 같이됩니다.
int a[] = {
5,
6,
7,
};
나중에.
줄을 쉽게 추가 / 제거 / 생성하는 것에 대해 모든 사람들이 말하는 것은 정확하지만이 구문이 빛나는 곳은 소스 파일을 병합 할 때입니다. 이 배열이 있다고 상상해보십시오.
int ints[] = {
3,
9
};
이 코드를 리포지토리에 체크인했다고 가정합니다.
그런 다음 친구가 편집하여 끝 부분에 추가합니다.
int ints[] = {
3,
9,
12
};
그리고 동시에 편집하여 시작 부분에 추가하십시오.
int ints[] = {
1,
3,
9
};
의미 상 이러한 종류의 작업 (처음에 추가, 끝에 추가)은 완전히 안전하게 병합되어야하며 버전 관리 소프트웨어 (가 급히 git)가 자동 병합 될 수 있어야합니다. 슬프게도, 9 이후 버전에 쉼표가없고 친구의 버전이 있기 때문에 그렇지 않습니다. 반면, 원본 버전의 끝에 9가 있으면 자동으로 병합 된 것입니다.
따라서 제 경험에 따르면 목록이 여러 줄에 걸쳐 있으면 후행 쉼표를 사용하고 목록이 한 줄에 있으면 사용하지 마십시오.
후행 쉼표는 이전 버전과의 호환성을 위해 허용됩니다. 주로 자동 생성되는 많은 기존 코드가 있으며, 뒤에 쉼표가 있습니다. 마지막에 특별한 조건없이 루프를 작성하는 것이 더 쉽습니다. 예 :
for_each(my_inits.begin(), my_inits.end(),
[](const std::string& value) { std::cout << value << ",\n"; });
프로그래머에게는 실제로 이점이 없습니다.
추신 :이 방법으로 코드를 자동 생성하는 것이 더 쉽지만 실제로는 항상 쉼표를 넣지 않도록주의를 기울였으며 노력이 최소화되고 가독성이 향상되었으며 더 중요합니다. 코드를 한 번 작성하면 여러 번 읽습니다.
내가 아는 한 이것이 허용되는 이유 중 하나는 자동으로 코드를 생성하는 것이 간단해야하기 때문입니다. 마지막 요소에 대한 특별한 처리가 필요하지 않습니다.
배열이나 열거를 뱉어내는 코드 생성기를 더 쉽게 만듭니다.
상상해보십시오.
std::cout << "enum Items {\n";
for(Items::iterator i(items.begin()), j(items.end); i != j; ++i)
std::cout << *i << ",\n";
std::cout << "};\n";
즉, 후행 쉼표를 뱉지 않기 위해 첫 번째 또는 마지막 항목을 특별하게 처리 할 필요가 없습니다.
예를 들어 코드 생성기가 Python으로 작성된 경우 str.join()
함수 를 사용하여 후행 쉼표를 뱉지 않는 것이 쉽습니다 .
print("enum Items {")
print(",\n".join(items))
print("}")
다른 답변에서 언급되지 않은 하나의 사용 사례, 가장 좋아하는 매크로가 있습니다.
int a [] = {
#ifdef A
1, //this can be last if B and C is undefined
#endif
#ifdef B
2,
#endif
#ifdef C
3,
#endif
};
마지막으로 처리 ,
할 매크로를 추가하는 것은 큰 고통입니다. 이 작은 구문 변경으로 관리하기는 쉽지 않습니다. 그리고 이것은 매우 제한된 전임자보다 Turing complete langue에서 일반적으로 수행하기가 훨씬 쉽기 때문에 기계 생성 코드보다 더 중요합니다.
이 시간이 지나면 아무도 Annotated C ++ Reference Manual ( ARM ) 을 인용하지 않았으며 [dcl.init] 에 대해 다음 과 같이 강조합니다.
초기화에 대한 표기법이 너무 많지만 각각 특정 스타일의 용도로 사용되는 것 같습니다. = {initializer_list가 옵트} 표기법 C로부터 상속 된 데이터 구조 및 배열의 초기화에 잘 작용한다. [...]
비록 ARM 이 쓰여진 이래로 문법은 진화했지만 기원은 남아있다.
C99의 이론적 근거 로 가서 왜 이것이 C에서 허용되는지 알 수 있습니다 .
K & R은 이니셜 라이저 목록의 끝에 이니셜 라이저에서 후행 쉼표를 허용합니다. 표준은 이니셜 라이저 목록에서 멤버를 추가하거나 삭제할 때 유연성을 제공하고 이러한 목록의 기계 생성을 단순화하므로이 구문을 유지했습니다 .
실제로는 허용되지 않는 언어는 Javascript 뿐이며 수많은 문제를 일으 킵니다. 예를 들어 배열의 중간에서 행을 복사하여 붙여 넣은 다음 끝에 붙여넣고 쉼표를 제거하지 않으면 IE 방문자가 사이트를 완전히 사용할 수 없게됩니다.
* 이론적으로는 허용되지만 Internet Explorer는 표준을 따르지 않고 오류로 취급합니다.
기계 분석, 예를 들어 구문 분석 및 코드 생성이 더 쉽습니다. 일관성을 통한 수정, 주석 달기 및 시각적 우아함과 같은 인간에게는 더 쉽습니다.
C라고 가정하면 다음을 작성 하시겠습니까?
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
puts("Line 1");
puts("Line 2");
puts("Line 3");
return EXIT_SUCCESS
}
아닙니다. 최종 진술은 오류 일뿐만 아니라 일관성이 없기 때문입니다. 그렇다면 왜 컬렉션과 동일합니까? 마지막 세미콜론과 쉼표를 생략 할 수있는 언어로도 커뮤니티는 보통 그것을 좋아하지 않습니다. 예를 들어, Perl 커뮤니티는 세미콜론을 생략하는 것을 좋아하지 않는 것 같습니다. 그들은 쉼표에도 적용합니다.
여러 줄의 코드 블록에 대해 세미콜론을 생략하지 않는 것과 같은 이유로 여러 줄 컬렉션에서 쉼표를 생략하지 마십시오. 언어가 허락하더라도 그렇게하지 않겠어요? 권리?
그 이유는 사소한 것입니다. 라인을 쉽게 추가 / 제거 할 수 있습니다.
다음 코드를 상상해보십시오.
int a[] = {
1,
2,
//3, // - not needed any more
};
이제 후행 쉼표를 추가하거나 제거하지 않고도 목록에 항목을 쉽게 추가 / 제거 할 수 있습니다.
다른 답변과 달리 목록을 생성하는 것이 쉬운 이유라고 생각하지 않습니다. 결국 코드가 마지막 (또는 첫 번째) 행을 특수하게 처리하는 것은 사소한 일입니다. 코드 생성기는 한 번 작성되어 여러 번 사용됩니다.
모든 줄이 같은 형식을 따를 수 있습니다. 첫째, 이것은 새로운 행을 쉽게 추가하고 버전 제어 시스템이 변경을 의미있게 추적하도록하고 코드를보다 쉽게 분석 할 수있게합니다. 나는 기술적 인 이유를 생각할 수 없다.
이를 통해 긴 목록에서 요소를 이동하여 발생하는 실수로부터 보호 할 수 있습니다.
예를 들어, 이와 같은 코드가 있다고 가정 해 봅시다.
#include <iostream>
#include <string>
#include <cstddef>
#define ARRAY_SIZE(array) (sizeof(array) / sizeof *(array))
int main() {
std::string messages[] = {
"Stack Overflow",
"Super User",
"Server Fault"
};
size_t i;
for (i = 0; i < ARRAY_SIZE(messages); i++) {
std::cout << messages[i] << std::endl;
}
}
Stack Exchange 사이트의 원래 3 부작을 보여 주므로 훌륭합니다.
Stack Overflow
Super User
Server Fault
그러나 한 가지 문제가 있습니다. 보시다시피이 웹 사이트의 바닥 글에는 슈퍼 유저 전의 서버 오류가 표시됩니다. 다른 사람이 알아 차리기 전에 수정하십시오.
#include <iostream>
#include <string>
#include <cstddef>
#define ARRAY_SIZE(array) (sizeof(array) / sizeof *(array))
int main() {
std::string messages[] = {
"Stack Overflow",
"Server Fault"
"Super User",
};
size_t i;
for (i = 0; i < ARRAY_SIZE(messages); i++) {
std::cout << messages[i] << std::endl;
}
}
결국, 라인을 움직이는 것은 그렇게 어려울 수 없었습니까?
Stack Overflow
Server FaultSuper User
"Server FaultSuper User"라는 웹 사이트는 없지만 컴파일러에서 웹 사이트가 있다고 주장합니다. 이제 문제는 C에 문자열 연결 기능이있어서 큰 따옴표로 묶인 두 개의 문자열을 작성하고 아무 것도 사용하지 않고 문자열을 연결할 수 있다는 것입니다 -
.
이제 원래 배열에 쓸모없는 쉼표가 있으면 어떻게 될까요? 글쎄, 그 줄은 움직일 것이지만 그러한 버그는 발생하지 않았을 것입니다. 콤마처럼 작은 것을 놓치기 쉽습니다. 모든 배열 요소 뒤에 쉼표를 넣는 것을 기억하면 그러한 버그는 발생할 수 없습니다. 당신은 당신이 쉼표가 문제의 원인을 찾을 것까지, 무언가를 디버깅 네 시간 낭비하고 싶지 않을 것이다 .
많은 것들과 마찬가지로, 배열 이니셜 라이저의 후행 쉼표는 C ++에서 C로 상속 된 것 중 하나입니다 (그리고 영원히 지원해야 할 것입니다). 여기 에 놓인 것과는 완전히 다른 견해 는 "Deep C secrets" 책에 언급되어 있습니다.
두 개 이상의 "쉼표 역설"이있는 예를 보자.
char *available_resources[] = {
"color monitor" ,
"big disk" ,
"Cray" /* whoa! no comma! */
"on-line drawing routines",
"mouse" ,
"keyboard" ,
"power cables" , /* and what's this extra comma? */
};
우리는 읽고 :
... 최종 이니셜 라이저 뒤의 쉼표는 오타가 아니며 원주민 C에서 가져온 구문의 실수입니다 . 그것의 존재 또는 부재는 허용되지만 의미 는 없습니다 . ANSI C의 이론적 근거에서 주장하는 정당화는 자동화 된 C 생성을보다 쉽게 만든다는 것입니다. 열거 형 선언이나 단일 선언의 여러 변수 선언자와 같이 모든 쉼표로 구분 된 목록에서 후행 쉼표가 허용 된 경우 더 신뢰할 수 있습니다 . 그들은 아닙니다.
... 나에게 이것은 더 의미가 있습니다.
코드 생성 및 편집 용이성 외에도 구문 분석기를 구현하려는 경우이 유형의 문법이 더 단순하고 구현하기 쉽습니다. C #은 enum
정의의 항목과 같이 쉼표로 구분 된 항목 목록이있는 여러 위치에서이 규칙을 따릅니다 .
한 줄만 추가하면되며 마지막 항목을 특수한 경우처럼 추가 할 필요가 없으므로 코드 생성이 더 쉬워집니다. 매크로를 사용하여 코드를 생성 할 때 특히 그렇습니다. 언어에서 매크로에 대한 필요성을 없애려고 노력했지만 매크로가 사용 가능 해짐에 따라 많은 언어가 함께 발전했습니다. 추가 쉼표를 사용하면 다음과 같은 매크로를 정의하고 사용할 수 있습니다.
#define LIST_BEGIN int a[] = {
#define LIST_ENTRY(x) x,
#define LIST_END };
용법:
LIST_BEGIN
LIST_ENTRY(1)
LIST_ENTRY(2)
LIST_END
매우 간단한 예이지만, 종종이 패턴은 디스패치, 메시지, 이벤트 또는 번역 맵 및 테이블과 같은 것을 정의하기 위해 매크로에서 사용됩니다. 마지막에 쉼표를 사용할 수없는 경우 다음과 같은 특별 항목이 필요합니다.
#define LIST_LAST_ENTRY(x) x
사용하기가 매우 어색합니다.
지정된 길이가없는 배열을 사용하면 VC ++ 6.0에서 자동으로 길이를 식별 할 수 있으므로 "int a [] = {1,2,};"를 사용하면 a의 길이는 3이지만 마지막 길이는 ' t 초기화되었습니다, 당신은 "cout <
참고 URL : https://stackoverflow.com/questions/7043372/int-a-1-2-weird-comma-allowed-any-particular-reason
'Programming' 카테고리의 다른 글
파이썬 문자열에서 어떻게 백분율 (%)을 이스케이프 처리 할 수 있습니까? (0) | 2020.03.06 |
---|---|
git remote prune, git prune, git fetch --prune 등의 차이점은 무엇입니까? (0) | 2020.03.06 |
PHP에서 배열 요소의 키를 어떻게 변경합니까? (0) | 2020.03.06 |
C #에서 현재 사용자의 데스크톱 경로를 얻는 방법은 무엇입니까? (0) | 2020.03.06 |
내 안드로이드 응용 프로그램을 세로 모드로만 실행하고 싶습니까? (0) | 2020.03.06 |