프로그래밍 언어, 특히 C가 왜 중괄호가 아닌 중괄호를 사용합니까?


96

"C 스타일 언어"의 정의는 실질적으로 "중괄호 ( {}) 사용"으로 단순화 할 수 있습니다 . 왜 우리는 그 특정 문자를 사용합니까 (그리고 []적어도 미국 키보드에서는 Shift 키가 필요하지 않은 것과 같은 더 합리적인 것이 아닌 이유는 무엇 입니까)?

이러한 중괄호에서 비롯된 프로그래머 생산성에 실질적인 이점이 있습니까, 아니면 새로운 언어 디자이너가 대안을 찾아야합니까 (예 : Python의 전문가)?

Wikipedia 는 C 중괄호를 사용 한다고 말하지만 이유는 아닙니다. C 기반 프로그래밍 언어 목록 에 대한 Wikipedia 기사의 내용에 따르면이 구문 요소는 다소 특별합니다.

일반적으로 C 계열 언어 는 C와 같은 블록 구문 (블록 을 시작하고 끝내는 중괄호 포함)을 사용하는 언어입니다 .


35
이것에 대답 할 수있는 유일한 사람은 Dennis Ritchie이고 그는 죽었습니다. 합리적인 추측은 []이 이미 배열에 사용되었다는 것입니다.
Dirk Holsopple

2
@DirkHolsopple 그래서 그는 추론을 남기지 않았다? 드랏. 또한 : 내가 정말로 궁금한 것에 대한 두 개의 downvotes? 고마워 ..
SomeKittens

1
이 메타 질문 에서이 질문 대한 토론을 계속하십시오 .
Thomas Owens

2
이 게시물의 잠금을 해제했습니다. 질문에 대한 의견과 메타 질문 에 대한 적절성에 대한 토론을 유지하십시오 .
Thomas Owens

5
또한 중괄호는 수학에서 집합 표기법으로 사용되므로 구조체, 배열 등과 같은 "set"선언 같은 것보다는 배열 요소 액세스에 사용하기가 다소 어색하다는 사실과 관련이 있습니다. 파이썬과 같은 현대 언어조차도 중괄호를 사용하여 집합과 사전을 선언합니다. 그렇다면 C는 왜 중괄호를 사용하여 범위를 선언 했습니까? 아마도 설계자는 BEGIN / END와 같은 알려진 대안을 좋아하지 않았기 때문에 과부하 된 어레이 액세스 표기법 ([])은 세트 표기법보다 심미적으로 덜 안전하다고 간주되었습니다.
Charles Salvia

답변:


102

C에 대한 주요 영향 중 두 가지는 Algol 언어 군 (Algol 60 및 Algol 68)과 BCPL (C의 이름을 따름)이었습니다.

BCPL은 최초의 중괄호 프로그래밍 언어였으며, 중괄호는 구문상의 변화에도 불구하고 프로그램 소스 코드 문장을 나타내는 일반적인 수단이되었습니다. 실제로 오늘날 제한된 키보드에서는 소스 프로그램이 {및} 기호 대신 $ (및 $) 시퀀스를 사용하는 경우가 많습니다. C에서 채택되지 않은 BCPL의 한 줄 '//'주석은 C ++로, 나중에 C99로 다시 나타납니다.

에서 http://www.princeton.edu/~achaney/tmve/wiki100k/docs/BCPL.html

BCPL은 몇 가지 혁신을 도입하고 구현하여 이후 언어 디자인에서 매우 일반적인 요소가되었습니다. 따라서 첫 번째 중괄호 프로그래밍 언어 ({}를 블록 구분 기호로 사용하는 언어)였으며 // 인라인 주석을 표시하기 위해 //를 사용한 첫 번째 언어였습니다.

에서 http://progopedia.com/language/bcpl/

BCPL 내에서 중괄호는 종종 보이지만 항상 그런 것은 아닙니다. 이것은 당시 키보드의 한계였습니다. 문자는 $($)사전 식에 해당했다 {}. 이중 음자와 trigraph를는 C로 유지 하였다 (중괄호 교체 상이한 세트 - 비록 ??<하고 ??>).

중괄호 사용은 B (C 이전) 에서 추가로 개선되었습니다 .

에서 B에 대한 사용자의 참조 켄 톰슨 :

/* The following function will print a non-negative number, n, to
  the base b, where 2<=b<=10,  This routine uses the fact that
  in the ASCII character set, the digits 0 to 9 have sequential
  code values.  */

printn(n,b) {
        extern putchar;
        auto a;

        if(a=n/b) /* assignment, not test for equality */
                printn(a, b); /* recursive */
        putchar(n%b + '0');
}

중괄호가 짧은 손을 사용했다 표시가 있습니다 beginend알골 내에는.

CACM에 게시 한 256 자 카드 코드에도이 코드를 포함 시켰다는 사실을 기억합니다. Algol 'begin'및 'end'키워드 대신 사용할 수 있다고 제안했기 때문에 흥미로 웠습니다. 나중에 C 언어에서 어떻게 사용되었는지

에서 http://www.bobbemer.com/BRACES.HTM


대괄호를 사용하면 (질문에서 제안 된 대체품으로) 훨씬 더 발전합니다. 언급 한 바와 같이, Algol 패밀리는 C에 영향을 미쳤다. Algol 60 및 68 (C는 1972 년에 작성되었고 1966 년에 BCPL로 작성 됨) 내에서, 대괄호는 인덱스를 배열 또는 행렬로 지정하는 데 사용되었다.

BEGIN
  FILE F(KIND=REMOTE);
  EBCDIC ARRAY E[0:11];
  REPLACE E BY "HELLO WORLD!";
  WRITE(F, *, E);
END.

프로그래머들은 이미 Algol과 BCPL의 배열을위한 대괄호와 BCPL의 블록을위한 중괄호에 대해 잘 알고 있었기 때문에 다른 언어를 만들 때 이것을 바꾸고 자하는 필요가 거의 없었습니다.


업데이트 된 질문에는 중괄호 사용법에 대한 생산성 부록이 포함되어 있으며 파이썬에 대한 언급이 있습니다. 이 연구를 수행하는 다른 자료들도 있지만 그 대답은 "일화적이고, 당신이 익숙한 것은 가장 생산적인 것"으로 요약됩니다. 프로그래밍에 대한 다양한 기술과 다양한 언어에 대한 친숙 함으로 인해이를 설명하기가 어렵습니다.

다음을 참조하십시오 : 스택 오버플로 Python이 "보다 생산적"이라는 통계 연구가 있습니까?

많은 이점은 사용되는 IDE (또는 부족)에 따라 달라집니다. vi 기반 편집기에서 커서를 일치하는 하나의 열기 / 닫기 위에 놓고를 누르면 %커서가 다른 일치하는 문자로 이동합니다. 이것은 예전에는 C 기반 언어에서 매우 효율적입니다.

더 나은 비교는 사이 의 옵션 {}이며 begin/ end는 오늘의 옵션이었습니다 (가로 공간은 중요했습니다). 많은 Wirth 언어는 a beginand endstyle (위에서 언급 한 Algol, pascal (많은 사람들에게 익숙 함) 및 Modula family)을 기반으로했습니다.

이 특정 언어 기능을 분리하는 것을 찾는 데 어려움이 있습니다. 최선의 방법은 중괄호 언어가 최종 언어보다 훨씬 인기가 있으며 일반적인 구성이라는 것을 보여주는 것입니다. 위의 Bob Bemer 링크에서 언급했듯이 중괄호는 속기처럼 쉽게 프로그래밍 할 수 있도록 사용되었습니다.

에서 파스칼 내 좋아하는 프로그래밍 언어되지 않는 이유

C와 Ratfor 프로그래머는 {와}에 비해 '시작'과 '종료'가 부피가 크다는 것을 알게됩니다.

친숙 함과 선호도-말할 수있는 모든 것입니다.


14
이제 여기에 모든 사람들이 대신 : 작업의 BCPL를 배우고
데니스 Séguret

대한 (1989 년 ISO C 표준에 도입)을 trigraph를 {하고 }있다 ??<??>. 서약 (1995 년 개정에 의해 도입 된)은 <%%>이다. 3 단계는 모든 상황에서 초기 번역 단계로 확장됩니다. Digraphs는 토큰이며 문자열 리터럴, 문자 상수 또는 주석으로 확장되지 않습니다.
Keith Thompson

C에서 이것을 위해 1989 년 이전에 무언가가있었습니다. 모든 EBCDIC 코드 페이지에 중괄호 (또는 대괄호)가있는 것은 아니며 초기 C 컴파일러에는 이에 대한 조항이있었습니다.

@NevilleDNZ BCPL은 1966 년에 중괄호를 사용했습니다. Algol68의 개념은 탐구해야 할 것이지만 BCPL은 Algo68에서 얻지 못했습니다. 삼항 연산자는 내가 관심을 가지고 있으며 Lisp (1958)에서 개념을 빌린 CPL (1963) (BCPL의 전신)로 다시 추적했습니다.

1968 : Algol68은 둥근 괄호 (~)를 시작 ~ 굵은 기호 블록 의 속기로 허용 합니다. 이것을 간단한 기호 라고합니다 ( cf wp : Algol68 Bold symbols ). 이렇게하면 코드 블록을 표현식 처럼 취급 할 수 있습니다 . A68은이 간단한 C의 같은 shorthands ? 삼항 연산자의 : 예를 들어, x:=(c|s1|s2)대신 C의를 x=c?s1|s2. 마찬가지로 이것은 if & case 문 에도 적용됩니다 . ¢는 BTW : 쉘 가지고는 어디 A68은에서입니다 ESAC & Fi를 ¢
NevilleDNZ

24

"Multics" OS에서 광범위하게 사용 된 IBM 2741 터미널이 개발 팀 멤버로 C 언어 작성자 인 Dennis Ritchie를 사용하기[] 때문에 대괄호 는 입력하기가 더 쉽습니다 .

http://upload.wikimedia.org/wikipedia/commons/thumb/9/9f/APL-keybd2.svg/600px-APL-keybd2.svg.png

IBM 2741 레이아웃에서 중괄호 가 없음 을 참고하십시오 !

C에서는 대괄호가 배열과 포인터에 사용되므로 "중괄호"가 사용됩니다 . 언어 설계자가 배열과 포인터가 코드 블록보다 더 중요하고 자주 사용될 것으로 예상한다면 ( 아래의 코딩 스타일의 역사적 맥락에서 합리적으로 가정 하는 것처럼 들리면 ) 중괄호가 "중요하지 않음"을 의미합니다. "구문.

배열의 중요성은 Ritchie 의 C 언어 개발 기사에서 매우 분명합니다 . "C 프로그램에서 포인터가 널리 퍼져있다" 는 명백한 가정도있다 .

... 새로운 언어 는 배열의 의미론에 대해 일관되고 실행 가능한 (비정상적인) 설명을 유지했습니다 ... 클래스의 언어 중에서 C의 가장 특징적인 두 가지 아이디어는 배열과 포인터 사이의 관계입니다. C, 배열의 처리 는 정말 미덕이 있습니다. 포인터와 배열의 관계는 드물지만 배울 수 있습니다. 또한이 언어는 몇 가지 기본 규칙과 규칙만으로 런타임에 길이가 변하는 벡터와 같은 중요한 개념을 설명 할 수있는 상당한 힘 을 보여줍니다 .


C 언어가 생성 된 당시의 역사적 맥락과 코딩 스타일에 대한 추가 이해를 위해서는 "C의 기원은 유닉스의 개발과 밀접한 관련이 있습니다" , 특히 OS를 PDP- 11 "C의 초기 버전의 개발" ( 인용 소스 ). 에 따르면 위키 백과 , "1972 년, 유닉스는 C 프로그래밍 언어로 재 작성했다" .

다양한 유닉스 버전의 소스 코드는 예를 들어 유닉스 트리 사이트 에서 온라인으로 볼 수 있습니다. 거기에 제시된 다양한 버전 중에서 가장 관련성이 높은 것은 1972-06 년의 Second Edition Unix 인 것 같습니다 .

Unix의 두 번째 버전은 Bell Labs의 PDP-11을 위해 Ken Thompson, Dennis Ritchie 등이 개발했습니다. 더 많은 시스템 호출과 더 많은 명령으로 1 판을 확장했습니다. 이 판은 또한 C 언어의 시작을 보았습니다.이 언어는 일부 명령을 작성하는 데 사용되었습니다 ...

Second Edition Unix (V2) 페이지 에서 C 소스 코드를 찾아서 연구 하여 일반적인 코딩 스타일에 대한 아이디어를 얻을 수 있습니다.

V2 / c / ncc.c 소스 코드 에서 프로그래머가 대괄호를 쉽게 입력 할 수있게하는 것이 중요하다는 생각을 뒷받침하는 두드러진 예제를 찾을 수 있습니다 .

/* C command */

main(argc, argv)
char argv[][]; {
    extern callsys, printf, unlink, link, nodup;
    extern getsuf, setsuf, copy;
    extern tsp;
    extern tmp0, tmp1, tmp2, tmp3;
    char tmp0[], tmp1[], tmp2[], tmp3[];
    char glotch[100][], clist[50][], llist[50][], ts[500];
    char tsp[], av[50][], t[];
    auto nc, nl, cflag, i, j, c;

    tmp0 = tmp1 = tmp2 = tmp3 = "//";
    tsp = ts;
    i = nc = nl = cflag = 0;
    while(++i < argc) {
        if(*argv[i] == '-' & argv[i][1]=='c')
            cflag++;
        else {
            t = copy(argv[i]);
            if((c=getsuf(t))=='c') {
                clist[nc++] = t;
                llist[nl++] = setsuf(copy(t));
            } else {
            if (nodup(llist, t))
                llist[nl++] = t;
            }
        }
    }
    if(nc==0)
        goto nocom;
    tmp0 = copy("/tmp/ctm0a");
    while((c=open(tmp0, 0))>=0) {
        close(c);
        tmp0[9]++;
    }
    while((creat(tmp0, 012))<0)
        tmp0[9]++;
    intr(delfil);
    (tmp1 = copy(tmp0))[8] = '1';
    (tmp2 = copy(tmp0))[8] = '2';
    (tmp3 = copy(tmp0))[8] = '3';
    i = 0;
    while(i<nc) {
        if (nc>1)
            printf("%s:\n", clist[i]);
        av[0] = "c0";
        av[1] = clist[i];
        av[2] = tmp1;
        av[3] = tmp2;
        av[4] = 0;
        if (callsys("/usr/lib/c0", av)) {
            cflag++;
            goto loop;
        }
        av[0] = "c1";
        av[1] = tmp1;
        av[2] = tmp2;
        av[3] = tmp3;
        av[4] = 0;
        if(callsys("/usr/lib/c1", av)) {
            cflag++;
            goto loop;
        }
        av[0] = "as";
        av[1] = "-";
        av[2] = tmp3;
        av[3] = 0;
        callsys("/bin/as", av);
        t = setsuf(clist[i]);
        unlink(t);
        if(link("a.out", t) | unlink("a.out")) {
            printf("move failed: %s\n", t);
            cflag++;
        }
loop:;
        i++;
    }
nocom:
    if (cflag==0 & nl!=0) {
        i = 0;
        av[0] = "ld";
        av[1] = "/usr/lib/crt0.o";
        j = 2;
        while(i<nl)
            av[j++] = llist[i++];
        av[j++] = "-lc";
        av[j++] = "-l";
        av[j++] = 0;
        callsys("/bin/ld", av);
    }
delfil:
    dexit();
}
dexit()
{
    extern tmp0, tmp1, tmp2, tmp3;

    unlink(tmp1);
    unlink(tmp2);
    unlink(tmp3);
    unlink(tmp0);
    exit();
}

getsuf(s)
char s[];
{
    extern exit, printf;
    auto c;
    char t, os[];

    c = 0;
    os = s;
    while(t = *s++)
        if (t=='/')
            c = 0;
        else
            c++;
    s =- 3;
    if (c<=8 & c>2 & *s++=='.' & *s=='c')
        return('c');
    return(0);
}

setsuf(s)
char s[];
{
    char os[];

    os = s;
    while(*s++);
    s[-2] = 'o';
    return(os);
}

callsys(f, v)
char f[], v[][]; {

    extern fork, execv, wait, printf;
    auto t, status;

    if ((t=fork())==0) {
        execv(f, v);
        printf("Can't find %s\n", f);
        exit(1);
    } else
        if (t == -1) {
            printf("Try again\n");
            return(1);
        }
    while(t!=wait(&status));
    if ((t=(status&0377)) != 0) {
        if (t!=9)       /* interrupt */
            printf("Fatal error in %s\n", f);
        dexit();
    }
    return((status>>8) & 0377);
}

copy(s)
char s[]; {
    extern tsp;
    char tsp[], otsp[];

    otsp = tsp;
    while(*tsp++ = *s++);
    return(otsp);
}

nodup(l, s)
char l[][], s[]; {

    char t[], os[], c;

    os = s;
    while(t = *l++) {
        s = os;
        while(c = *s++)
            if (c != *t++) goto ll;
        if (*t++ == '\0') return (0);
ll:;
    }
    return(1);
}

tsp;
tmp0;
tmp1;
tmp2;
tmp3;

목표로 삼은 실제 응용 프로그램에서의 사용을 기반으로 언어 구문 요소를 나타 내기 위해 문자를 선택하는 실용적인 동기 부여 가이 훌륭한 답변에서 설명 된 것처럼 Zipf의 법칙과 어떻게 유사한 지 주목하는 것이 흥미 롭습니다 ...

주파수와 길이의 관계를 Zipf의 법칙 이라고합니다

... 위 문장의 길이 가 타이핑 속도로 대체되거나 일반화 된다는 유일한 차이점이 있습니다.


5
언어 디자이너들이이 "명백한"기대를지지하는 것이 있습니까? 중괄호가 배열 선언보다 훨씬 흔하다는 것을 알기 위해 C에서는 많은 프로그래밍이 필요하지 않습니다. 옛날부터 크게 바뀌지 않았습니다 .K & R을 살펴보십시오.

1
나는 어떻게 든이 설명을 의심합니다. 우리는 사람들이 배열 표기법을 결정하기 때문에 기대했던 것을 알지 못하고 다른 방법으로 쉽게 선택할 수있었습니다. 중괄호가 "중요하지 않은"옵션이라고 생각하는지조차 모릅니다. 아마도 중괄호를 더 좋아했을 것입니다.
thorsten müller

3
@ gnat : 현대 키보드에서 대괄호를 입력하는 것이 더 쉽습니다. 유닉스와 c가 처음 구현되었을 때의 키보드에 적용됩니까? 나는 그들이 같은 키보드를 사용하고 있거나 다른 키보드가 자신의 키보드와 같다고 생각하거나 타이핑 속도가 한 문자로 최적화 할 가치가 있다고 생각했을 것이라고 생각할 이유가 없습니다.
Michael Shaw

1
또한 Zipf의 법칙은 자연 언어로 끝나는 일에 대한 일반화입니다. C는 인위적으로 구성되었으므로 C의 디자이너가 의도적으로 C를 적용하기로 결정하지 않는 한 여기에 적용 할 것이라고 생각할 이유가 없습니다. 그것이 적용 되었다면, 이미 단일 문자만큼 짧은 것을 단순화한다고 가정 할 이유가 없습니다.
Michael Shaw

1
@gnat FWIW는 grep -Fo나에게 말한다 *.c, libffi를 포함하는 (즉 내가 손에 무엇을 때문에 회전 속도를 올린다. 4b42d7f288c5) 39511 들어있는 CPython의 소스 코드 파일 {(39,508이 {두 중괄호가 폐쇄되지 왜, 몰라), 만 13,718 [13,702 ( [). 문자열 과이 질문과 관련이없는 컨텍스트에서 발생 횟수를 계산하므로 코드베이스가 대표적이지 않을 수도 있음을 무시하더라도 실제로는 정확하지 않습니다 (이 바이어스는 어느 방향 으로든 갈 수 있음에 유의하십시오). 여전히 2.8 배입니까?

1

C (및 C ++ 및 C # )는 1969 년 Ken Thompson (Dennis Ritchie의 공헌)이 작성한 이전 모델 B 에서 브레이싱 스타일을 상속했습니다 .

이 예제는 Ken Thompson의 위키 백과 를 통해 사용자에 대한 B의 참조입니다 .

/* The following function will print a non-negative number, n, to
   the base b, where 2<=b<=10,  This routine uses the fact that
   in the ASCII character set, the digits 0 to 9 have sequential
   code values.  */

printn(n,b) {
        extern putchar;
        auto a;

        if(a=n/b) /* assignment, not test for equality */
                printn(a, b); /* recursive */
        putchar(n%b + '0');
}

B 자체는 다시 1966 년 Martins Richards가 Multics 운영 체제 용으로 작성한 언어 인 BCPL을 기반으로했습니다 . B의 브레이싱 시스템은 추가 문자로 수정 된 둥근 중괄호 만 사용했습니다 ( Wikipedia 를 통해 Martin Richards의 팩토리얼 인쇄 예 ).

GET "LIBHDR"

LET START() = VALOF $(
        FOR I = 1 TO 5 DO
                WRITEF("%N! = %I4*N", I, FACT(I))
        RESULTIS 0
)$

AND FACT(N) = N = 0 -> 1, N * FACT(N - 1)

B 및 후속 언어 "{...}"에 사용 된 중괄호는 BCPL "$ (...) $"의 원래 복합 중괄호 스타일을 개선 한 Ken Thompson입니다.


1
아니요. Bob Bemer ( en.wikipedia.org/wiki/Bob_Bemer )가이 책임을 맡고있는 것 같습니다. "... 알골 '시작'및 '종료'키워드 대신 사용할 수 있다고 제안했습니다 나중에 C 언어로 어떻게 사용되는지 " ( bobbemer.com/BRACES.HTM에서 )
SChepurin

1
$( ... $)형식은 C에서 와 { ... }마찬가지로 BCPL의 렉서에서 ??< ... ??>와 동일합니다 { ... }. 두 스타일 사이의 개선은 언어가 아닌 키보드 하드웨어에서 이루어집니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.