Просмотр полной версии : [C#] Из жизни багов: байты в циклах
МиниКвест.
Недавно выловил такой вот баг в программе:
Требовалось перебрать все значения байта от 0 до 255. Как оказалось, это сделать не так-то просто :) Очевидный, на первый взгляд код, не работает:
for (byte b = 0; b <= 255; b++)
Console.WriteLine(b);
Кто скажет почему не работает этот код? (желательно без компиляции :) )
так там после 255 идет 0
т.е. если будет число 255, то оно проверится по условию. цикл выполнится, далее число увеличится на 1. А так как это байт, то оно станет = 0 и тогда опять процдет проверку.
достаточно было бы поставить < 256 чтобы исключить такое.
Это собственно говоря проблема любого языка.
А да точно. туплю. Просто не ткомпилятора под рукой чтобы проверить. тогда типа так:
for (byte b = 0; b <= 255; b++)
{
Console.WriteLine(b);
if (b == 255) break;
}
но вообще в данных условиях лучше просто расширить переменную хотябы до short
достаточно было бы поставить < 256 чтобы исключить такое.
Это вообще говоря некорректно, потому как 256 выходит за рамки байта, и сравнивать с байтом - не очень хорошо (компилятор морщится на это варнингом). Ну и кроме того, как уже сказали, это не решает проблему.
Это собственно говоря проблема любого языка.
А вот здесь ты не прав. На delphi аналогичный код работает на ура
procedure TForm1.Button1Click(Sender: TObject);
var i:byte;
begin
for i:=0 to 255 do
self.text := intToStr(i);
end;
Algol, все нормально, смотри что я придумал:
for (byte i = 0, j = 255; i < j; i++)
System.Console.WriteLine(i);
Работает! =)
Твой код выводит не все значения байта, он не выводит 255 :)
Algol, в C-like во всех так будет. Это не бага ЯП, это бага программиста.
Algol, в C-like во всех так будет. Это не бага ЯП, это бага программиста.
И ?
Я разве спрашивал/утверждал чей это баг?
МиниКвест.
Недавно выловил такой вот баг в программе:
Требовалось перебрать все значения байта от 0 до 255. Как оказалось, это сделать не так-то просто :) Очевидный, на первый взгляд код, не работает:
for (byte b = 0; b <= 255; b++)
Console.WriteLine(b);
Кто скажет почему не работает этот код? (желательно без компиляции :) )
тут как-то замешано то, что все типы в .net основаны на object, и byte - это не привычные 8 битов.
Решение: Использовать CompareTo() (http://msdn.microsoft.com/en-us/library/system.byte.compareto(VS.71).aspx)
рабочий пример:
byte b = 0x0;
for (; b.CompareTo(0xFF)<0 ; b++) Console.WriteLine("{0}",b);
рабочий пример:
byte b = 0x0;
for (; b.CompareTo(0xFF)<0 ; b++) Console.WriteLine("{0}",b);
Ну какой же он рабочий, если он выводит только числа до 254, а где 255 ? :)
Ну какой же он рабочий, если он выводит только числа до 254, а где 255 ? :)
for (byte i = byte.MaxValue; i > byte.MinValue; i--)
{
Console.WriteLine(byte.MinValue + i);
}
rofl, теперь нет нуля? :)))))
а с char'ом ещё смешнее - он ещё и signed :)
я так обходил
UCHAR b = 0;
do{
...
}while(--b); // или ++
byte b = 0;
do
{
Console.WriteLine(b++);
} while (b != 0);
В цикле for такого в любом случае не сделать.
В цикле for такого в любом случае не сделать.
Можно добавить дополнительное условие - тогда будет работать.
Ну какой же он рабочий, если он выводит только числа до 254, а где 255 ? :)
ой, я почему-то был уверен в том, что оно печатается. Как-то это всё очень странно.
Markus_13
19.12.2009, 20:01
а в JavaScript все работает:
for(b=0;b<=255;b++)document.write(b); :D
а в 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);
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
А в JS есть типы? Оо :D
Есть
for (unsigned char b = 0; (std::cout<<(int)b++<<"\n")&&(b!=0); );
тоже самое на плюсах
M_script_
19.12.2009, 22:26
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
Есть
не, конечно не спорю что 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 работаю весьма недолго - я знаю это, но я и не писал что там нельзя этого делать ;)
З.Ы. вообще имхо это не классы и объекты, а костыли)))
по-моему ответ где-то тут
.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, миниквест пора заканчивать. Говори правильный ответ. Мне кажется постпроверка с префиксным инкрементом в этом случае оптимальный вариант. По крайней мере по количеству используемой памяти )
Эээ... А разве здесь может быть правильный ответ :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, то цикл будет бесконечным :[
Root-access
30.12.2009, 16:44
Мне это напомнило такой код на с:
int a[10], i;
for(i=0;i<=10;i++) a[i]=0;
На старых компиляторах он уходил в бесконечный цикл.
Вот и задачка. А ну-ка скажите, почему это происходит? ;)
Кстати, если чуть подправить код, программа и в новых компиляторах будет получаться вечная.
Раз никто не ответил, отвечу сам:
На самом деле, в си-подобных языках нет оператора 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#:
checked
{
for (byte b = 0; b <= 255; b++)
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))!
Мне это напомнило такой код на с:
int a[10], i;
for(i=0;i<=10;i++) a[i]=0;
На старых компиляторах он уходил в бесконечный цикл.
Вот и задачка. А ну-ка скажите, почему это происходит? ;)
Кстати, если чуть подправить код, программа и в новых компиляторах будет получаться вечная.
Память под a[] не выделяется?
Дружно учим C#:
checked
{
for (byte b = 0; b <= 255; b++)
Console.WriteLine(b);
}
Это конечно здорово, но в подавляющем числе случаев - checked не используется, а описанное поведение является неожиданным для программиста.
Root-access
30.12.2009, 23:04
Память под a[] не выделяется?
Почему же? Выделяется вполне...
каким образом 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#. Да, сишарп по синтаксису близок к Си, но семантика и идеология языка - гораздо ближе к Паскалю, чем к Си. Именно поэтому я и написал что си-подобное поведение шарпа в данном случае не очень ожидаемо.
Почему же? Выделяется вполне...
Я просто не пишу на Си, но мне казалось что под массив нужно явно выделить память. Нет ?
память под а выделяется. тока она на стеке. а цикл идет <= 10, компиль видимо генерит так, что переменная i лежит за массивом(a[10] указывает на i). таким образом i при значении 10 затирается нулем, с помощью выхода за границы массива. видимо как то так
Фактически for это цикл while, для которого указано условие продолжения (b<=255) и некторый оператор, изменяющий b (b++).
а вот это бред. тогда уж скорее while - это for, у кторого не задан первый и последний параметр типа
for(; b < 255;){}
память под а выделяется. тока она на стеке. а цикл идет <= 10, компиль видимо генерит так, что переменная i лежит за массивом(a[10] указывает на i). таким образом i при значении 10 затирается нулем, с помощью выхода за границы массива. видимо как то так
Ясно, ну здесь уже проблема с отсутствием контроля границ массива. В шарпе и паскале тут бы выпал эксепшен.
Retimiled
30.12.2009, 23:37
8)) ... ну еще раз повторюсь .... програмисты используют особенности работы for (в Си понимании) .... кароче ВСЕХ С НОВЫМ ГОДОМ !! :p
а вот это бред. тогда уж скорее while - это for, у кторого не задан первый и последний параметр
Да какая разница, что в лоб, что по лбу. Главное - что они семантически близки.
ЗЫ
[здесь начинается грубость]
Обрати внимание, я никогда, ни к тебе, и ни к кому либо другому не применял слова "бред", "глупость" и т.д. А вот от тебя (и от некоторых других товарищей) так и прет грубость и ненавсить к собеседнику. Так вот, в подъезде у себя будешь по фене ботать, и пальцы веером растопыривать, а мне - твои понты нахрен не нужны. Будешь продолжать в том же тоне - попадешь в бан. Надеюсь понятно объяснил ?
[/здесь я снова белый и пушистый]
Retimiled
31.12.2009, 00:02
согласен с algol на 100% .... всегда нужно уважать собеседника!
Особенно это заметно на том как кроют новичков... здесь много людей с низкими знаниями ... и если всех крыть ,то можно закончить как KEZ
... крыть у новичков нужно единственную весчь это их ЛЕНЬ!
Ra$cal , Algol
Дальних не судить,
Ближним помогать.
С тишиной сойтись На исходе дня(с)
Всех с наступающим и удачи .
[как всегда обычный режим]до чего ранимые люди встречаются. еще "базарить по фене" приплетают к разговору. все очень просто - если выраженная мысль бредова - значит я так и скажу. это не оскорбление. или мне писать "сударь, не серчайте на меня, но думается мне скромно, что мысль Ваша не очень корректна"? будьте проще. если я не прав - критикуйте. если бред говорю - так и пишите. аналогично ко всем. и да - градация есть. если говорю бред - значит не вижу никаких серьезных причин считать изложенное собеседником чем-то адекватным(а не просто правильным). как поиск подстроки в строке с помощью рекурсии например. не согласны - велкам. буду рад услышать любые доводы вашей правоты (адекватности утверждения).
по поводу новичков - есть границы, когда человек хочет чему-то научиться, прикладывая усилия, и когда хочет, чтоб его учили и давали ему код, а он только дает требования. первым - велкам. вторые - игнорирую, ибо время тратить жалко. но иногда уж слишком настырны, тогда приходится пытаца образумить в пару слов.[да, я всегда такой, поэтому тег не закрывается]
Retimiled
31.12.2009, 00:42
2 Ra$cal
....есть разные стили общения, все же я думаю что нейтральный ЭТО ЗАКОН ПУБЛИЧНОГО форума, либо нужно добавлять ИМХО после каждого предложения!
либо нужно добавлять ИМХО после каждого предложения!
о мой бог. на форуме люди собираются, чтобы обсуждать чтото, спорить, находить ответы. или может как в тру американских фильмецах будем каждому хлопать в ладоши после каждого откровения, как анонимные алкоголики. Не люблю я лицемерие. Не согласен с кем то - скажи. С тобой не согласны - обоснуй. Без всякого экстрима, банальный спор, рождающий истину. Хз откуда вы тут проблему нашли.
ps: закончили офтопик.
Retimiled
31.12.2009, 00:59
ну ... и ты прав Ra$cal но ведь слово БРЕД было вставкой не несущей смысла в твоем предложении .... про спор и отсаивание мнения никто не против!
2 Ra$cal
Ну вот, уже гораздо лучше :)
vBulletin® v3.8.14, Copyright ©2000-2026, vBulletin Solutions, Inc. Перевод: zCarot