Форум программистов, компьютерный форум, киберфорум
Наши страницы
C для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.63/27: Рейтинг темы: голосов - 27, средняя оценка - 4.63
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
1

[Задача] Адресная арифметика

08.03.2012, 12:38. Просмотров 5005. Ответов 49

Просьба к модераторам: НЕ надо перетаскивать в разделы типа "Си\Си++ для экспертов"

Пример возник на основе реальной программы. Пример содержит ошибку, а потому не факт, что повторится на всех компиляторах. В моём случае ошибка проявлялась на i386-linux32 при использовании компилятора gcc.

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
 
char a = 5;
char b = 10;
 
int main (void)
{
  char tmp;
 
  /* Для контроля убедимся, что "a" и "b" оказались в соседних
   * ячейках памяти. Разница указателей должна равняться единице.
   * Именно единицу мы используем в следующем операторе */
  printf ("&b - &a = %d\n", &b - &a);
 
  /* Записываем в переменную "b" */
  *(&a + 1) = 11;
 
  /* Читаем из переменной "b" */
  tmp = b;
 
  printf ("tmp = %d\n", tmp);
  printf ("b = %d\n", b);
  return 0;
}
Запуск без оптимизаций:

Код
$ gcc t.c
$ ./a.out 
&b - &a = 1
tmp = 11
b = 11
Запуск с оптимизациями:

Код
$ gcc t.c -O3
$ ./a.out 
&b - &a = 1
tmp = 10
b = 11
Вопрос. В программе перед printf'ами есть операция "tmp = b;". Почему напечатались разные значения для "tmp" и "b"? Эти значения далеко не рандомные: одно напечатанное значение соответствует "старому" значению переменной "b", а другое - "новому"

Добавлено через 29 минут
Традиционная просьба прятать догадки и ответы под CUT'ом
0
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
08.03.2012, 12:38
Ответы с готовыми решениями:

Адресная арифметика
Можно ли в C++ в массиве произвольного типа использовать адресную...

адресная арифметика
int funk(char *s) { char *p=s; while(*p) p++; ...

Адресная арифметика
Объясните пожалуйста вот эту строчку кода return allocp -n: Почему просто...

Указатели и адресная арифметика
Язык C. Задание звучит так: 2) Для этого фрагмента программы написать...

Адресная арифметика и массивы
Нужно с помощью указателя организовать ввод и вывод матрицы. Индексную...

49
go
Эксперт С++
3637 / 1369 / 243
Регистрация: 16.04.2009
Сообщений: 4,527
08.03.2012, 13:56 2
Цитата Сообщение от Evg Посмотреть сообщение
Запуск с оптимизациями:
Другой(верный) результат
Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
‘‚@ANDREY-PC /c/mingw
$ gcc Evg.c
 
‘‚@ANDREY-PC /c/mingw
$ a.exe  
&b - &a = 1
tmp = 11
b = 1
‘‚@ANDREY-PC /c/mingw
$ gcc Evg.c -O3
 
‘‚@ANDREY-PC /c/mingw
$ a.exe
&b - &a = -1
tmp = 10
b = 10
Переменные были созданы иначе по адресам(с начало идет b, затем a, результат b соответственно не меняется), но результат верный вывело. Как у Вас так получилось?


Добавлено через 6 минут
Цитата Сообщение от Evg Посмотреть сообщение
Пример содержит ошибку,
А, да. Разобрался где ошибку.
0
alex_x_x
бжни
2455 / 1662 / 134
Регистрация: 14.05.2009
Сообщений: 7,162
08.03.2012, 14:02 3
у меня компилятор в адресном пространстве переставил местами a и b, но это не суть важно
судя по результату дизассемблирования компилятор
у меня b находится в $0x804a014
a - в $0x804a015
переменной tmp вообще нету, вместо нее компилятор подставляет адрес $0x804a014

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
int main (void)
{
 8048350:   55                      push   %ebp
 8048351:   89 e5                   mov    %esp,%ebp
 8048353:   83 e4 f0                and    $0xfffffff0,%esp
 8048356:   83 ec 10                sub    $0x10,%esp
}
 
__extern_always_inline int
printf (__const char *__restrict __fmt, ...)
{
  return __printf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ());
 8048359:   c7 44 24 0c 14 a0 04    movl   $0x804a014,0xc(%esp)
 8048360:   08 
 8048361:   c7 44 24 08 15 a0 04    movl   $0x804a015,0x8(%esp)
 8048368:   08 
 8048369:   c7 44 24 04 70 85 04    movl   $0x8048570,0x4(%esp)
 8048370:   08 
 8048371:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 8048378:   e8 c3 ff ff ff          call   8048340 <__printf_chk@plt>
 
  /* Для контроля убедимся, что "a" и "b" оказались в соседних
   * ячейках памяти. Разница указателей должна равняться единице.
   * Именно единицу мы используем в следующем операторе */
  printf ("%p %p\n", &a, &b);
  printf ("&b - &a = %d\n", &b - &a);
 804837d:   b8 14 a0 04 08          mov    $0x804a014,%eax
 8048382:   2d 15 a0 04 08          sub    $0x804a015,%eax
 8048387:   89 44 24 08             mov    %eax,0x8(%esp)
 804838b:   c7 44 24 04 77 85 04    movl   $0x8048577,0x4(%esp)
 8048392:   08 
 8048393:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 804839a:   e8 a1 ff ff ff          call   8048340 <__printf_chk@plt>
  *(&a - 1) = 11;
 
  /* Читаем из переменной "b" */
  tmp = b;
 
  printf ("tmp = %d\n", tmp);
 804839f:   0f be 05 14 a0 04 08    movsbl 0x804a014,%eax // помещаем b в регистр eax
 80483a6:   c7 44 24 04 85 85 04    movl   $0x8048585,0x4(%esp)
 80483ad:   08 
 80483ae:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
   * Именно единицу мы используем в следующем операторе */
  printf ("%p %p\n", &a, &b);
  printf ("&b - &a = %d\n", &b - &a);
 
  /* Записываем в переменную "b" */
  *(&a - 1) = 11;
 80483b5:   c6 05 14 a0 04 08 0b    movb   $0xb,0x804a014 // компилятор все понял и пишет прямо в b
 
  /* Читаем из переменной "b" */
  tmp = b;
 
  printf ("tmp = %d\n", tmp);
 80483bc:   89 44 24 08             mov    %eax,0x8(%esp) //[B] !!! кладет старое значение, сохраненное в eax[/B]
 80483c0:   e8 7b ff ff ff          call   8048340 <__printf_chk@plt>
  printf ("b = %d\n", b);
 80483c5:   0f be 05 14 a0 04 08    movsbl 0x804a014,%eax // кладет новое значение b в eax
 80483cc:   c7 44 24 04 8f 85 04    movl   $0x804858f,0x4(%esp)
 80483d3:   08 
 80483d4:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)
 80483db:   89 44 24 08             mov    %eax,0x8(%esp)
 80483df:   e8 5c ff ff ff          call   8048340 <__printf_chk@plt>
  return 0;
}
 80483e4:   31 c0                   xor    %eax,%eax
 80483e6:   c9                      leave  
 80483e7:   c3                      ret
собственно компилятор вырезал переменную tmp, положил значение переменной b в eax, после этого изменил значение b, но при выводе tmp использовал старое значение из eax
баг компилятор на лицо
0
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
08.03.2012, 14:13  [ТС] 4
Там, где компилятор поменял местами "a" и "b" нужно изменить выражение "&a + 1" на "&a - 1". Я специально вставил печать разницы адресов. Чтобы если что-то не так, то подправить то место, где через адрес одной переменной мы получаем адрес другой переменной

Цитата Сообщение от alex_x_x Посмотреть сообщение
баг компилятор на лицо
Позже объясню, что это не баг компилятора, а ошибка в исходнике. Собственно, вопрос можно было бы переформулировать по другому: в чём ошибка данного исходника?

Добавлено через 34 секунды
Цитата Сообщение от go Посмотреть сообщение
Разобрался где ошибку
Если ты имел в виду порядок распределения в памяти переменных a и b - то только что пояснил
0
alex_x_x
бжни
2455 / 1662 / 134
Регистрация: 14.05.2009
Сообщений: 7,162
08.03.2012, 14:14 5
Цитата Сообщение от Evg Посмотреть сообщение
Там, где компилятор поменял местами "a" и "b" нужно изменить выражение "&a + 1" на "&a - 1". Я специально вставил печать разницы адресов. Чтобы если что-то не так, то подправить то место, где через адрес одной переменной мы получаем адрес другой переменной
я так и сделал, ессно

собсно правильно начинает работать только при volatile-модификаторе у a и b - может в этом подвох?
0
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
08.03.2012, 14:16  [ТС] 6
Цитата Сообщение от alex_x_x Посмотреть сообщение
собсно правильно начинает работать только при volatile-модификаторе у a и b - может в этом подвох?
volatile нужно использовать в тех местах, когда есть доступ к переменным, который компилятору не виден (типа аппаратных портов ввода-вывода). Это ведь явно не тот случай
0
go
Эксперт С++
3637 / 1369 / 243
Регистрация: 16.04.2009
Сообщений: 4,527
08.03.2012, 14:22 7
alex_x_x, http://ideone.com/GaThh а вот такой результат?

Добавлено через 5 минут
Цитата Сообщение от Evg Посмотреть сообщение
Там, где компилятор поменял местами "a" и "b" нужно изменить выражение "&a + 1" на "&a - 1".
Ну это ясно то.
Цитата Сообщение от Evg Посмотреть сообщение
Если ты имел в виду порядок распределения в памяти переменных a и b - то только что пояснил
Нет, я имел другое. Позже отпишусь.
0
alex_x_x
бжни
2455 / 1662 / 134
Регистрация: 14.05.2009
Сообщений: 7,162
08.03.2012, 14:37 8
Цитата Сообщение от Evg Посмотреть сообщение
volatile нужно использовать в тех местах, когда есть доступ к переменным, который компилятору не виден (типа аппаратных портов ввода-вывода). Это ведь явно не тот случай
насколько я помню, его используют в случаях, когда происходит неявное обращение к той или иной переменной и нужно учесть это при оптимизации
в данном случае обращение к b неявное, и судя по дизасму программы компилятор не учитывает, что b может измениться, и собсно поэтому хранит ее значение в регистре, хотя в памяти она меняется

Добавлено через 13 секунд
go, да
0
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
08.03.2012, 14:58  [ТС] 9
alex_x_x, в следующем примере обращение к "b" тоже неявное, но ведь подобного косяка нет

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
 
char b = 10;
char *pb = &b;
 
int main (void)
{
  char tmp;
 
  *pb = 11;
 
  tmp = b;
 
  printf ("tmp = %d\n", tmp);
  printf ("b = %d\n", b);
  return 0;
}
0
alex_x_x
бжни
2455 / 1662 / 134
Регистрация: 14.05.2009
Сообщений: 7,162
08.03.2012, 15:12 10
Evg, не согласен, тут все вполне явно и b не модифицируется через неявную арифметику указателей
ну вообщем, в любом случае в хорошем коде как в примере делать нельзя )
0
retmas
Жарю без масла
865 / 747 / 225
Регистрация: 13.01.2012
Сообщений: 1,702
08.03.2012, 15:53 11
тут по сути попытка модификации констант не?

Добавлено через 1 минуту
при оптимизации компилятор подставляет именно значения констант
0
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
08.03.2012, 16:15  [ТС] 12
Цитата Сообщение от alex_x_x Посмотреть сообщение
Evg, не согласен, тут все вполне явно и b не модифицируется через неявную арифметику указателей
Чем принципиально "*pb = 11" отличается от "*(&a+1) = 11"? В общем случае указатель pb у тебя будет проинициализирован извне (т.е. в функции из другого модуля). Чем для компилятора обращение по указателю pb принципиально отличается от обращения по указателю (&a+1)?

Цитата Сообщение от alex_x_x Посмотреть сообщение
ну вообщем, в любом случае в хорошем коде как в примере делать нельзя
В оригинальном примере было что-то типа

C
1
2
3
delta = &b - &a;
...
*(&a + delta) = 11;
Просто тот компилятор, которым пользовались те, у кого возникла ситуация, был более умным и оптимизации отработали даже в таком случае. Я не смог подобрать другой общедоступный компилятор, который бы смог соптимизировать такую ситуацию и потому пришлось извращаться с константами. Давай тогда перепишу пример в другом виде. Ты скорее всего не сможешь повторить эту ошибку, т.к. gcc на нём не оптимизирует (хотя хз как поведут себя microsoft или borland)

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
 
char a = 5;
char b = 10;
 
int main (void)
{
  char tmp;
  long delta = &b - &a;
 
  /* Записываем в переменную "b" */
  *(&a + delta) = 11;
 
  /* Читаем из переменной "b" */
  tmp = b;
 
  printf ("tmp = %d\n", tmp);
  printf ("b = %d\n", b);
  return 0;
}
Где кроется ошибка в данном примере?

Цитата Сообщение от retmas Посмотреть сообщение
тут по сути попытка модификации констант не?
Где?

Цитата Сообщение от retmas Посмотреть сообщение
при оптимизации компилятор подставляет именно значения констант
Куда подставляет? Почему после операции "tmp = b" значения b и tmp оказываются разными?
0
alex_x_x
бжни
2455 / 1662 / 134
Регистрация: 14.05.2009
Сообщений: 7,162
08.03.2012, 16:23 13
Цитата Сообщение от Evg Посмотреть сообщение
Чем принципиально "*pb = 11" отличается от "*(&a+1) = 11"? В общем случае указатель pb у тебя будет проинициализирован извне (т.е. в функции из другого модуля). Чем для компилятора обращение по указателю pb принципиально отличается от обращения по указателю (&a+1)?
тем, что в pb явно кладется указатель на b
в оригинальном случае указатель получается путем арифметики с адресами
Цитата Сообщение от Evg Посмотреть сообщение
Где кроется ошибка в данном примере?
этот у меня как раз правильно отработал
а вот первоначальный - нет

Добавлено через 2 минуты
Evg, те по сути если мы в коде сделаем
int* ptr = rand();
*ptr = 11;
то компилятор практически не сможет в коде вокруг этой конструкции ничего оптимизировать (относительно хранения переменных в регистрах)
0
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
08.03.2012, 16:30  [ТС] 14
Цитата Сообщение от alex_x_x Посмотреть сообщение
этот у меня как раз правильно отработал
Я уже говорил, что он отработал из-за того, что оптимизация отработала недостаточно агрессивно и эффекта, как в оригинальном примере не получилось. Те, у кого изначально возникла эта проблема, пользовались более продвинутым компилятором (но машина была не Intel и компилятор не бесплатный). Пример из поста #12 содержит ровно такую же ошибку

Цитата Сообщение от alex_x_x Посмотреть сообщение
тем, что в pb явно кладется указатель на b
в оригинальном случае указатель получается путем арифметики с адресами
Разобьём пример на два файла

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
 
char b = 10;
 
extern char* func();
 
int main (void)
{
  char tmp;
  chat *pb = func();
 
  *pb = 11;
 
  tmp = b;
 
  printf ("tmp = %d\n", tmp);
  printf ("b = %d\n", b);
  return 0;
}
C
1
2
extern char b;
char* func () { return &b; }
При компиляции первого модуля компилятор не видит никаких явных или неявных взятий адресов на переменную "b". Тем не менее этот пример всегда будет работать правильно

Добавлено через 1 минуту
Цитата Сообщение от alex_x_x Посмотреть сообщение
Evg, те по сути если мы в коде сделаем
int* ptr = rand();
*ptr = 11;
то компилятор практически не сможет в коде вокруг этой конструкции ничего оптимизировать (относительно хранения переменных в регистрах)
Не сможет. И в этом примере компилятор чтение из "b" никогда не переставит с записью в "*ptr". Но в оригинальном примере, как ты уже видел, он переставил. Так чем для компилятора оригинальный пример отличается от этого? Почему оригинальный пример является ошибочным, а твой коротенький - нет
0
NoMasters
Псевдослучайный
1912 / 1123 / 90
Регистрация: 13.09.2011
Сообщений: 3,181
08.03.2012, 16:40 15
В любом случае нельзя делать никаких предположений насчёт взаимного расположения элементов из не непрерывных областей памяти, так что не очень понятно о чём спор...
0
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
08.03.2012, 16:56  [ТС] 16
Лучший ответ Сообщение было отмечено как решение

Решение

Цитата Сообщение от NoMasters Посмотреть сообщение
В любом случае нельзя делать никаких предположений насчёт взаимного расположения элементов из не непрерывных областей памяти, так что не очень понятно о чём спор...
Когда вводится операция "delta = &a - &b;", то ты не делаешь никаких предположений. Ты это вычисляешь как факт (а не предположение). Далее, запустив конкретный пример на конкретном компиляторе и посмотрев на код ты опять работаешь с фактом, а не с предположением. Ну а сказать про то что нельзя делать какие-то предположения - это просто попытка отмахнуться, не пытаясь вникнуть в суть. Кстати. спора тут нет

Как оно на самом деле
По стандарту языка Си адресная арифметика "адрес + целое" определена только в тех случаях, когда и "адрес" и результат операции являются адресами внутри одного и того же объекта (переменной языка). В нашем случае значение "&a + 1" вываливается за границу переменной "a", а потому результат такой операции неопределён.

Данным свойством стандарта руководствуется компилятор при оптимизациях на современных процессорах, которые имеют возможность параллельного исполнения инструкций. Современные Intel'овские процессоры являются суперскалярами, которые состоят из нескольких исполняющих устройств, позволяющих исполнять операции в параллель. Для подобных процессоров компилятор пытается переупорядочить порядок исполнения операций, чтобы более долгие по времени исполнения операции исполнились как можно раньше (а в пвраллель будут исполняться более быстрые операции). Разумеется, такие перестановки допутимы только в тех случаях, когда компилятор может доказать, что перестановка операций не приведёт к тому, что изменится результат программы.

Операция load (чтение из памяти) в современных процессорах является самой медленной операцией, потому как в случае отсутсвия данных в кэше операция исполняется чуть ли не 100 тактов. Это обусловлено сильно различающейся тактовой частоты процессора и памяти (а так же накладные расходы на работу контроллера памяти). Поэтому компилятор старается load'ы вытащить вперёд по исполнению. При этом требуется проверка на то, а не конфликтуют ли load'ы со store'ами (операциями записи в память). Т.е. если емеется операция записи в память, которая пишет в ту же ячейку памяти или возможно пишет в ту же ячейку памяти, то переставлять её с операцией load нельзя. В нашем случае компилятор видит операцию записи в память по адресу "&a + 1" и операцию чтения из памяти по адресу "&b". Пользуясь указанным выше свойтсвом стандарта компилятор, немного его перефразируя, трактует как "операции чтения и записи в разные объекты никогда не конфликтуют между собой и их всегда можно переставлять". Другими словами, если операции чтения и записи зацеплены за разные объекты, то при любой адресной арифметике можно считать, что их можно переставлять. Но это не так, если мы имеем дело, к примеру, с массивом. В этом случае чтение элемента a[i] можно переставлять с записью элемента a[j] только в том случае, если компилятор сможет доказать, что во всех случаях i != j. Поэтому в нашем примере компилятор переставил местами операторы "*(&a + 1) = 11;" и "tmp = b;", поскольку с точки зрения стандарта он имеет на это право. Ну и если кто-то посмотрит построенный код, то в явном виде это дело увидит. А вот когда есть просто указатель, взявшийся из какой-нибудь внешней функции, то компилятор НЕ имеет права переставлять store по этому указателю с любым load'ом, потому как он не сможет доказать, что эти обращения в память не конфликтуют между собой

История данного примера была следующая. Давным-давно имелась программа, написанная на ассемблере. В программе были два массива "a" и "b" одинакового размера. Была некая функция, которая выполняла симметричную обработку этих массивов. Поскольку на ассемблере программист сам располагает объекты в памяти, то он сам гарантирует и разницу адресов между объектами. Поэтому для обработки массивов был написан код, в который на регистре передаётся адрес массива "a" через который можно достучаться до объекта "b". Таким образом, не нужно было дополнительного регистра для хранения адреса "b" (т.е. сэкономили на регистрах). Смысл кода был примерно такой (для простоты я его напишу на Си, хотя он был написан на ассемблере):

C
1
2
3
4
5
6
7
8
9
10
11
12
13
int a[10];
int b[10];
 
void func (int *p) /* сюда передаётся адрес массива "a" */
{
  /* В качестве примера просто обнулим оба массива, но реальная
   * логика была более сложная */
  for (i = 0; i < 10; i++)
  {
    p[i] = 0;
    p[i+(&b - &a)] = 0;
  }
}
Далее этот код решили аз ассемблера переписать на Си. Ну и переписали с сохранением этого свойства. Это дело долго работало на 486-м процессоре, который НЕ был суперскаляром, а потому компилятор не занимался перестановкой load'ов и store'ов. А потом при перекомпиляции этого исходника под современные процессоры и возникла ошибка, которую очень долго не могли отловить. В режиме с возможностью символьной отладки из отладчика (т.е. по сути без оптимизаций) ошибка не проявлялась. Внесение дополнительных отладочных печатей приводило к тому, что ошибка не проявлялась. А ковыряться в коде оказалось длительным и сложным процессом
8
NoMasters
Псевдослучайный
1912 / 1123 / 90
Регистрация: 13.09.2011
Сообщений: 3,181
08.03.2012, 17:03 17
Цитата Сообщение от Evg Посмотреть сообщение
ты не делаешь никаких предположений
Делаю. Именно потому, что компилятор имеет полное право переставить не непрерывные куски памяти любым удобным для себя способом даже во время исполнения. Собственно, в некотором смысле, это и происходит.
0
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
08.03.2012, 17:07  [ТС] 18
Цитата Сообщение от NoMasters Посмотреть сообщение
Именно потому, что компилятор имеет полное право переставить не непрерывные куски памяти любым удобным для себя способом даже во время исполнения
Ну переставил он удобным для себя способов. В итоге переменная delta вместо 1 стала равна -1, 10, 100 или сколько угодно. Операцией (&a + delta) делается обратное действие и получается точный адрес переменной b. Ну сделали мы вместо переменной delta константу -1, 10, 100 или что там получилось. Это никак не объясняет причину того, что после операции "tmp = b" получились разные значения в переменных "tmp" и "b".
0
NoMasters
Псевдослучайный
1912 / 1123 / 90
Регистрация: 13.09.2011
Сообщений: 3,181
08.03.2012, 17:12 19
Evg, delta может меняться в процессе исполнения.
0
Evg
Эксперт CАвтор FAQ
19631 / 7323 / 551
Регистрация: 30.03.2009
Сообщений: 20,485
Записей в блоге: 30
08.03.2012, 17:22  [ТС] 20
Цитата Сообщение от NoMasters Посмотреть сообщение
Evg, delta может меняться в процессе исполнения.
Ну-ну

Из квантовой теории следует, что если я с разбегу долбанусь в стену, то существует ненулевая вероятность того, что я пройду сквозь стену из-за туннельного эффекта. Если я напишу код "int x = 1000;" то есть шанс, что он отработает неправильно из-за того, что на каком-то компиляторе размер int'а равен 1, или на процессоре байт состоит из 2 битов, а не из 8, или ещё что-то. Могу привести ещё кучу примеров, которые в данном случае будут неуместны (как и твоё возражение)
0
08.03.2012, 17:22
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
08.03.2012, 17:22

Указатели и адресная арифметика
Помогите с заданием Язык C. Задание звучит так: 2) Для этого фрагмента...

Указатели, адресная арифметика
Ребята, помогите плиз вот с таким вот заданием... Написать программу,...

Адресная арифметика: поиск max элемента массива
Здравствуйте! Это программа находит максимальный элемент из введеного массива....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru