您的位置:首页 > 编程语言

《C专家编程》:最庞大的实体类型-Bug(二)

2016-05-21 16:01 253 查看
          Bug是迄今为止地球上最庞大的实体类型,有近百万种已知的品种。在这方面,它比其他任何已知的生物种类的总和还要多,而且至少多出四倍。---摘自Snope教授的Encyclopedia of Animmal Life。
         分析编程语言缺陷的一种方法就是把所有的缺陷归于三类:

        “多做之过”--不该做的做了;

        “少做之过”--该做的没有做;

        “误做之过”--该做了做了但是做的不对;

一、无论何时,如果你遇见这样一条语句,你可以断定它是错误的:

malloc(strlen(str)); //error


    这是因为其他的字符串处理库函数几乎都包含一个额外空间,用于容纳字符串结尾‘\0’字符。所以malloc(strlen(str)+1);才是正确的!人们很容易忽略strlen这个特殊的情况。

    sizeof 是一个运算符:计算大小时,如果运算对象是类型的名字就必须加括号,如果是一个具体的数据或者变量加不加都可以!

    例如 sizeof int  ;     int *p ; sizeof  *p  ;  sizeof (int );//此括号必须加;

二、“L”的故事

        一个L的NUL和两个L的NULL

        一个L的NUL用于结束一个ASCII字符串。ASCII中字符0的位模式被称为‘NUL’;

        两个L的NULL用于表示什么也不指向,表示一个(空指针);

        如果发现了三个L的NULLL,就要检查看是否出现了错误!
三、switch语句中default的作用与位置对程序的影响。
  (1)default对switch语句的影响:

例一:
int num=4;
switch(num)
{
default:cout<<"default"<<endl;
case 1:cout<<"1"<<endl;break;
case 4:cout<<"4"<<endl;break;
}
结果:4;//如果有对应的case,则与default无关;

例二:
int num=4;
switch(num)
{
case 7:cout<<"7"<<endl;
default:cout<<"default"<<endl;
case 1:cout<<"1"<<endl;break;
case 2:cout<<"2"<<endl;break;
}
结果:default
1

      //从default后面开始执行,遇到break退出;

例三:

    
int num=4;
switch(num)
{
default:cout<<"default"<<endl;
case 1:cout<<"1"<<endl;
case 2:cout<<"2"<<endl;
}
结果:default;
1
2

      //如果没有case语句与之对应,并且case后面没有break,则从default语句的位置开始向后执行,直到遇到某些特殊的要求停止。我们称这种情况为"fall through"。缺省采用“fall through”,在97%情况下都是错误的。

 (2)switch的另一个问题,break到底中断了什么?

        由于C语言中的switch一条语句,曾经导致美国AT&T电话服务的停顿大约9个小时,使AT&T电话网络大部分处于瘫痪状态。当时的电话交换(switch system)系统,采用了switch语句实现交换功能。

代码大意如下:
netword code()
{
switch(line1)
{
case thing1:do1();break;
case thing2:do2();
if(true)
{
dosother();
break;//愿意是跳出本次的if语句;
}
initialization_machine();
break;
case thing3:do3();break;
default:processing();
} //但是break跳出了这里;
using_initialization_machine();//导致没有执行inittialization_machine()函数;
}
     就是因为这段简略的代码导致了AT&T历史上重大的网络故障。这里可以看出,break是跳出最近的那层循环语句或者switch语句。由于它跳出了switch语句致使initialization_machine()工作没有完成,为后面的失败埋下了伏笔。
四、缺省的可见性

       对于一个声明的函数,如果不加任何的修饰符,则是全局可见的。

      function work(){//全局可见}

      extern function work1(){//在任何地方可见}

      static function work2(){//这个文件之外不可见,限制了其作用范围}

      extern:用于函数定义,表示全局可见(属于冗余的)。用于变量,表示它在其他地方定义。

      static:在函数内部,表示该变量的值在各个调用间一直保持延续性;在函数这一级,表示该函数只对本文件可见。

      根据经验,这种缺省的可见性被多次证明是个错误。对象在大多数情况下应该采用缺省的可见性。如果要让它全局可见,应该采用显示的手段来进行注明。
五、“,”号赋值

     i=1,2,3;

    i的结果最终是什么呢?我们知道“,”运算符的值就是最右边操作数的值。但是这里,赋值符的优先级更高,,所以实际的情况应该是:
(i=1),2,3;
      i赋值为1,接着执行常量2,3的运算,计算结果丢弃,最终结果为1。

      在看下面的情况:
i=(1,2,3);
      此时i的结果就是3,由于()改变了赋值语句的优先级,“,”运算符的值就是最右边操作数的值,所以最终是3.
六、“结合性”是什么意思?

     C语言中的优先级是一个十分重要的知识点儿。需要牢记。那么结合性又是什么呢?我们知道在一个表示式中,如果运算符的优先级都不一样,那么我们就按照运算符的优先级进行计算。但是如果所有的优先级都一样呢?

      这时候就用到了结合性。结合性就是仲裁者,它决定在几个优先级相同的操作符中先执行哪一个。

     例如:a*b+c //很easy,我们知道先算*,在算+,因为乘除优先于加减。

     下面一个例子:
int a,b=1,c=2;
a=b=c;
    那么上面的例子如何进行计算呢?
   这里就用到了结合性。所有的赋值符都具有右结合性,就是从右至左依次进行;类似的(&和|)位运算符都具有左结合性。结合性只用于表达式出现两个以上相同优先级的操作符情况。

     所以上面的结果毫无疑问:a=b=c=2.而不是1或者其他的情况。
七、最大一口策略 --(maximal munch strategy)

   如下表达式:

    z=y+++x;

   此句话编译器该怎么翻译呢?

    z=y++ + x; 或者 z=y+ ++x;呢?

   为了避免歧义:ANSI C规定了一种逐渐为人所知的“maximal munch strategy”,最大一口策略。这种策略表示如果下一个标记有超过一种的解释方案,编译器将选取能组成最长字符序列的方案。

   所以翻译的结果为:z=y++ + x;
八、返回局部变量的问题. 

          我们知道,当控制流离开声明自动变量的(即局部变量)的范围时,自动变量便会自动失效,其栈内存空间将被回收。

那么如何解决这个问题呢?

(1)返回一个指向字符串常量的指针;
char * func()
{
return "Hello";  //只适用于简单的字符;
}
(2)使用全局声明的数组;
int num[3]
int *func()
{
num[3]=...
return num;
}
(3)使用静态变量或数组,改变其生命周期;
int func()
{
static int number=10;
return number;
}
(4)显示的分配内存 ,当然要记得释放;
int *func()
{
int *p=malloc(SIZE);
return p;
}

             一般的来说,函数是可以返回局部变量的。 局部变量的作用域只在函数内部,在函数返回后,局部变量的内存已经释放了。因此,如果函数返回的是局部变量的值,不涉及地址,程序不会出错。但是如果返回的是局部变量的地址(指针)的话,程序运行后会出错。因为函数只是把指针复制后返回了,但是指针指向的内容已经被释放了,这样指针指向的内容就是不可预料的内容,调用就会出错。

        准确的来说,函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针是可以的)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: