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

          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;
}

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

        准确的来说,函数不能通过返回指向栈内存的指针(注意这里指的是栈,返回指向堆内存的指针是可以的)。



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值