嵌套范围变量的异常行为

时间:2017-06-14 10:08:42

标签: c gcc scope

我熟悉C中的嵌套范围规则,其中嵌套块内的相同变量名称会影响具有相同名称的外部变量。 但是对于以下代码片段,我无法确定输出的解释。

var pr1 = db.queryAsync('SELECT * FROM safeplusdb.vector;')
      .then(function(records){
          for(i=0; i<records.length; i++)
          {
            if(records[i].type=="ADSL")   
            {
              struct.labelAdsl=records[i].type;
            }
            else if (records[i].type=="Cable")
            {
              struct.labelCable=records[i].type;
            }
            else if (records[i].type=="Fibre")
            {
              struct.labelFibre=records[i].type;
            }
            else if (records[i].type=="Others")
            {
              struct.labelOthers=records[i].type;
            }
            else if (records[i].type=="Ott")
            {
              struct.labelOtt=records[i].type;
            }
            else if (records[i].type=="Satellite")
            {
              struct.labelSatellite=records[i].type;
            }
            else if (records[i].type=="Tnt")
            {
              struct.labelTnt=records[i].type;
            }
          }
      })
      .catch(function(err){
        throw err;
      });

    var pr2 = db.queryAsync("SELECT * FROM safeplusdb.data;")
      .then(function (records){
        for(i=0; i<records.length; i++)
        {
          let record= records[i];

          if(record.vector_id==0)
          {
            factorizeSQL(UTCGap, struct.cllForecastAdsl, struct.valueReturnAdsl, record);       
          }

          else if(record.vector_id==1)
          {
            factorizeSQL(UTCGap, struct.cllForecastCable, struct.valueReturnCable, record);        
          }

          else if(record.vector_id==2)
          {
            factorizeSQL(UTCGap, struct.cllForecastFibre, struct.valueReturnFibre, record);
          }

          else if(record.vector_id==3)
          {
            factorizeSQL(UTCGap, struct.cllForecastOthers, struct.valueReturnOthers, record);
          }

          else if(record.vector_id==4)
          {
            factorizeSQL(UTCGap, struct.cllForecastOtt, struct.valueReturnOtt, record);
          }

          else if(record.vector_id==5)
          {
            factorizeSQL(UTCGap, struct.cllForecastSatellite, struct.valueReturnSatellite, record);
          }

          else if(record.vector_id==6)
          {
            factorizeSQL(UTCGap, struct.cllForecastTnt, struct.valueReturnTnt, record);
          }
        }
      })
      .catch(function(err){
        throw err;
      });

    var pr3 = db.queryAsync("SELECT * FROM safeplusdb.admin;")
      .then(function (records){
        for(i=0; i<records.length; i++)
        {
          struct.adminList[i]=records[i].name;
        }
      })
      .catch(function(err){
        throw err;
      });

    var pr4 = db.queryAsync("SELECT * FROM safeplusdb.media;")
      .then(function (records){
        for(i=0; i<records.length; i++)
        {
          struct.mediaList[i] = { label : records[i].label, value : records[i].value};
        }
      })
      .catch(function(err){
        throw err;
      });

    var pr5 = db.queryAsync("SELECT * FROM safeplusdb.scroll ORDER BY orderscroll;")
      .then(function (records){
        for(i=0; i<records.length; i++)
        {
          let record= records[i];

          if(record.isscrolled==false)
          {
            if(record.idm==null && record.vector_id !=null)
            {
              struct.listUnScroll[struct.indj] = {media : null, platform : null, orderscroll: null};
              struct.listUnScroll[struct.indj].orderscroll = record.orderscroll;
              struct.listUnScroll[struct.indj].platform = struct.listPlatforms[record.vector_id]; 
              struct.indj++;
            }
            else if(record.vector_id==null && record.idm!=null)
            {
              struct.listUnScroll[struct.indj] = {media : null, platform : null, orderscroll: null};
              struct.listUnScroll[struct.indj].orderscroll = record.orderscroll;
              struct.listUnScroll[struct.indj].media = struct.listMedias[record.idm-1];
              struct.indj++;
            }
            else
            {
              struct.listUnScroll[struct.indj] = {media : null, platform : null, orderscroll: null};
              struct.listUnScroll[struct.indj].orderscroll = record.orderscroll;
              struct.listUnScroll[struct.indj].media = struct.listMedias[record.idm-1];
              struct.listUnScroll[struct.indj].platform = struct.listPlatforms[record.vector_id]; 
              struct.indj++;  
            }    
          }

          else if(record.isscrolled==true)
          {
            if(record.idm==null && record.vector_id !=null)
            {
              struct.listScroll[struct.indk] = {media : null, platform : null, orderscroll: null};
              struct.listScroll[struct.indk].orderscroll = record.orderscroll;
              struct.listScroll[struct.indk].platform = struct.listPlatforms[record.vector_id]; 
              struct.indk++;
            }
            else if(record.vector_id==null && record.idm!=null)
            {
              struct.listScroll[struct.indk] = {media : null, platform : null, orderscroll: null};
              struct.listScroll[struct.indk].orderscroll = record.orderscroll;
              struct.listScroll[struct.indk].media = struct.listMedias[record.idm-1];
              struct.indk++;
            }
            else
            {
              struct.listScroll[struct.indk] = {media : null, platform : null, orderscroll: null};
              struct.listScroll[struct.indk].orderscroll = record.orderscroll;
              struct.listScroll[struct.indk].media = struct.listMedias[record.idm-1];
              struct.listScroll[struct.indk].platform = struct.listPlatforms[record.vector_id]; 
              struct.indk++;  
            }       
          }
        }
      })
      .catch(function(err){
        throw err;
      });

    var pr6 = db.queryAsync("SELECT * FROM safeplusdb.device;")
      .then(function (records){
        for(i=0; i<records.length; i++)
        {
          struct.deviceList[i] = {value : records[i].value,  label : records[i].label};
        }
      })
      .catch(function(err){
        throw err;
      });

    var pr7 = db.queryAsync("SELECT * FROM safeplusdb.listmsd;")
      .then(function (records){
        for(i=0; i<records.length; i++)
        {
          let aaa = records[i].namelist;
          let ccc = [];
          struct.msdList[i] = {id: records[i].idlistmsd, labelList: aaa, msds : []};  
        }

        let k = struct.msdList.length;

        db.queryAsync("SELECT * FROM safeplusdb.msd;")
          .then(function (records1)
          {
            for(j=0; j<records1.length; j++)
            {
              for(l=0;l<k;l++)
              {
                if(records1[j].listmsd==struct.msdList[l].id)
                {
                  struct.msdList[l].msds.push(records1[j].msd);
                }
              }
            }
          })
          .catch(function(err){
            throw err;
          });         
      })
      .catch(function(err){
        throw err;
      });

    Promise.all(pr1, pr2, pr3, pr4, pr5, pr6, pr7)
      .then(function(){
        console.log(struct);
      })
       .catch(function(err){
            throw err;
      });   
}

上述代码段的输出为:

  

x = 1,y = 2,z = 3

     

x = 10,y = 20.000000,z = 3

     

x = 1,y = 20.000000,z = 2

请有人帮我理解第三个printf语句中#include <stdio.h> int main() { int x = 1, y = 2, z = 3; printf(" x = %d, y = %d, z = %d \n", x, y, z); { int x = 10; float y = 20; printf(" x = %d, y = %f, z = %d \n", x, y, z); } { int z = 100; printf(" x = %d, y = %f, z = %d \n", x, y, z); } return 0; } 的值如何产生超出范围的变量值。

首先我认为它可能是垃圾值,因为打印y整数会产生垃圾值,因此将内部范围中%f的值更改为其他值会产生与输出相同的值因此,我确认这不是垃圾值。

我使用gcc版本6.3.1 20161221(Red Hat 6.3.1-1)(GCC)编译了程序,并使用各种在线编译器编译了程序。

5 个答案:

答案 0 :(得分:10)

您有一个未定义的行为:

{
    int z = 100;
    printf(" x = %d, y = %f, z = %d \n", x, y, z); // here y is an int
}

如果printf()中的标记与良好类型不匹配,则这是未定义的行为。

您必须使用%d标志。

{
    int z = 100;
    printf(" x = %d, y = %d, z = %d \n", x, y, z); // here y is an int
}

答案 1 :(得分:8)

问题在于格式说明符和提供的参数不匹配。让我们仔细看看。

在第一个内部范围

{
    int x = 10;
    float y = 20;

    printf(" x = %d, y = %f, z = %d \n", x, y, z);
}

y被(重新)定义为float,并且使用该变量作为%f转换说明符的参数非常好。但是,一旦范围结束,( the visble y再次为int。因此,在第二个内部范围

 printf(" x = %d, y = %f, z = %d \n", x, y, z);
                  ^^^^^^

使用%f是错误的并导致undefined behavior。您必须使用%d打印y那里的值。

相关,引用C11,章节§6.2.1/ P4

  

每个其他标识符的范围由其声明的位置决定(在   声明者或类型说明符)。

     

[...]

     

如果是声明符或类型说明符   声明标识符出现在块内或参数声明列表中   一个函数定义,标识符有块作用域,它终止于   相关块。

     

[....]

     

如果标识符指定同名的两个不同实体   空间,范围可能重叠。如果是这样,一个实体(内部范围)的范围将结束   严格地在另一个实体的范围之前(外部范围)。在内部范围内,   identifier指定在内部作用域中声明的实体;在外部宣布的实体   范围在内部范围内隐藏(并且不可见)。

并且,与UB相关,来自章节§7.21.6.1

  

[...]如果有任何论据   不是相应转换规范的正确类型,行为是   未定义。

答案 2 :(得分:4)

  

首先我认为它可能是垃圾值,因为打印带有%f的整数会产生垃圾值,所以将内部范围中的y值更改为其他值会产生与输出相同的值,因此我确认它不是垃圾值。

这是一个错误的假设。它“垃圾”,因为你在这里有未定义的行为

我引用自己对未定义行为的解释:

  

C

中未定义的行为      

C是一种非常低级的语言,其结果如下:

     

没有什么能阻止你做一些完全错误的事情

     

许多语言,尤其是某些托管环境的语言,例如Java   或者C#当你做不允许的事情时,实际上会阻止你,比方说,   访问不存在的数组元素。 C没有。只要你的   程序语法正确,编译器不会抱怨。如果你这样做   在你的程序中被禁止的东西,C只是调用你的行为   程序未定义。这正式允许在运行时发生任何事情   该程序。通常,结果将是崩溃或只是输出“垃圾”   值,如上所示。但如果你真的不走运,你的计划似乎就会出现   正常工作直到它得到一些稍微不同的输入,并由此   时间,你将很难找到你的计划的确切位置   未定义。因此,一定要避免未定义的行为!

     

另外,未定义的行为也会导致安全漏洞。这个   在实践中已经发生了很多。

你遇到程序似乎做了类似正确执行的事情之一,这是运气不好

但它未定义,并且证明使用未经修改的代码在我的机器上发生了什么:

$ ./scope
 x = 1, y = 2, z = 3
 x = 10, y = 20.000000, z = 3
 x = 1, y = 0.000000, z = 3

旁注:尽管 shadowing 定义明确,但您也应该避免使用它。使用阴影创建错误代码只是冒险,因为虽然编译器没有问题,但开始混淆你的变量。

答案 3 :(得分:1)

尝试下面的代码中的方式! ;)

问题如其他答案所示。您忘记了y变量是范围int !!!

#include <stdio.h>

int main()
{
    int x = 1, y = 2, z = 3;
    printf(" x = %d, y = %d, z = %d \n", x, y, z);
    {
        int x = 10;
        float y = 20;

        printf(" x = %d, y = %f, z = %d \n", x, y, z);
    }

    {
        int z = 100;
        printf(" x = %d, y = %f, z = %d \n", x, (float)y, z);
    }

    return 0;
}

答案 4 :(得分:1)

在我的英特尔平台上用VC解释了未定义的行为,因为%f格式说明符需要在堆栈上加倍,这大于int所以当它检索该值,它检索的字节数多于int,现在假定堆栈上不同位置的下一个参数,导致z打印错误(即打印时不是z )。