规范化的C++编程方法备忘录 程序流程

 - goto语句的争议
    从前很多人都喜欢用goto,结果程序结构常常变得难以理解,现在却相反——坚决反对goto,甚至有人要取消它。这就叫“goto恐惧症”...
    用goto即有好处又有坏处,看具体情况了。
    如:
for(y = 0; y < 256; y++)
{
    for(x = 0; x < 256; x++)
    {
        if(clr == 0x000000)
            goto J_BlkPixelsFnd; // Here.
    }
}
J_BlkPixelsFnd:;
...
上面的程序段中,深绿色可以改成:
{
    y = 256; x = 256;
    break;
}
来避免使用goto,但程序段如果很大还要让观众分析一阵,容易让人不知所云。
    又如:
设有一字符串指针pt,如果字符串非空,则执行程序段St1,否则执行St2:
if(pt != NULL)
{
    if(*pt != '/0')
    {
        // St1 is in here.
    }
    else
        goto
 J_EmptyStr;
}
else
{
J_EmptyStr:;
    // St2 is in here.
}
如果去掉goto就要重写一次St2。如果St2很长,就会让看代码的人很费劲。这反而违反了“不要让程序难以让人理解”的初衷。还有,这些重复的代码是会增加代码段长度的,从而使程序运行时占用更多的内存(注意我们不要把两个if合并。理由前面已经提过)。
    goto在编译后就变成了jmp xxxx指令,很多编译器还会做一些附加修改。但要注意,不同的编译器修改行为不同(甚至不做任何修改),我们不该让编译器决定这些。
    一些让我们不能使用goto的情况:
  1. 不要让goto跨过本地变量的创建声明或回收区域:
不好:
...
{
    int a = s;
    CT ob;

    ...
    if(s != 0)
        goto J_Err;
}
...
J_Err:;
...
    这样的代码运行时会导致栈不平衡,还会让析构函数被跳过。注意break和continue语句不会导致这些错误。
但下面的代码是可以的:
...
if(b != 0)
    goto J_Errs;
...
{
    int a = s;
    CT ob;

    ...
}
...
J_Errs:;
...
强烈反对写出这样的代码:
if(c >= 2)
{
    int a = 0;

    ...
        goto J_OthRes;
    ...
}
else
{
    int b = 0;

    ...
J_OthRes:;
    ...
}
你不会想知道编译器会把它改成什么的。相信我。
 2. 不要在try和catch块的内外、块间跳转(理由同上)。但跳过整个try-catch块却是可以的。
 3. 可以在switchers里面使用goto,像这样:
switch(a)
{
case FROGS:;
    ...
    if(r != 0)
        goto J_SEARCHALL;
    ...
    break;
case FREEKS:;
    ...
    goto J_SEARCHALL;
case NERDS:;
    ...
    if(s != 0)
        goto J_SEARCHALL;
    ...
    break;
J_SEARCHALL:;
    // Hey! Do something now:
    ...
    break;
default:;
    ...
}
原因只是goto语句及其目标地址都位于整个switcher内,所以看到以上代码后可不要有什么非份之想...
也就是说,又要提下面的话了:
 4. 不要用goto跳进switcher或从switcher里跳出。后一种情况,应该用break。(注意:如果switcher外面包着循环,需要的话用continue也没问题)
注意对于switcher,编译器可能会为优化执行加入跳转表的数据结构。这样就和第一种情况相同。
 - 循环语句
1.结构化的循环的完整结构如下:
// Pre-procees:
...
// Enter the cycle:
for(;;)
{
    // Do somting:
    ...
    if(...)
    {
        ...
        break// Or a 'continue'.
    }
    // Do somting:
    ...
    if(...)
    {
        ...
        break// Or a 'continue'.
    }
    // Do somting:
    ...
    if(...)
    {
        ...
        break// Or a 'continue'.
    }
        ......
    // The 'if-break/continue's have ended.
    // looping ...
   ...
}
// Process after looping:
...
    对以上内容的说明如下:
 1.这是循环的完全版,其中if的位置可能出现在任何地方,而不限于上面给出的位置。
 2.你可能将预处理和后处理部分不当作循环的一部分,但是按逻辑来说,他们就是循环的一部分。注意写代码时分块清楚。
 3.他们在各种情况下才被简化成简单的for、while、do-while结构。对于嵌套,情况类似。
2.使用指针的循环结构:
 比如复位内存块,从pFrom开始n个int整数,设n << MAX_INT - 1:
int *p0, * p2;

for(p0 = pFrom, p2 = pFrom + n; p0 < p2; p0++)
    *p0 = 0;
上面代码存在有争议的问题:
按程序结构来说,它是运行最快的。以下代码就比较慢:
int *p0, ns;

for(p0 = pFrom, ns = 0; ns < n; ns++)
    *(p0 + ns) = 0;
但按虚内存的概念来看,第一种代码有潜在的安全问题,因为有可能pFrom被分配到可以使pFrom + n == NULL成立。
不过标准让new操作符分配的内存块大小比实际大小多出一个提交的数据结构大小(malloc仅多给一字节),来避免这种问题(不表示你可以用到那个地址,改写会让DEBUG版抛出异常)。
我们最好用第二种代码,虽然这样会慢一些。
[题外话:这里的ns声明是否正确呢?是否要改成size_t?其实不用。注意pFrom开始的n个字节是已经分配到的。不然代码就运行不到这里来了,这里一定要有n << MAX_INT - 1的保证]
再按虚内存的概念,不要依赖操作系统的内存分配机制来保证pFrom + n == NULL不成立。
3.注意循环内部的变量赋值对循环次数的影响。相信有经验的人这种错误会很少。这里就强调对数据结构对象调用写方法时要注意对循环的影响。使用泛型的尤其要注意。没办法,你得自己研究每种这样的调用会产生的影响了。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

打赏
文章很值,打赏犒劳作者一下
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页

打赏

unituniverse2

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值