В качестве подтверждения комментария @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 байтов, которые
результат:
Дампы имеют формат [char ] или гекс с начальным нулем (% c. Или% 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 байтами / 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.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
. На некоторых системах это может иметь
мало эффекта, но в 64-битной системе все идет плохо.
Проверить по асм:
Мы копируем код и делаем две копии, одну с long int steps;
и одну с
int steps;
по имени A : 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, скомпилируйте с другими флагами. Лично я использую, как правило:
gcc -Wall- Wextra -pedantic -std = c89 -o main main.c
или -std=c99
если нужно.
Это предупредит вас о таких проблемах, как неправильный тип в scanf.
Пример макета работающего приложения. Это может быть совершенно другим,
в зависимости от системы и т. д., но это примерно 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.