c/c++语言开发共享奇怪的全局变量行为,一旦变量名称被更改,问题就会消失

在我的大学练习中,我遇到了变量的奇怪行为。

/* Main parameters */ double sizeX, sizeY; /* Size of the global domain */ int nPartX, nPartY; /* Particle number in x, y direction */ int nPart; /* Total number of particles */ int nCellX, nCellY; /* (Global) number of cells in x, y direction */ int steps; /* Number of timesteps */ double dt; /* Stepsize for timesteps */ int logs; /* Whether or not we want to keep logfiles */ void ReadInput(const char *fname) { FILE *fp; char c; Debug("ReadInput", 0); if(rank == 0) { fp = fopen(fname, "r"); if(!fp) Debug("Cannot open input file", 1); if(fscanf(fp, "sizeX: %lfn", &sizeX) != 1) Debug("sizeX?", 1); if(fscanf(fp, "sizeY: %lfn", &sizeY) != 1) Debug("sizeY?", 1); if(fscanf(fp, "nPartX:%in", &nPartX) != 1) Debug("nPartX?", 1); if(fscanf(fp, "nPartY:%in", &nPartY) != 1) Debug("nPartY?", 1); if(fscanf(fp, "nCellX:%in", &nCellX) != 1) Debug("nCellX?", 1); //read value is 10 if(fscanf(fp, "nCellY:%in", &nCellY) != 1) Debug("nCellY?", 1); if(fscanf(fp, "steps: %lin", &steps) != 1) Debug("steps?", 1); //here the nCellX variable value 10 is changed somehow to 0 if(fscanf(fp, "dt: %lfn", &dt) != 1) Debug("dt?", 1); if(fscanf(fp, "logs: %cn", &c) != 1) Debug("logs?", 1); logs = (c == 'y'); fclose(fp); } printf("(%i) reporting in...n", rank); MPI_Bcast(&sizeX, 1, MPI_DOUBLE, 0, grid_comm); MPI_Bcast(&sizeY, 1, MPI_DOUBLE, 0, grid_comm); MPI_Bcast(&nPartX,1, MPI_INT, 0, grid_comm); MPI_Bcast(&nPartY,1, MPI_INT, 0, grid_comm); MPI_Bcast(&nCellX,1, MPI_INT, 0, grid_comm); MPI_Bcast(&nCellY,1, MPI_INT, 0, grid_comm); MPI_Bcast(&steps, 1, MPI_INT, 0, grid_comm); MPI_Bcast(&dt, 1, MPI_DOUBLE, 0, grid_comm); MPI_Bcast(&logs, 1, MPI_INT, 0, grid_comm); nPart = nPartX * nPartY; dt2 = dt * dt; } 

老师和我得出结论,如果我们将变量名称从“nCellX”更改为“nCellX_2”,问题就会消失,代码会按预期工作。 另一个有趣的事情是,只有这个单一的全局变量有这个问题,其他变量才能正常工作。 我想知道是否有人遇到过这种类型的问题。 任何指南/解释将不胜感激。

如果这个问题不够清楚,请告诉我,如果需要完整代码,我也可以提供。 通常,代码是粒子在单元格中的并行算法。

    以下代码行可能会导致问题:

     if(fscanf(fp, "steps: %lin", &steps) != 1) Debug("steps?", 1); 

    %li表示长整数,可能是64位,而stepsint ,可能是32位。 格式说明符应为%i而不是%li

    是否存在实际问题取决于环境(例如,如果构建64位应用程序,则很可能是一个问题)。 如果存在64位与32位不匹配,则fscanf调用将覆盖内存并可能破坏内存布局中的steps之后的任何变量(可能是nCellX )。 请注意,使用-Wall选项应该警告您这种情况。 为什么将nCellX的名称更改为不同的名称应掩盖问题尚不清楚,但似乎更改名称可能会导致内存中变量布局的更改; 我怀疑C标准是不允许的(虽然我没看过)。

    作为对@Mark Wilkins&Co。评论的确认,我试图certificate命名定义可以产生影响。

    案件:
    fprintf()接受一个指针 ,它存储它所读取的内容。 它不知道它指向的类型,但从格式中获取定义并转换参数。 像sscanf("36", "%i", &my_dest); – > number = va_arg(vl, int*);

    为编译器使用正确的标志来捕获它


    当exec启动程序时,它通常为称为BSS的区域中的未初始化数据(即int foo;)分配地址。 (参见图1下方的图)。

    在许多系统上,这将来自低内存地址。

    为了certificate发生了什么(在给定的系统上),我们有如下:

    我从以下开始:

     /* global scope */ unsigned char unA; unsigned char unB; unsigned char unC; unsigned int unD; 

    清单1

    main()我说:

     unA = '1'; unB = '2'; unC = '3'; /* bit shifting the "string" NAC! into unD, reverse order as my system is LSB * first (little-endian), unD becomes 558055758 => by byte ASCII !CNA */ unD = 0 | ('!' << 24) | ('C' << 16) | ('A' << 8) | 'N'; 

    清单2

    并指向unA的unsigned char指针并转储以下16个字节,这导致:
    转储的格式为[char ],或带有前导零的hex (%c。或%02x)*

      +-- Address of unA | 0x804b06c: 1.3.0000N.AC!. 2.00000000000000 | | |_____| | | | | +--- unB | | +--------- unD | +------------------ unC +-------------------- unA 

    清单3

    然后我将unB名称更改为unB ,文件中的顺序相同:

     unsigned char unA; unsigned char un2; unsigned char unC; unsigned int unD; 

    清单4

    现在我的转储给出:

      +-- Address of unA | 0x804b06c: 1.3.2.00NAC!. 0000000000000000 | | | |_____| | | | +--------- unD | | +---------------- unB | +------------------ unC +-------------------- unA 

    清单5

    可以看出地址/对齐的顺序已经改变。 类型无变化,仅限于名称。


    分配错误的类型:

    然后下一步是转换和溢出范围的类型。 将unB更改回unB 。 我们在列表3中进行了对齐。

    我们创建一个设置字节的函数(在一个4字节/ 32位int的系统上),高阶为:

     void set_what(unsigned int *n) { *n = 0 | ('t' << 24) | ('a' << 16) | ('h' << 8) | 'w'; /* or *n = 0x74616877; in an ASCII environment * 0x74 0x61 0x68 0x77 == tahw */ } 

    清单6

    main()我们说:

     /* dump */ set_what((unsigned int*)&unA); /* dump */ 

    清单7

    得到:

     0x804b06c: 1.3.0000N.AC!. 2.00000000000000 0x804b06c: whatNAC!. 2.00000000000000 

    清单8

    要么:

     set_what((unsigned int*)&unB); -> Yield: 0x804b06c: 1.3.0000N.AC!. 2.00000000000000 0x804b06c: 1.3.0000N.AC!. what00000000 set_what((unsigned int*)&unC); -> Yield: 0x804b06c: 1.3.0000N.AC!. 2.00000000000000 0x804b06c: 1.whatAC!. 2.00000000000000 

    清单9

    正如人们所看到的那样,数据是过度编写的,无论类型和内容都没有。

    在某些情况下,这将导致SIGSEGV。


    对于代码中的问题,如前面的评论中所述,但我重复一遍。

    在声明中你说int stepsfscanf()你指定%li这是一个long int而不是int 。 在几个系统上,这可能影响不大,但在64位系统上一切都很糟糕。

    由asm检查:

    我们复制代码并制作两个副本,一个带有long int steps; 和一个有int steps; 命名为Alin_ok.cBlin_bad.c 。 然后我们创建一些asm输出。

     A $ cpp lin_ok.c > lin_ok_m32.i A $ cpp lin_ok.c > lin_ok_m64.i B $ cpp lin_bad.c > lin_bad_m32.i B $ cpp lin_bad.c > lin_bad_m64.i A $ gcc -std=c89 -m32 -S lin_ok_m32.i A $ gcc -std=c89 -m64 -S lin_ok_m64.i B $ gcc -std=c89 -m32 -S lin_bad_m32.i B $ gcc -std=c89 -m64 -S lin_bad_m64.i $ diff lin_ok_m32.s lin_ok_m64.s | head 9c9 < .comm steps,4,4 ; reserve 4 bytes --- > .comm steps,8,8 ; reserve 8 bytes ... 

    可以看出,代码指示在64位上保留8个字节,在32位(本系统)上保留4个steps


    如果使用gcc,请使用更多标志进行编译。 我个人使用,通常:

    gcc -Wall- Wextra -pedantic -std = c89 -o main main.c或-std=c99如果需要的话。

    这将为您提供有关scanf中错误类型等问题的警告。


    正在运行的应用程序的布局示例。 它可以完全不同,取决于系统等,但是aprox AFAIK 。 希望我已经完成了大部分工作。

      ________________ _________________ [ ] [ ] [ ] [ Physical memory ] [ Virtual memory ] <-- Translation --> [ ] [ range ] table { - - - - - - - - } [________________] [ ] | [_________________] | +--+ high address : Virtual address | 0xF00 +-------------------+'''''''''''''''''' Runnning env | argv, env-vars, ..| | 0xBFF +-------------------+ | ptr | stack | <- Running storage, where | |... grows down ...| fun_a should return, local | 0xC0000000 on | | variables, env, ... | linux Intel x86 | < huge area > | New frame allocated for | | | recursive calls etc. | |... grows up ...| | | | <- Dynamic memory alloc. | | heap | malloc, etc | 0x9e49+-------------------+ | | double sizeX; | <- Uninitialized data | bss | ... | BSS 000000 ... | seg. | int nCellY | | | int steps; | | 0x804c+-------------------+''''''''''''''''''''' Stored '| --- edata data | | on | seg. | int rank = 0; | <- Initialized data disk | 0x804b+-------------------+ : | --- etext | main() | : | text | mov ecx, edx | <- Instructions : | 0x08048000 on seg. | ELF, or the like | Layout, link, etc : | linux Intel x86 0x8040+-------------------+ '''''''''''''''''''''''''''''' | +--- low address : Virtual address 

    图。1。

      以上就是c/c++开发分享奇怪的全局变量行为,一旦变量名称被更改,问题就会消失相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/550080.html

      (0)
      上一篇 2021年1月14日
      下一篇 2021年1月14日

      精彩推荐