Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   С/С++, C#, Delphi, .NET, Asm (https://forum.antichat.xyz/forumdisplay.php?f=24)
-   -   [C#] Из жизни багов: байты в циклах (https://forum.antichat.xyz/showthread.php?t=164218)

Algol 17.12.2009 14:42

[C#] Из жизни багов: байты в циклах
 
МиниКвест.
Недавно выловил такой вот баг в программе:
Требовалось перебрать все значения байта от 0 до 255. Как оказалось, это сделать не так-то просто :) Очевидный, на первый взгляд код, не работает:

Код:

for (byte b = 0; b <= 255; b++)
      Console.WriteLine(b);

Кто скажет почему не работает этот код? (желательно без компиляции :) )

slesh 17.12.2009 15:07

так там после 255 идет 0
т.е. если будет число 255, то оно проверится по условию. цикл выполнится, далее число увеличится на 1. А так как это байт, то оно станет = 0 и тогда опять процдет проверку.

достаточно было бы поставить < 256 чтобы исключить такое.

Это собственно говоря проблема любого языка.

slesh 17.12.2009 15:17

А да точно. туплю. Просто не ткомпилятора под рукой чтобы проверить. тогда типа так:
Код:

for (byte b = 0; b <= 255; b++)
{
      Console.WriteLine(b);
      if (b == 255) break;
}

но вообще в данных условиях лучше просто расширить переменную хотябы до short

Algol 17.12.2009 17:30

Цитата:

Сообщение от slesh
достаточно было бы поставить < 256 чтобы исключить такое.

Это вообще говоря некорректно, потому как 256 выходит за рамки байта, и сравнивать с байтом - не очень хорошо (компилятор морщится на это варнингом). Ну и кроме того, как уже сказали, это не решает проблему.
Цитата:

Сообщение от slesh
Это собственно говоря проблема любого языка.

А вот здесь ты не прав. На delphi аналогичный код работает на ура
Код:

procedure TForm1.Button1Click(Sender: TObject);
var i:byte;
begin
 for i:=0 to 255 do
    self.text := intToStr(i);
end;


Algol 17.12.2009 17:33

Цитата:

Сообщение от dober.man
Algol, все нормально, смотри что я придумал:
Код:

for (byte i = 0, j = 255; i < j; i++)
    System.Console.WriteLine(i);

Работает! =)

Твой код выводит не все значения байта, он не выводит 255 :)

.Slip 17.12.2009 17:55

Algol, в C-like во всех так будет. Это не бага ЯП, это бага программиста.

Algol 17.12.2009 18:05

Цитата:

Сообщение от .Slip
Algol, в C-like во всех так будет. Это не бага ЯП, это бага программиста.

И ?
Я разве спрашивал/утверждал чей это баг?

scrat 17.12.2009 23:48

Цитата:

Сообщение от Algol
МиниКвест.
Недавно выловил такой вот баг в программе:
Требовалось перебрать все значения байта от 0 до 255. Как оказалось, это сделать не так-то просто :) Очевидный, на первый взгляд код, не работает:

Код:

for (byte b = 0; b <= 255; b++)
      Console.WriteLine(b);

Кто скажет почему не работает этот код? (желательно без компиляции :) )

тут как-то замешано то, что все типы в .net основаны на object, и byte - это не привычные 8 битов.

Решение: Использовать CompareTo()

рабочий пример:
Код:

byte b = 0x0;
        for (; b.CompareTo(0xFF)<0 ; b++) Console.WriteLine("{0}",b);


Algol 18.12.2009 01:00

Цитата:

Сообщение от scrat
рабочий пример:
Код:

byte b = 0x0;
        for (; b.CompareTo(0xFF)<0 ; b++) Console.WriteLine("{0}",b);


Ну какой же он рабочий, если он выводит только числа до 254, а где 255 ? :)

W!z@rD 18.12.2009 12:27

Цитата:

Сообщение от Algol
Ну какой же он рабочий, если он выводит только числа до 254, а где 255 ? :)

for (byte i = byte.MaxValue; i > byte.MinValue; i--)
{
Console.WriteLine(byte.MinValue + i);
}

rofl, теперь нет нуля? :)))))

desTiny 18.12.2009 19:20

а с char'ом ещё смешнее - он ещё и signed :)
я так обходил
Код:

UCHAR b = 0;
do{
...
}while(--b); // или ++


.Slip 19.12.2009 11:39

Цитата:

byte b = 0;
do
{
Console.WriteLine(b++);
} while (b != 0);
В цикле for такого в любом случае не сделать.

Forcer 19.12.2009 12:19

Цитата:

Сообщение от .Slip
В цикле for такого в любом случае не сделать.

Можно добавить дополнительное условие - тогда будет работать.

scrat 19.12.2009 19:52

Цитата:

Сообщение от Algol
Ну какой же он рабочий, если он выводит только числа до 254, а где 255 ? :)

ой, я почему-то был уверен в том, что оно печатается. Как-то это всё очень странно.

Markus_13 19.12.2009 20:01

а в JavaScript все работает:
Код:

for(b=0;b<=255;b++)document.write(b);
:D

Algol 19.12.2009 20:10

Цитата:

Сообщение от Markus_13
а в JavaScript все работает:
Код:

for(b=0;b<=255;b++)document.write(b);
:D

А в джаваскрипте есть тип byte ?

M_script_ 19.12.2009 20:22

Вариант 1
Код:

byte b = 0;
do
      Console.WriteLine(b++);
while(b);

Вариант 2
Код:

byte b = 0;
do
      Console.WriteLine(b);
while(++b);


Irdis 19.12.2009 20:41

Код:

    static bool print(byte b)
    {
      Console.WriteLine(b);
      return true;
    }
    static void Main(string[] args)
    {
      for (byte b = 0; (print(b++)) && (b != 0); );
    }

опа...
в плюсах можно ещё красивее сделать

Markus_13 19.12.2009 21:28

Цитата:

А в джаваскрипте есть тип byte ?
А в JS есть типы? Оо :D

Algol 19.12.2009 21:51

Цитата:

Сообщение от Markus_13
А в JS есть типы? Оо :D

Есть

Irdis 19.12.2009 22:06

Код:

        for (unsigned char b = 0; (std::cout<<(int)b++<<"\n")&&(b!=0); );
тоже самое на плюсах

M_script_ 19.12.2009 22:26

Цитата:

Сообщение от Irdis
Код:

        for (unsigned char b = 0; (std::cout<<(int)b++<<"\n")&&(b!=0); );
тоже самое на плюсах

Код:

for (byte b = 0; (std::cout << b++ << std::endl) && b; );
P.S.: не помню, как в VS, но в билдере по умолчанию typedef unsigned char byte; есть

Markus_13 19.12.2009 22:34

Цитата:

Сообщение от Algol
Есть

не, конечно не спорю что JS разделяет переменные по типам, но насколько я помню в самом коде переменные не объявляются - т.к. JS работает с переменными взависимости от обращения, если я напишу:
Цитата:

var s;
s=2+3;
s=s+"0";
s=document.getElementById("someID");
то сначала перем-ая s будет обрабатываться как число, затем как строка, а затем и вовсе как объект... :rolleyes:

M_script_ 19.12.2009 22:41

Algol, миниквест пора заканчивать. Говори правильный ответ. Мне кажется постпроверка с префиксным инкрементом в этом случае оптимальный вариант. По крайней мере по количеству используемой памяти )

Markus_13 19.12.2009 23:08

SitraIT, ыыы, типа вот этого чтоли:
Код:

function MyClass(){
this.name="Some";
}
var s=new MyClass();

хотя я с JS работаю весьма недолго - я знаю это, но я и не писал что там нельзя этого делать ;)
З.Ы. вообще имхо это не классы и объекты, а костыли)))

scrat 20.12.2009 00:44

по-моему ответ где-то тут
Код:

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size      47 (0x2f)
  .maxstack  2
  .locals init (uint8 V_0,
          bool V_1)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  br.s      IL_001b
  IL_0005:  ldstr      "{0}"
  IL_000a:  ldloc.0
  IL_000b:  box        [mscorlib]System.Byte
  IL_0010:  call      void [mscorlib]System.Console::WriteLine(string,
                                                                object)
  IL_0015:  nop
  IL_0016:  ldloc.0
  IL_0017:  ldc.i4.1
  IL_0018:  add
  IL_0019:  conv.u1
  IL_001a:  stloc.0
  IL_001b:  ldloca.s  V_0
  IL_001d:  ldc.i4    0xff
  IL_0022:  call      instance int32 [mscorlib]System.Byte::CompareTo(uint8)
  IL_0027:  ldc.i4.0
  IL_0028:  clt
  IL_002a:  stloc.1
  IL_002b:  ldloc.1
  IL_002c:  brtrue.s  IL_0005
  IL_002e:  ret
} // end of method c::Main


Algol 20.12.2009 02:35

Цитата:

Сообщение от M_script_
Algol, миниквест пора заканчивать. Говори правильный ответ. Мне кажется постпроверка с префиксным инкрементом в этом случае оптимальный вариант. По крайней мере по количеству используемой памяти )

Эээ... А разве здесь может быть правильный ответ :rolleyes:
Правильный - любой который работает :)
Лично я бы писал так как предлагал slesh:
Код:

for(byte b=0;b<=255;b++)
{
  Console.WriteLine(b);
  if(b==255) break;
}

Некрасиво конечно, но по другому не получится.

Еще мне понравился вариант desTiny, что на C# выглядело бы типа такого
Код:

byte b = 0;
do
      Console.WriteLine(b);
      b++;
while(b!=0);

Он конечно содержит минимум операторов, но у него есть недостаток - он в общем случае не работает, если границы перебора заранее не известны.

Да и кстати, вообще-то я спрашивал почему же исходный код не работает, а все почему то начали приводить примеры работающих кодов :D

Раз никто не ответил, отвечу сам:
На самом деле, в си-подобных языках нет оператора for в чистом виде. Фактически for это цикл while, для которого указано условие продолжения (b<=255) и некторый оператор, изменяющий b (b++).
Причем условние продолжения цикла проверяется на каждой итерации.
Когда значение b достигает 255, цикл отрабатывает, и далее выполняется операция b++, результатом которой является снова 0! Далее проверяется условие b<=255 - оно очевидно выполняется, и цикл идет дальше, таким образом зацикливаясь в бесконечность.
Для си в принципе это поведение нормальное, однако для C# лично мне это кажется не очень логичным поведением. Логичнее было бы на 255++ генерировать исключение (что то типа OverflowException) - ведь фктически происходит переполение. Но нет, он тихо сбрасывается в ноль и идет себе дальше :(
Точно такая же ситуация возникает и для всех остальных целочисленных типов. Например цикл
for(int i=0;i<=int.MaxInt;i++) - тоже зацикливается.
Получается что стандартный код типа
for(int i=1;i<=arr.Count;i++)
является небезопасным, так как если arr.Count==int.MaxInt, то цикл будет бесконечным :[

Irdis 20.12.2009 02:50

спасибо кэп...

Root-access 30.12.2009 16:44

Мне это напомнило такой код на с:
Код:

int a[10], i;
for(i=0;i<=10;i++) a[i]=0;

На старых компиляторах он уходил в бесконечный цикл.
Вот и задачка. А ну-ка скажите, почему это происходит? ;)
Кстати, если чуть подправить код, программа и в новых компиляторах будет получаться вечная.

scrat 30.12.2009 18:34

Цитата:

Сообщение от Algol
Раз никто не ответил, отвечу сам:
На самом деле, в си-подобных языках нет оператора for в чистом виде. Фактически for это цикл while, для которого указано условие продолжения (b<=255) и некторый оператор, изменяющий b (b++).
Причем условние продолжения цикла проверяется на каждой итерации.
Когда значение b достигает 255, цикл отрабатывает, и далее выполняется операция b++, результатом которой является снова 0! Далее проверяется условие b<=255 - оно очевидно выполняется, и цикл идет дальше, таким образом зацикливаясь в бесконечность.
Для си в принципе это поведение нормальное, однако для C# лично мне это кажется не очень логичным поведением. Логичнее было бы на 255++ генерировать исключение (что то типа OverflowException) - ведь фктически происходит переполение. Но нет, он тихо сбрасывается в ноль и идет себе дальше :(
Точно такая же ситуация возникает и для всех остальных целочисленных типов. Например цикл
for(int i=0;i<=int.MaxInt;i++) - тоже зацикливается.
Получается что стандартный код типа
for(int i=1;i<=arr.Count;i++)
является небезопасным, так как если arr.Count==int.MaxInt, то цикл будет бесконечным :[

предлагаю обратиться с этим на формуы msdn или к разработчикам платформы .net

jawbreaker 30.12.2009 18:46

Дружно учим C#:
PHP код:

checked
{
    for (
byte b 0<= 255b++)
    
Console.WriteLine(b);



Retimiled 30.12.2009 19:17

каким образом Algol вывел логическую цепочку от целеноправленного введения for в бесконечны цикл.... до отсутствия реализации for как цельной структуры а не цикла while не понятно (для программиста компилятора это узкоспециализированная задача и реализовывать он ее может в соответствии с быстродействием или оптимизацией памяти, никто же не заставляет его использовать реализацию while)
http://i010.radikal.ru/0912/ff/071f1fb29023.jpg
... по вашей логике все в этом мире сведется к циклу loop (ассемблера) а все остальное от лукавого?????? :D

цикл for в примере algol-а не на миг не ушел от описния Керниган Ричи....(не делали же списиально списифического описания for для шарпа) дак что же задавать вопрос о том "..а почему он не разворачивает конфеты?"

Вопросы переполнения переменных это азы Си 8)))
Школьники многих школ .... где преподают Си вместо Паскаля ВАМ ПОДТВЕРДЯТ ЭТО :D :p

для С# не придумывали новый синтаксис ни для 'for' ни для if ни для while или until, используется стандартные функции подходящие под описание языка Си и мне программировать совершенно без разницы на TurboC MSVC Net или шарпе без разницы что касается основных структур языка а если я хочу переполнять .... ПОЧЕМУ РАЗРАБОТЧИК КОМПИЛЯТОРА должен запретить мне в соответствии с синтаксисом переполнять переменную чтоб превратить ее в ноль?

такой прием я видел дважды один раз в реализаци победителя конференции AES - блочном шифровании 2fish...

каждый можете сделать аналог for
и понять разницу
между
while(... ) {.....}
и do {....} while(....);
только тот кто использует for 8))) и сам знает как им пользоваться В СООТВЕТСВИИ С ОПИСАНИЕМ 8))!

Algol 30.12.2009 22:36

Цитата:

Сообщение от Root-access
Мне это напомнило такой код на с:
Код:

int a[10], i;
for(i=0;i<=10;i++) a[i]=0;

На старых компиляторах он уходил в бесконечный цикл.
Вот и задачка. А ну-ка скажите, почему это происходит? ;)
Кстати, если чуть подправить код, программа и в новых компиляторах будет получаться вечная.

Память под a[] не выделяется?

Algol 30.12.2009 22:41

Цитата:

Сообщение от jawbreaker
Дружно учим C#:
PHP код:

checked
{
    for (
byte b 0<= 255b++)
    
Console.WriteLine(b);



Это конечно здорово, но в подавляющем числе случаев - checked не используется, а описанное поведение является неожиданным для программиста.

Root-access 30.12.2009 23:04

Цитата:

Сообщение от Algol
Память под a[] не выделяется?


Почему же? Выделяется вполне...

Algol 30.12.2009 23:10

Цитата:

Сообщение от Retimiled
каким образом Algol вывел логическую цепочку от целеноправленного введения for в бесконечны
.....

Из всего этого набора слов (отмечаешь наверное уже :D ) я так понял ты критикуешь мою фразу о том что "в си-подобных языках нет оператора for в чистом виде" ?
Могу объяснить более подробно, что и имел ввиду:
1) Цикл for в Си действительно органозован через while. И даже не столько в синтаксическом плане, сколько в логическом. На каждой итерации - проверяется некое условие, и выполняется некое действие. По сути - это while, просто синтаксически оформленный иначе. Но, если мы посмотрим на теорию программирования (безотносително к какому-либо языку), то смысл for - совсем другой. Смысл for - повторить свое тело заданное число раз. Причем некоторые языки дают даже более жесткое определение - повторение своего тела определенное и заданное наперед число раз. Именно такое поведение реализуется в Паскале (Школьники многих школ .... где преподают Паскаль вместо Си ВАМ ПОДТВЕРДЯТ ЭТО :D ). И это правильно, ибо это ближе к изначальному определению for. В Си же (как и во многих других вещах) решили не париться и сделали for через while.
А приведенный выше пример как раз и демонстрирует недостаток такого подхода. В Паскале же этот цикл работает без проблем. Он и не может не работать, ведь смысл конструкции for i:=0 to 255 do; в том, что i последовательно принимает значения от 0 до 255, и никаких i++ тут нет, поэтому и переполнения тут никакого быть не может в принципе. Конечно, где-то там внутри , в откомпилированном exe i++ где-то есть , но отсутсвие побочных эффектов, переполения нам гарантирует компилятор, и мы об этом не должны думать.
Надеюсь понятно изложил? Да и к тому же фразу о том, что в Си по факту нет for - придумал не я. Слышал давно очень, уже и не вспомню откуда :)

2) Касаемо C#. Да, сишарп по синтаксису близок к Си, но семантика и идеология языка - гораздо ближе к Паскалю, чем к Си. Именно поэтому я и написал что си-подобное поведение шарпа в данном случае не очень ожидаемо.

Algol 30.12.2009 23:12

Цитата:

Сообщение от Root-access
Почему же? Выделяется вполне...

Я просто не пишу на Си, но мне казалось что под массив нужно явно выделить память. Нет ?

Ra$cal 30.12.2009 23:20

память под а выделяется. тока она на стеке. а цикл идет <= 10, компиль видимо генерит так, что переменная i лежит за массивом(a[10] указывает на i). таким образом i при значении 10 затирается нулем, с помощью выхода за границы массива. видимо как то так

Цитата:

Фактически for это цикл while, для которого указано условие продолжения (b<=255) и некторый оператор, изменяющий b (b++).
а вот это бред. тогда уж скорее while - это for, у кторого не задан первый и последний параметр типа

Код:

for(; b < 255;){}

Algol 30.12.2009 23:23

Цитата:

Сообщение от Ra$cal
память под а выделяется. тока она на стеке. а цикл идет <= 10, компиль видимо генерит так, что переменная i лежит за массивом(a[10] указывает на i). таким образом i при значении 10 затирается нулем, с помощью выхода за границы массива. видимо как то так

Ясно, ну здесь уже проблема с отсутствием контроля границ массива. В шарпе и паскале тут бы выпал эксепшен.

Retimiled 30.12.2009 23:37

8)) ... ну еще раз повторюсь .... програмисты используют особенности работы for (в Си понимании) .... кароче ВСЕХ С НОВЫМ ГОДОМ !! :p


Время: 04:53