2012-03-23 4 views
1

Во время моего университетского учения я столкнулся с странным поведением переменной.Странное поведение глобальной переменной, после изменения имени переменной проблема исчезает

/* 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: %lf\n", &sizeX) != 1) Debug("sizeX?", 1); 
    if(fscanf(fp, "sizeY: %lf\n", &sizeY) != 1) Debug("sizeY?", 1); 
    if(fscanf(fp, "nPartX:%i\n", &nPartX) != 1) Debug("nPartX?", 1); 
    if(fscanf(fp, "nPartY:%i\n", &nPartY) != 1) Debug("nPartY?", 1); 
    if(fscanf(fp, "nCellX:%i\n", &nCellX) != 1) Debug("nCellX?", 1); //read value is 10 
    if(fscanf(fp, "nCellY:%i\n", &nCellY) != 1) Debug("nCellY?", 1);  
    if(fscanf(fp, "steps: %li\n", &steps) != 1) Debug("steps?", 1);  
//here the nCellX variable value 10 is changed somehow to 0 
    if(fscanf(fp, "dt: %lf\n", &dt) != 1) Debug("dt?",  1); 
    if(fscanf(fp, "logs: %c\n", &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», проблема исчезает, и код работает, как ожидалось. Еще одна интересная вещь заключается в том, что только эта единственная глобальная переменная имеет эту проблему, другие переменные работают правильно. Мне было интересно, кто-нибудь сталкивался с этим типом проблемы. Будут оценены любые рекомендации/объяснения.

Если эта проблема недостаточно ясна, дайте мне знать, также если требуется полный код, я также могу это предоставить. В общем случае код является параллельным алгоритмом Particle-in-Cell.

+1

Ваша программа многопоточной? И что такое код для 'DEBUG'? – Shahbaz

+2

Вы получаете предупреждения о компиляторе? Попробуйте выполнить компиляцию с помощью '-Wall -Wextra -pedantic' и посмотреть, получаете ли вы что-нибудь. – Collin

+0

Также попробуйте определить 'nCellX', установив его равным некоторому начальному значению. Если это имя было инициализировано ранее (скажем, в MPI где-то), что приведет к ошибке – Collin

ответ

5

Вполне возможно, что следующая строка кода вызывает проблему:

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

%li указывает на длинное целое число, которое может быть 64-бит, а steps является int, который может быть 32 бита , Спецификатор формата должен быть %i вместо %li.

Существует ли реальная проблема в зависимости от среды (например, это скорее всего проблема при построении 64-разрядного приложения). Если это 64-битное и 32-разрядное несоответствие, то вызов fscanf перезапишет память и, возможно, уничтожит любую переменную, которая следует за steps в макете памяти (и это может быть nCellX). Обратите внимание, что использование опции -Wall должно предупредить вас об этой ситуации. Почему изменение имени nCellX на что-то другое должно маскировать проблему, неясно, но казалось бы, что изменение имен может привести к изменению макета переменных в памяти; Я сомневаюсь, что это запрещено стандартом C (хотя я не смотрел).

+0

Это не может объяснить, почему изменение имени 'nCellX' на' nCellX_2' могло бы устранить проблему. – Shahbaz

+2

@Shahbaz, да, может. Если есть несоответствие типа, которое является «неопределенным поведением», все может случиться, даже это. –

+0

«все может случиться», но компиляторы детерминированы. Теоретически да, все может случиться, но на практике с одним и тем же компилятором, одним и тем же кодом, кроме одного изменения имени переменной, вывод «то же самое», что бы это ни было. – Shahbaz

2

В качестве комментария к комментарию от @Mark Wilkins & Co. Я пытаюсь показать, что определение может иметь эффект.

По делу:
fprintf() принимает указатель, где он хранит то, что он прочитал. Он не знает тип, на который он указывает, но возьмите определение из формата и внесите аргумент . Что-то вроде sscanf("36", "%i", &my_dest); -> number = va_arg(vl, int*);

Используйте правильные флаги для вас компилятор поймать эту


Когда Exec запускает программу, как правило, назначать адреса для неинициализированного данных (т.е. Int Foo;) в регион, известный как BSS. (См. Рисунок 1 ниже для рисунка).

На многих системах это было бы с низким адресом памяти и вверх.

Чтобы продемонстрировать, что происходит (в данной системе) мы имеем следующее:

Я начинаю со следующим:

/* 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 и отвалам следующих 16 байт, которые результата:
отвалы в формате [полукокс < точки >], или гексе с ведущим нулевой (% с. или% 02x) *

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

Список 3

Тогда я изменить имя unB к un2, тот же порядок в файле:

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

Список 4

сейчас мой свал дает:

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

Список 5

Как можно видеть порядок адреса/выравнивание было изменено. Без изменений по типу, только по названию.


Назначение неправильного типа:

Следующий шаг затем бросить и диапазон переполнения типа. Изменить un2 назад unB. У нас есть выравнивание, как в Список 3.

Мы создаем функцию, набор байтов (в системе с 4 байта/32bit 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.A.C.!. 2.00000000000000 
0x804b06c: w.h.a.t.N.A.C.!. 2.00000000000000 

Список 8

Или:

set_what((unsigned int*)&unB); -> Yield: 
0x804b06c: 1.3.0000N.A.C.!. 2.00000000000000 
0x804b06c: 1.3.0000N.A.C.!. w.h.a.t.00000000 

set_what((unsigned int*)&unC); -> Yield: 
0x804b06c: 1.3.0000N.A.C.!. 2.00000000000000 
0x804b06c: 1.w.h.a.t.A.C.!. 2.00000000000000 

Список 9

Как можно видеть данные перезаписаны, независимо от типа и что "нет.

В некоторых условиях это приведет к SIGSEGV.


К проблемам вашего кода, как указано в предыдущих комментариях, но повторяю.

В декларациях вы говорите int steps и в fscanf() вы указываете %li который является long int и не int. На quie несколько систем это могло бы иметь небольшой эффект, но на 64-битной системе все идет плохо.

Проверка на ассемблере:

Мы копируем код и сделать две копии, один с long int steps; и один с int steps; имени : lin_ok.c и B: lin_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 
... 

Как можно видеть код предписывает, чтобы зарезервировать 8 байт на 64 бита и 4 на 32 бита (эта система) для steps.


Если вы используете gcc, скомпилируйте с большим количеством флагов. Лично я использую, как правило:

НКУ -Wall- Wextra -pedantic -std = c89 -o основной 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.

+0

Ничего себе, это одно всестороннее объяснение. Большое вам спасибо, как за усилия, так и за разъяснения. – alien01