实际上,“ C样式语言”的定义可以简化为“使用花括号({})”。为什么我们要使用该特定字符(为什么不使用更合理的字符,例如[],至少在美国键盘上不需要使用Shift键)?

对程序员的生产力有什么实际好处?是来自这些花括号,还是新语言设计师应该寻找替代品(例如Python背后的家伙)?

维基百科告诉我们C使用了花括号,但不是为什么。 Wikipedia上有关基于C的编程语言列表的一篇文章表明,此语法元素有些特殊:


广义上讲,C系列语言是使用类似C块的语言语法(包括开始和结束块的花括号)...


评论

唯一可以回答这个问题的人是丹尼斯·里奇(Dennis Ritchie),他已经死了。一个合理的猜测是[]已经用于数组。

@DirkHolsopple所以他没有留下任何理由吗?德拉特另外:对我真正好奇的事情有两票否决?谢谢....

请在此元问题中继续讨论此问题。

我已经解锁了这个帖子。请保留有关该问题的任何评论以及有关元问题的适当性的讨论。

这可能与以下事实有关:在数学的集合符号中使用了花括号,使得花括号在使用数组元素时显得有些尴尬,而不是声明诸如结构,数组等“集合”之类的东西。甚至像Python这样的现代语言,都使用花括号来声明集合和字典。那么问题是,为什么C还使用花括号声明作用域?可能是因为设计人员只是不喜欢BEGIN / END这样的已知替代方法,并且认为重载数组访问符号([])在美学上比集合符号更不合理。

#1 楼

对C的两个主要影响是Algol语言家族(Algol 60和Algol 68)和BCPL(C以其命名)。


BCPL是第一个大括号编程语言和大括号括住了语法的变化,并成为表示程序源代码语句的常见方式。实际上,在当今受限的键盘上,源程序经常使用序列
$(和$)代替符号{和}。 BCPL的单行'//'
注释(未在C中接受,在C ++中重新出现,并在C99中稍后出现。


来自) http://www.princeton.edu/~achaney/tmve/wiki100k/docs/BCPL.html


BCPL引入并实施了几项创新,这些创新成为了
后期语言的设计。因此,它是
第一种花括号编程语言(一种使用{}作为块
分隔符),并且是使用//标记内联
注释的第一种语言。 >

在BCPL中,人们经常会看到花括号,但并非总是如此。这是当时键盘的局限性。在字典上,字符$($){}等价。字母和三边字母在C中维护(尽管花括号替换使用不同的集合-??<??>)。

在B中花括号的使用得到了进一步完善(在C之前)。
从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');
}

有迹象表明花括号在Algol中被用作beginend的简写。 br />

我记得您也将它们包含在您在CACM中发布的256个字符的卡代码中,因为我发现您有趣的是,他们
提议他们可以代替Algol的“开始”和
'end'关键字,这正是后来在C
语言中使用的方式。


来自http://www.bobbemer.com/BRACES.HTM


使用方括号(作为问题中的建议替代品)的使用还可以追溯到更早。如前所述,Algol家族影响了C。在Algol 60和68(C于1972年编写,BCPL于1966年编写)中,方括号用于将索引指定为数组或矩阵。

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



由于程序员已经熟悉Algol和BCPL中的数组的方括号,以及BCPL中的块的花括号,因此在制作另一种语言时几乎不需要更改。


更新后的问题包括使用花括号的生产率附录,并提到了python。尽管答案可以归结为“它的轶事,而您习惯的是最有生产力的东西”,但是还有其他一些资源可以进行这项研究。由于编程技能和对不同语言的熟悉程度差异很大,因此很难解释这些问题。

另请参阅:堆栈溢出是否有统计研究表明Python的“生产力更高”? />
大部分收益将取决于所使用的IDE(或缺少)。在基于vi的编辑器中,将光标置于一个匹配的打开/关闭上,然后按%将光标移至另一匹配的字符。过去使用基于C的语言时,这非常有效-现在要少得多。

更好的比较是{}begin / end之间的选择(水平空间非常宝贵) 。许多Wirth语言基于beginend风格(Algol(上面提到),pascal(很多人熟悉)和Modula家族)。

我很难找到任何能够隔离这种特定语言功能的东西-充其量我只能做的是证明花括号语言比开始结束语言更受欢迎,这是一种常见的构造。如上面的Bob Bemer链接中所述,花括号用于简化速记程序的编写。

为什么Pascal不是我最喜欢的编程语言



与{和}相比,C和Ratfor程序员发现'begin'和'end'庞大。


可以说的全部-它的熟悉性和偏好。

评论


现在这里的每个人都在学习BCPL,而不是在工作:)

– DenysSéguret
13 Feb 26 '15:36



{和}的三元组(在1989 ISO C标准中引入)是?? <和??>。图(由1995年修订版引入)为<%和%>。在很早的翻译阶段,三部曲在所有情况下都得到了扩展。图是标记,不会在字符串文字,字符常量或注释中扩展。

–基思·汤普森(Keith Thompson)
13年2月26日在16:45

在1989年以前,C语言中就存在这样的东西(我必须掏出第一版的书才能得到日期)。并非所有EBCDIC代码页中都带有花括号(或方括号),并且最早的C编译器中对此有规定。

–user40980
13年2月26日在18:31

@NevilleDNZ BCPL在1966年使用了花括号。Algol68的概念源自何处,但BCPL并不是Algo68的。三元运算符是我一直感兴趣的东西,并且可以追溯到CPL(1963)(BCPL的前身),后者从Lisp(1958)借用了这个概念。

–user40980
13年2月27日在0:19

1968年:Algol68允许使用圆括号(〜)作为begin〜end粗体符号块的简写。这些称为简短符号,请参见wp:Algol68粗体符号,使代码块可以像表达式一样对待。 A68还具有简短的简写,例如C的?:三元运算符,例如x:=(c | s1 | s2)而不是C的x = c?s1 | s2。同样,这适用于if&case语句。 ¢顺便说一句:A68是从外壳获得esac和fi的位置¢

– NevilleDNZ
13 Feb 27'0:22



#2 楼

自从IBM 2741终端“在Multics上广泛使用”以来,方括号[]的键入更加容易,反过来,该终端又由C语言创建者之一Dennis Ritchie担任开发团队成员。



请注意,IBM 2741布局中没有花括号!

在C语言中,“花括号”被“采用”,因为它们用于数组和指针。如果语言设计师期望数组和指针比代码块更重要/更频繁地使用(这听起来像是一种合理的假设,更多地是在下面的编码样式的历史背景下),则这意味着花括号将变得“不太重要”。语法。

Ritchie的文章《 C语言的发展》很明显地体现了数组的重要性。甚至有一个明确陈述的假设,即“ C程序中的指针盛行”。


...新语言保留了对数组语义的连贯且可行的(如果不寻常的)解释。 。C语言在其同类语言中最有两个特点:数组与指针之间的关系... C的另一个特点是对数组的处理...具有真正的优点。尽管指针和数组之间的关系很特殊,但可以学习。此外,该语言还具有描述重要概念的强大功能,例如,向量的长度在运行时会有所变化,只有一些基本规则和约定...



为了进一步了解创建C语言时的历史背景和编码风格,需要考虑到“ C的起源与Unix的开发紧密相关”,尤其是将OS移植到PDP- 11“导致开发C的早期版本”(引用来源)。根据Wikipedia的说法,“在1972年,Unix用C编程语言重写了。”

各种旧版本的Unix的源代码都可以在线获得,例如在Unix Tree网站上。在这里介绍的各种版本中,最相关的似乎是1972-06年的Unix第二版:


Unix的第二版是由Ken Thompson在Bell Labs为PDP-11开发的,丹尼斯·里奇(Dennis Ritchie)和其他人。它通过更多的系统调用和更多的命令扩展了第一版。此版本还看到了C语言的开始,该语言用于编写一些命令...


您可以从第二版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++ == 'q4312078q') return (0);
ll:;
    }
    return(1);
}

tsp;
tmp0;
tmp1;
tmp2;
tmp3;



有趣的是,注意到基于字符的字符选择语言来表示语言语法元素的实用动机在有针对性的实际应用中使用类似于Zipf定律,正如在这个极好的答案中所解释的...


观察到的频率和长度之间的关系称为Zipf定律


...唯一的区别是上述语句中的长度被/概括为键入速度。

评论


是否有任何支持语言设计师的“明显”期望的东西?在C中不需要花太多编程就可以注意到花括号比数组声明更常见。自从过去以来,这并没有真正改变-看看K&R。

–user7043
13年2月26日在15:09

我以某种方式怀疑这种解释。我们不知道期望值是什么,因为他们也是决定数组表示法的人,所以他们可以很容易地选择它。我们甚至不知道他们是否认为花括号是“不太重要”的选择,也许他们更喜欢花括号。

–瑟森·穆勒
13年2月26日在15:09

@gnat:方括号在现代键盘上更容易键入,这是否适用于首次实现unix和c时使用的键盘?我没有理由怀疑他们使用的是相同的键盘,或者他们会假设其他键盘会像他们的键盘一样,或者他们认为打字速度值得一个字符来优化。

–迈克尔·肖(Michael Shaw)
13年2月26日在15:22

同样,齐普夫定律是对自然语言中最终结果的概括。 C是人为构造的,因此除非C的设计者有意识地决定故意使用它,否则没有理由认为它会在这里应用。如果确实适用,则没有理由假设它会简化已经短于单个字符的内容。

–迈克尔·肖(Michael Shaw)
13年2月26日在15:28

@gnat FWIW,grep -Fo告诉我CPython源代码的* .c文件(修订版4b42d7f288c5,因为这就是我手头的文件),其中包括libffi,包含39511 {(39508 {,不知道为什么两个大括号不是关闭),但只有13718 [(13702 [)。这是在字符串中以及与此问题无关的上下文中计算出现的次数,因此即使我们忽略代码库可能不具有代表性(请注意,这种偏向可能会朝任一方向),这也不是很准确。仍然是2.8的倍数?

–user7043
13年2月26日在18:02

#3 楼

C(以及后来的C ++和C#)从其前身B继承了其支撑样式,该样式由Ken Thompson(由Dennis Ritchie撰写)于1969年编写。

此示例来自《用户参考》 Ken Thompson的B(通过Wikipedia):

/* 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本身又基于BCPL,这是Martin Richards在1966年为Multics操作系统编写的语言。 B的支撑系统仅使用圆括号,并通过其他字符进行了修改(Martin Richards的打印阶乘示例,通过维基百科):
“ {...}”是Ken Thompson对BCPL“ $(...)$”中原始复合花括号样式的改进。

评论


否。似乎鲍勃·贝默(en.wikipedia.org/wiki/Bob_Bemer)对此负有责任-“ ...您建议可以使用它们代替Algol的'begin'和'end'关键字,这正是后来如何在C语言中使用它们。” (来自bobbemer.com/BRACES.HTM)

–谢普林
13年2月26日在15:49

$(... $)格式等效于BCPL中的词法分析器中的{...},就像?? <... ??>等效于C语言中的{...}一样。两者之间的改进样式是在键盘硬件中,而不是语言。

–user40980
13年2月26日在16:30