zoukankan      html  css  js  c++  java
  • 《品悟C——抛弃 C程序设计 中的谬误与恶习》勘误

      由于种种原因,《品悟C——抛弃 C程序设计 中的谬误与恶习》一书中存在着一些错误或瑕疵,在此向该书广大读者表示深深的歉意。并向发现并指出这些错误的wlmouse、〇〇、幻の上帝、OwnWaterloo、Ager、zhou_jingzhi 、小六子、记住,微笑。、C-TTF(无限吸收)各位网友表示衷心的感谢。

     

    (前言)P4

    初学者忽视不良习惯的另一个主要原因是,这些编程陋习有时并不能在短期内显示出其恶果,比如“int a,b,c;”这种烂得不能再烂的变量名。实际上良好编程习惯的意义不仅在于避免错误,还在于它意味着少犯错误,或者在出现错误时很容易检查到并修正,这些长远的利益在短时间内一般是体会不到的。

    应为:

    初学者忽视不良习惯的另一个主要原因是,这些编程陋习有时并不能在短期内显示出其恶果,比如“int a,b,c;”中“a”、“b”、“c”这种烂得不能再烂的变量名。实际上良好编程习惯的意义不仅在于避免错误,还在于它意味着少犯错误,或者在出现错误时很容易检查到并修正,这些长远的利益在短时间内一般是体会不到的。

     

    P7

    在C语言中,赋值表达式并非左值,因而“(a=3*5)=4*3”在语法上就是错误的,甚至根本无法编译;

    应为:

    在C语言中,赋值表达式并非左值,因而“(a=3*5)=4*3”是错误的,甚至根本无法编译;

     

    P7

    问题2中第一段应为存在错误的“样本”文本。

     P9

    #include <stdio.h>

     

    int main( void )

    {

      printf("你好!");

    return 0;

    }

     应为:

     #include <stdio.h>

     

    int main( void )

    {

      printf("你好!”);

    return 0;

    }

     

    P10

    这段代码的错误在于把“printf("Hello,World!\n");”这句中的“"”、“ )”和“;”这三个英文字符错写成了汉语文字字符“””、“)”和“;”。

     

    应为:

    这段代码的错误在于把“printf("你好!”);”这句中的“"”、“ )”和“;”这三个英文字符错写成了汉语文字字符“””、“)”和“;”。

    ----------------------------------------------------------------

    除了在标识符、注释、预处理命令、字符串字面量和字符常量中,C语言源代码只能使用下面的拉丁字符

    n           52个大写和小写拉丁字符

     

    应为:

    除了在标识符、注释、预处理命令、字符串字面量和字符常量中,C语言源代码只能使用下面的拉丁字母字符

    n           52个大写和小写拉丁字母字符

    ----------------------------------------------------------------

    n           29个图形字符(graphic characters):

    ! " # % & ' ( ) * + , . / :

     

    应为:

    n           29个图形字符(graphic characters):

    ! " # % & ' ( ) * + ,  -  . / :

      

    P11

    对于初学者来说,这个样本是错误的代码。不少初学者,在写main()时,由于种种原因,可能会一不小心把“main”这个单词写成“mian”。两者“长”极其相似,加上初学者缺乏调试代码的经验,所以很难发现其中的问题。

     

    应为:

    对于初学者来说,这个样本是错误的代码。不少初学者,在写main()时,由于种种原因,可能会一不小心把“main”这个单词写成“mian”。两者“长相”极其相似,加上初学者缺乏调试代码的经验,所以很难发现其中的问题。

     

    P17

    而scanf()函数之所以要求指针作为参数,最主要的原因是希望通过调用这个函数来改变main()函数本地的变量i的值

     

    应为:

    而scanf()函数之所以要求指针作为参数,最主要的原因是希望通过调用这个函数来改变本地的变量i的值

     
    P18
    第18页:但必须要说的是,这是一种很糟糕的编程风格

    应为  

    第18页:但必须要说的是,重名是一种很糟糕的编程风格

     

    P19

    #define ABC 123

       it abc;

       ABC = 456 ;

       abc = 456 ;

    应为:

    #define ABC 123

       int abc;

       ABC = 456 ;

       abc = 456 ;

    P19
    例如:abcABC、“ABC”,“ABC”,

     应为  

     例如:abcABC、“ABC”,

     

    P23
    初学咋练
    应为:初学乍练
     
    P23
    或对运算规则理解得不够精确到位
    应为:或对运算规则理解得不够精确
     
    P28
    int a[8];
    int i;
    for (i=0; i<8; i++)
    printf("%d", a[i]);
    

     应为:

    int a[8]={0,1,2,3,4,5,6,7};
    int i;
    for (i=0; i<8; i++)
    {
        printf("%d", a[i]);
    }
    
    P29
    printf("%d", sizeif a);
    

     应为:

    printf("%u", sizeif a);
    

    P32

    不错,“d /16”的值和“d >>4”的值有时确实一样,但是将“d /16”或“d >>4”作为表达式的一部分出现时,它们未必是原来的含义。这就好比1+2的值是3,但是当“1+2”作为子表达式时——例如“1+2*3”,就不能把1+2作为3来理解了,“1+2*3”这个表达式的含义是“1+(2*3)”而不是“(1+2)*3”。

    应为:

    不错,“d /16”的值和“d >>4”的值有时确实一样,但是将“d /16”或“d >>4”作为表达式的一部分出现时,它们未必是原来的含义。这就好比1+2的值是3,但是当“1+2”作为表达式的一部分时——例如“1+2*3”,就不能把1+2作为3来理解了,“1+2*3”这个表达式的含义是“1+(2*3)”而不是“(1+2)*3”。

     

    P33

    实际上,有时大人不但比孩子更可笑,但却往往不像孩子那么可爱,甚至可气。因为孩子知道改正错误,而大人却往往对自己的错误不以为然。

    应为:

    实际上,有时大人不但比孩子更可笑,但却往往不像孩子那么可爱,反而可气。因为孩子知道改正错误,而大人却往往对自己的错误不以为然。

     

    P40

    /*

    题目:编写一个函数,求两个int类型数据的和。

    */

     

    int sum(int ,int );

     

    int sum(int n1 ,int  n2 )

    {

      sum;

      sum = n1 + n2 ;

    }  

    应为:

    /*

    题目:编写一个函数,求两个int类型数据的和。

    */

     

    int add(int ,int );

     

    int add (int n1 ,int  n2 )

    {

      sum;

      sum = n1 + n2 ;

    }  

     

    P47-48

    /*题目:建立一个简单的链表,它由三个学生数据的结点组成。输出各结点中的数据。*/

    #include <stdio.h>

    #define NULL 0

     

    struct student{

            long num;

            float score;

            struct student *next;

    };

     

    void  main()

    {  struct student a,b,c,*head,*p;

       a.num = 10101;a.score = 89.5;

       b.num = 10103;b.score = 90;

       do

       c.num = 10107;c.score = 85;

       head = &a;

       a.next = &b;

       b.next = &c;

       c.next = NULL;

       p = head;

          {   printf("%ld%5.1f\n",p->num,p->score);

              p=p->next;

          }while (p != NULL );

    }  

    应为:

    void  main()

    {  struct student a,b,c,*head,*p;

       a.num = 10101;a.score = 89.5;

       b.num = 10103;b.score = 90;

       c.num = 10107;c.score = 85;

       head = &a;

       a.next = &b;

       b.next = &c;

       c.next = NULL;

       p = head;

       do

          {   printf("%ld%5.1f\n",p->num,p->score);

              p=p->next;

          }while (p != NULL );

    }

     

    P49

    struct student a = {10101, 89.5},

               b = {10103, 90},

               c = {10107, 85} ,*head,*p;

     

    应为:

    struct student   a = {10101, 89.5F} ,

                      b = {10103, 90.0F} ,

                      c = {10107, 85.0F} ,*head,*p;

     

     

    P60

      int i , k ;

      printf("请输入一个小于%d的整数i:",M);

      {printf("输入的数据不符合要求,请重新输入一个小于%d的整数i:",M);

       scanf("%d",&i);

      scanf("%d",&i);

      if(i>M)

     

      }

      k=sqrt(i);

      printf("%d的平方根的整数部分是%d\n",i,k);

      return 0;

    }

     

    应为:

      int i , k ;

      printf("请输入一个小于%d的整数i:",M);

      scanf("%d",&i);

      if(i>M)

      {printf("输入的数据不符合要求,请重新输入一个小于%d的整数i:",M);

       scanf("%d",&i);

      }

      k=sqrt(i);

      printf("%d的平方根的整数部分是%d\n",i,k);

      return 0;

    }

      

    P69~70(样本)

      const int n=5;

     int *p = (int *)&n;

     ++*p ;

     printf("%d\n",n);

     int i,j,k;

     printf("The order is:\n");

     for(i=0;i<n-1;i++)

       {k=i;

        for(j=i+1;j<n;j++)

          if(stu[j].score>stu[k].score)

              k=j;

        temp=stu[k];stu[k]=stu[i];stu[i]=temp;

       }

     for(i=0;i<n;i++)

       printf("%6d%8s%6.2f\n",stu[i].num,stu[i].name,stu[i].score);

     printf("\n");

     system("PAUSE");   

     return 0;

    }

      

    应为:

     

     const int n=5;

     int i,j,k;

     printf("The order is:\n");

     for(i=0;i<n-1;i++)

       {k=i;

        for(j=i+1;j<n;j++)

          if(stu[j].score>stu[k].score)

              k=j;

        temp=stu[k];stu[k]=stu[i];stu[i]=temp;

       }

     for(i=0;i<n;i++)

       printf("%6d%8s%6.2f\n",stu[i].num,stu[i].name,stu[i].score);

     printf("\n");

     return 0;

    }

      

    P78

    //把数据存放在C:\   pf = fopen("C:\\DATA.TXT","w");

     

    应为:

    //把数据存放在C:   pf = fopen("C:\\DATA.TXT","w");

     

    P79

    p=(char *)malloc(size_of_string);

    应为: 

    p=(char *)malloc(sizeof string); 

    P79

    在这个题目中,str1、str2这两个指针并没有正确的初始值,但是“canf("%s%s",str1,str2);”却使用了这两个值进行输入,这是使用指针时初学者常见的一个错误。

    应为:

    在这个题目中,str1、str2这两个指针并没有正确的初始值,但是“scanf("%s%s",str1,str2);”却使用了这两个值进行输入,这是使用指针时初学者常见的一个错误。

     

    P87

    样本代码中的另一个问题是根本没有考虑输入一旦不小于3时应该如何处理,这样的一个严重后果是一旦用户误输入数据,比如输入了负数,程序将会发生悲惨的崩溃。在一个真正的软件中,绝对不能假设用户一定会正确地输入,否则可能带来非常严重且无法弥补的损失。

    应为:

    样本代码中的另一个问题是根本没有考虑输入一旦不大于3时应该如何处理,这样的一个严重后果是一旦用户误输入数据,比如输入了负数,程序将会发生悲惨的崩溃。在一个真正的软件中,绝对不能假设用户一定会正确地输入,否则可能带来非常严重且无法弥补的损失。

      

    P88

     也就是说,与"%s"格式转换所对应的实参应该是char *类型。但是在样本代码中的“&str”却是一个char (*)[80]类型的指针,即一个指向由80个char类型元素所构成的数组。因此“&str”是不正确的。

    应为:

    也就是说,与"%s"格式转换所对应的实参应该是char *类型。但是在样本代码中的“&str”却是一个char (*)[80]类型的指针,即指向由80个char类型元素所构成的数组的指针。因此“&str”是不正确的。

    P126

    int main ( void )

    {

      float array[10];

      float ave,max,min;

      int i;

      printf("Please enter 10 scores:");

      input(array , 10 );

     

    应为:

     

    int main ( void )

    {

      float array[10];

      float ave,max,min;

     

      printf("Please enter 10 scores:");

      input(array , 10 );

      

    P133

    int alphabetic(char c)

    {

    if((c>='a'&&c<='z')||(c>='A'&&c<='z'))

    return(1);

    else

    return(0);

    }

    这段代码的毛病在于,当c为字母字符时,表达式(c>='a'&&c<='z') || (c>='A'&&c<='z')的值为1;而若c不为字母字符时,表达式(c>='a'&&c<='z') || (c>='A'&&c<='z')的值为0。因此完全可以直接返回(c>='a'&&c<='z')||(c>='A'&&c<='z')这个表达式的值,而不需要表达成在表达式(c>='a'&&c<='z') || (c>='A'&&c<='z')的值为1时“return 1;”,在表达式(c>='a'&&c<='z') || (c>='A'&&c<='z') 的值为0时“return 0;”。

    int alphabetic(char c)

    {

    return (c>='a'&&c<='z')||(c>='A'&&c<='z') ;

    }

    这个代码与原来的功能完全等效,然而却简洁得多。

     

    应为:

     

    int alphabetic(char c)

    {

    if((c>='a'&&c<='z')||(c>='A'&&c<='Z'))

    return(1);

    else

    return(0);

    }

    这段代码的毛病在于,当c为字母字符时,表达式(c>='a'&&c<='z') || (c>='A'&&c<='Z')的值为1;而若c不为字母字符时,表达式(c>='a'&&c<='z') || (c>='A'&&c<='Z')的值为0。因此完全可以直接返回(c>='a'&&c<='z')||(c>='A'&&c<='Z')这个表达式的值,而不需要表达成在表达式(c>='a'&&c<='z') || (c>='A'&&c<='Z')的值为1时“return 1;”,在表达式(c>='a'&&c<='z') || (c>='A'&&c<='Z') 的值为0时“return 0;”。

    int alphabetic(char c)

    {

    return (c>='a'&&c<='z')||(c>='A'&&c<='Z') ;

    }

    这个代码与原来的功能完全等效,然而却简洁得多。

     

    P148

    1 2 3 4 5 6 7 8 9

    由于1不是素数,所以把它划去:

    1 2 3 4 5 6 7 8 9

    这样2就是找到的第一个素数,再把2的倍数划去:

    1 2 3 4 5 6 7 8 9

    2之后第一个没被划去的3就是第二个素数,再把3的倍数划去:

    1 2 3 4 5 6 7 8 9

    应为:

    1 2 3 4 5 6 7 8 9

    由于1不是素数,所以把它划去:

    1 2 3 4 5 6 7 8 9

    这样2就是找到的第一个素数,再把2的倍数划去:

    1 2 3 4 5 6 7 8 9

    2之后第一个没被划去的3就是第二个素数,再把3的倍数划去:

    1 2 3 4 5 6 7 8 9

      

    P158

     当输入不满足%d格式要求时,scanf()函数调用结束且返回值为0。这里,“,”和“&&”共同保证了必要的顺序。

      

    应为:

    当输入不满足%d格式要求时,scanf()函数调用结束且返回值为0。这里,第一个“,”和“||”共同保证了必要的顺序。

      

    P161

    题目:输出以下图案:

               *****

               *****

               *****

               *****               

               *****

    应为:

    题目:输出以下图案:

               *****

                *****

                 *****

                  *****            

                   *****

      

    P168

     

    #include <stdio.h>

     int main( void )

     {

       puts(******************");

       puts ("  How do you do!");

       puts(******************");

       return 0;

     }

      

    应为:

     

    #include <stdio.h>

     int main( void )

     {

       puts("******************");

       puts ("  How do you do!");

       puts("******************");

       return 0;

     }

     

    P188

    题目要求“写一个函数,输入一行字符,将此字符串中最长的单词输出”,可是无论alphabetic()函数还是longest()函数都没有实现“输入一行字符,将此字符串中最长的单词输出”这个功能要求。疑惑很久,发现实现这个功能的函数居然是main(),这就难免让人贻笑大方了。因为按照惯例,要求写一个函数实现某个功能,从来不是要求写main()函数,尽管不能说main()不是“一个函数”。然而如果是要求main()完成的事情,通常是作为一个完整的问题提出的,不会提出“写一个函数”这样的要求。如果硬要狡辩“写一个函数”也不排除是写main(),就牵强地近乎强词夺理了。不过假若真得有人如此嘴硬,你还真拿他没什么办法。

     

    应为:

     

    题目要求“写一个函数,输入一行字符,将此字符串中最长的单词输出”,可是无论alphabetic()函数还是longest()函数都没有实现“输入一行字符,将此字符串中最长的单词输出”这个功能要求。疑惑很久,发现实现这个功能的函数居然是main(),这就难免贻笑大方了。因为按照惯例,要求写一个函数实现某个功能,从来不是要求写main()函数,尽管不能说main()不是“一个函数”。然而如果是要求main()完成的事情,通常是作为一个完整的问题提出的,不会提出“写一个函数”这样的要求。如果硬要狡辩“写一个函数”也不排除是写main(),就牵强地近乎强词夺理了。不过假若真得有人如此嘴硬,你还真拿他没什么办法。

     

    P278

     之后,1990年国际标准组织ISO的ISO/IEC JTC1/SC22/WG14国际化标准嚣张对C89做了少量编辑性的修改,把它转换成了国际标准ISO/IEC 9899:1990,这就是所谓的C90。

      

    应为:

    之后,1990年国际标准组织ISO的ISO/IEC JTC1/SC22/WG14国际化标准小组对C89做了少量编辑性的修改,把它转换成了国际标准ISO/IEC 9899:1990,这就是所谓的C90。

    P286

    序点(sequence points)是C语言的一个基本概念。

    所谓序点就是指执行序列中的一些点,在这些点上,该点之前所有运算的副效应都应该结束,并且该点之后的副效应还没有发生。

    最典型的序点是“:”,例如:

    应为:

    问题10 序点(sequence points)

    这是C语言的一个基本概念。

    所谓序点就是指执行序列中的一些点,在这些点上,该点之前所有运算的副效应都应该结束,并且该点之后的副效应还没有发生。

    最典型的序点是“;”,例如:

    【注:书中第8章的 问题10 应为 问题11 ;与此相关,目录也应做相应更改。】

     

    P303

    #define CHAR_BIT 8

     

    应为:

     

    #define CHAR_BIT     8

     

    P352

    我们都知道,对于:

    int I;

     

    应为:

     

    我们都知道,对于:

    int i;

     

    P382

    答:这个是时代的产物。在PC从DOS时代转变为Windows时代的过程中,DOS时代开发的IDE(主要是TC2.0)无法在运行程序后显示输出结果,为了在运行后从容仔细地观察一下运行结果再返回IDE界面,加上了这么一句,人为地延长程序运行时间(因为getch()会等待用户输入一个字符)。但这与main()函数本身的结构无关。这条语句不具备普遍意义,只是迁就过时的IDE的一种权宜之计而已。

     

    应为:

     

    答:这个是时代的产物。在PC从DOS时代转变为Windows时代的过程中,DOS时代开发的IDE(主要是TC2.0)无法在运行程序后立即直接显示输出结果,为了在运行后从容仔细地观察一下运行结果再返回IDE界面,加上了这么一句,人为地延长程序运行时间(因为getch()会等待用户输入一个字符)。但这与main()函数本身的结构无关。这条语句不具备普遍意义,只是迁就过时的IDE的一种权宜之计而已。

     

    P384

    第二,就目前看,它们不存在任何过时或即将过时的成分。当然,如果喜欢文雅,不写“return 0;”而写“return SUCCESS;”也可以。

     

    应为:

     

    第二,就目前看,它们不存在任何过时或即将过时的成分。当然,如果喜欢文雅,不写“return 0;”而写“return EXIT_SUCCESS;”也可以。

     

    P493

    本章谈论的是虽然本身与C语言关系不大,但却经常出现在国内C语言书籍之中一些常识性的错误。例如,把ISO说成是International Standard Orgnization的缩写……。

     

    应为:

     

    本章谈论的是虽然本身与C语言关系不大,但却经常出现在国内C语言书籍之中一些常识性的错误。例如,把ISO说成是International Standard Organization的缩写……。

     

    P501

    问题11  关于ISO

    1990年,国际标准化组织ISO(International Standard Orgnization)接受C89作为

    国际标准ISO/IEC9899:1990。

    国际标准化组织ISO的正式名称是“International Organization for Standardization”,而不是“International Standard Orgnization”,后者显然是一种想当然。

    至于为什么“International Organization for Standardization”的简称是ISO而不是IOS,据说是出自希腊文“isos”,“the same”的意思。

     

    应为:

     

    问题11  关于ISO

    1990年,国际标准化组织ISO(International Standard Organization)接受C89作为

    国际标准ISO/IEC9899:1990。

    国际标准化组织ISO的正式名称是“International Organization for Standardization”,而不是“International Standard Organization”,后者显然是一种想当然。

    至于为什么“International Organization for Standardization”的简称是ISO而不是IOS,据说是出自希腊文“isos”,“the same”的意思。

  • 相关阅读:
    第一章 Java入门
    2020-2021-2 网络对抗技术 20181321 Exp 8 Web基础
    2020-2021-2 网络对抗技术 20181321 Exp7 网络欺诈防范
    2020-2021-2 网络对抗技术 20181321 Exp6 MSF基础应用
    Weblogic漏洞复现:CVE-2020-14882未授权代码执行
    利用cse-2020-16127,cve-2020-16125进行漏洞再现
    实验一-密码引擎-加密API实现与测试
    实验一-密码引擎-加密API研究
    API标准
    020-2021-2 网络对抗技术 20181321 Exp5 信息搜集与漏洞扫描
  • 原文地址:https://www.cnblogs.com/pmer/p/2711488.html
Copyright © 2011-2022 走看看