Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2012.02.12;
Скачать: [xml.tar.bz2];

Вниз

Особенности инициализации переменных в C#   Найти похожие ветки 

 
И. Павел ©   (2011-10-18 16:12) [0]

Есть код на C#:
string s;
for (int i = 0; i < 1; i++)
{
 s = "test";
};
Console.WriteLine(s);


При компиляции выскакивает ошибка на последней строке: переменная s не инициализированна. В книжке говорят, что так и должно быть, но как-то неубедительно аргументируют.
Зачем разработчики C# решили выбрать такой подход? В других языкая я ничего подобного не встречал.

PS: Кстати, если for заменить на простой блок {} или на блок while или do с константным выполнением 1 раз, то инициализация во внутреннем блоке засчитывается. Это только for такой особенный? Не верю, что современный компилятор не способен определить, что цикл (int i = 0; i < 1; i++) будет выполнен строго 1 раз.


 
Вася   (2011-10-18 16:21) [1]


> При компиляции выскакивает ошибка на последней строке: переменная
> s не инициализированна.

Предупреждение?


 
И. Павел ©   (2011-10-18 16:25) [2]

> Предупреждение?

Нет. Отказывается компилироваться и запускаться. В окне "список ошибок" указано, что обнаружена 1 ошибка.


 
_Юрий   (2011-10-18 16:38) [3]


> что современный компилятор не способен определить, что цикл
> (int i = 0; i < 1; i++) будет выполнен строго 1 раз.


Вряд ли современный компилятор заточен под такие бессмысленные частные случаи.
А в общем случае он совершенно прав.


 
stas ©   (2011-10-18 16:44) [4]

Я вот что-то не помню чтобы в C# за "}" ставилась ";"


 
И. Павел ©   (2011-10-18 16:49) [5]

[4] stas ©   (18.10.11 16:44)
Просто я пробовал разные циклы для этого блока. В том числе и do. Условие завершения удалил, а ";" лсталась. Все равно арят ли она останется в MSIL и уж тем более не переведется в машинный код ;)


 
Kerk ©   (2011-10-18 16:50) [6]


> stas ©   (18.10.11 16:44) [4]

Это ничего не меняет, если мне не изменяет склероз.


 
И. Павел ©   (2011-10-18 16:50) [7]

арят ли -> вряд ли


 
Kerk ©   (2011-10-18 16:50) [8]

В C точка с запятой -- statement terminator, в Pascal -- statement separator. Поэтому в Delphi их нельзя ставить просто так, а в C можно.


 
stas ©   (2011-10-18 16:54) [9]

Объявляйте так:string s="";


 
icelex ©   (2011-10-18 16:55) [10]


> И. Павел ©   (18.10.11 16:12) 

абсолютно правильно говорит: у тебя цикл может быть не выполнен ни разу и переменная не инициализируется.


 
stas ©   (2011-10-18 16:55) [11]

Вообще странно конечно почему ошибка, а не предупреждение.


 
stas ©   (2011-10-18 16:57) [12]

Kerk ©   (18.10.11 16:50) [6]
ага, считал что будет ошибка.


 
icelex ©   (2011-10-18 16:57) [13]

компилятор не должен додумывать, что у тебя происходит в цикле: быть может у тебя там возникнет исключительная ситуация и переменная не инициализируется


 
И. Павел ©   (2011-10-18 16:59) [14]

> [9] stas ©   (18.10.11 16:54)

Это ясно. Просто показалось странным, что в книге причиной оршибки считается именно область видимости, а не то, что компилятор побоялся, что for может не выполниться ни разу. Вот цитата:

"Здесь последнее значение, присваиваемое text в цикле, доступно и за пределами
цикла. Очевидно, что понимание данной темы требует приложения несколько  
больших усилий. На первый взгляд не совсем ясно, почему, с учетом предыдущего  
примера, у переменной text после цикла не остается в качестве значения та пустая строка,
которая была присвоена ей перед началом цикла в коде.
Причина такого поведения связана с выделением памяти для переменной text, как,
по сути, это делается для любой другой переменной. Одно лишь объявление  
переменной простого типа ни к каким особым действиям не приводит. Только при  
присваивании переменным значений для их хранения в памяти выделяется место. Когда эта
операция по выделению памяти происходит внутри цикла, значение определяется как
локальное значение и тогда за пределами цикла оно покидает свою область видимости.
Даже хотя сама переменная не ограничивается циклом, то значение, которое в ней
содержится, ограничивается им. Присваивание значения переменной за пределами
цикла, однако, делает его локальным по отношению к главному коду и при этом все
равно оставляет действительным внутри цикла. Это означает, что переменная не  
выходит за рамки области видимости до тех пор, пока не завершится выполнения  
основного блока кода, благодаря чему к содержащемуся в ней значению можно получать
доступ и за пределами цикла.
К счастью для программистов, компилятор С# обнаруживает проблемы, связанные
с областью видимости переменных, а реагирование на генерируемые им сообщения
об ошибках, действительно позволяет лучше разобраться в этой теме. "

Книга называется "Visual C# Базовый курс"


 
DiamondShark ©   (2011-10-18 17:12) [15]


> stas ©   (18.10.11 16:55) [11]
> Вообще странно конечно почему ошибка, а не предупреждение.

Потому что использование неинициализированной переменной -- грубая ошибка.


 
euru ©   (2011-10-18 20:03) [16]


> И. Павел ©   (18.10.11 16:12) 
> Зачем разработчики C# решили выбрать такой подход?

Чтобы гарантировать, что при обращении к этой переменной в ней будет храниться не мусор, а инициированное значение.


> PS: Кстати, если for заменить на простой блок {} или на
> блок while или do с константным выполнением 1 раз, то инициализация
> во внутреннем блоке засчитывается.

Поведение для while такое же, как и для for.
string s;
int i = 0;
while (i < 1)
{
  s = "test";
  i++;
}

Также выдаётся сообщение об ошибке.

Для цикла do сообщения не будет, если внутри него не будет команды выхода из цикла до инициализации переменной.
string s;
int i = 0;
do
{
  // if (i > 0) break; <-- убрать комментарий - появится соообщение
  s = "test";
  // if (i > 0) break; <-- не влияет на компиляцию
  i++;
} while (i < 1);


 
Sha ©   (2011-10-18 20:18) [17]

> И. Павел ©   (18.10.11 16:12)  
> При компиляции выскакивает ошибка на последней строке: переменная s не инициализированна.

Ты радоваться должен. Серьезно.

> Kerk ©   (18.10.11 16:50) [8]
> В C точка с запятой -- statement terminator, в Pascal -- statement separator.
> Поэтому в Delphi их нельзя ставить просто так, а в C можно.

Просто как?
Можно пример, что нельзя?


 
Kerk ©   (2011-10-18 20:30) [18]


> Sha ©   (18.10.11 20:18) [17]
> Просто как?
> Можно пример, что нельзя?

Например:
begin
 ...
end;;


Разве можно? Честно говоря, лень проверять :)


 
Sha ©   (2011-10-18 20:41) [19]

> Kerk ©   (18.10.11 20:30) [18]

Можно везде где допустим пустой оператор.


 
И. Павел ©   (2011-10-18 20:43) [20]

Всем спасибо!
То есть получается, что компилятор ругается просто из-за того, что не понимает, что цикл выполнится хотя бы один раз?

А то, судя по тому, что написано в книге, можно решить, что после выхода из блока все значения, присвоенные в этом блоке переменным, объявленным во внешних блоках, очищаются. Такого или подобного сюрприза в C# быть не может?

Вот это место в книге:

> Когда эта операция по выделению памяти происходит внутри
> цикла, значение определяется как локальное значение и тогда
> за пределами цикла оно покидает свою область видимости.
> Даже хотя сама переменная не ограничивается циклом, то значение,
>  которое в ней содержится, ограничивается им.


 
Kerk ©   (2011-10-18 21:02) [21]


> Sha ©   (18.10.11 20:41) [19]
>
> > Kerk ©   (18.10.11 20:30) [18]
>
> Можно везде где допустим пустой оператор.

Вот за что я люблю этот форум, так за то, что к киевскому дядьке разговор сводится моментально :)


 
Sha ©   (2011-10-18 21:12) [22]

> Kerk ©   (18.10.11 21:02) [21]

ты же не хотел написать

pro;ce;dure

правда?


 
Kerk ©   (2011-10-18 21:20) [23]


> Sha ©   (18.10.11 21:12) [22]

Если ты перечитаешь посты [8] и [18], ты сразу поймешь, что я хотел, а что нет. И какой нюанс в различиях языков хотел подчеркнуть.

:)


 
Kerk ©   (2011-10-18 21:22) [24]

Лучше даже начать с постов [4] и [6]


 
Sha ©   (2011-10-18 21:34) [25]

> Kerk

Речь о допустимости сепараторов/делимитеров операторов можно вести
только относительно тех мест в программе, где эти самые операторы допустимо писать.
В других местах, там где операторов вообще быть не может по правилам языка,
подобные слова лишены всякого смысла, не применимы.
Пустой оператор допустим в любом месте, где допустим любой оператор.


 
Kerk ©   (2011-10-18 21:51) [26]


> Sha ©   (18.10.11 21:34) [25]

Продолжай, не буду мешать.


 
euru ©   (2011-10-18 22:44) [27]


> И. Павел ©   (18.10.11 20:43) [20]
> То есть получается, что компилятор ругается просто из-за
> того, что не понимает, что цикл выполнится хотя бы один
> раз?

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

http://blogs.msdn.com/b/ruericlippert/archive/2009/10/12/9929757.aspx
http://blogs.msdn.com/b/ruericlippert/archive/2011/03/05/never-say-never-part-two.aspx


 
Pavia ©   (2011-10-18 23:02) [28]

По поводу кода в 1 посте. Этот код будет ругаться в C# 3 версии.
Потому что начиная с этой версии если компилятор может распознать тип переменной по строки инициализации то он это делает. Поэтому в данном коде есть две переменные c одним именем "s". Первая переменная объявлена в не цикла вторая в цикле.

Да, по логике это должна быть одна переменная. Но видимо разработчики что-то не учли. Конструирование компиляторов вещь простая, но много вариантов приходиться перебирать при помощи своего мозга.


 
Игорь Шевченко ©   (2011-10-18 23:11) [29]


> Потому что начиная с этой версии если компилятор может распознать
> тип переменной по строки инициализации то он это делает.
>  Поэтому в данном коде есть две переменные c одним именем
> "s". Первая переменная объявлена в не цикла вторая в цикле.
>  


А можно еще раз и помедленнее, для бестолковых ? :)


 
DiamondShark ©   (2011-10-18 23:44) [30]


> Pavia ©   (18.10.11 23:02) [28]

Вы бредите.


 
Kerk ©   (2011-10-18 23:54) [31]

Попробую угадать. Речь про вывод типов?


 
Pavia ©   (2011-10-18 23:59) [32]

Первая стадия компилятора лексический анализ. Вторая стадия грамматический анализ.
Третья стадия семантический анализ.
Часто работа в компиляторе построена по принципу конвейера.
Это значит что семантический анализ начинается когда грамматический закончил делать некоторую часть, не всю обработка теста.
Так вот прежде чем проверять допустимость применения переменных в операциях надо пройтись и найти эти самые переменные.
Объявлять переменные можно 2 способами.
string s;
..
s="...";
int i=5;

и второй способ

s="..."
i=5;

В том же паскале константы можно объявлять без указания типа. Компилятор сам справится с присвоением нужного типа.
Но в отличии от паскаля в Cи и С# переменные можно объявлять внутри statements.

> Не верю, что современный компилятор не способен определить,
>  что цикл (int i = 0; i < 1; i++) будет выполнен строго
> 1 раз.

Как раз таки компилятор правильно определяет. Цикл может выполниться 0 раз, а может бесконечно много. Это легко доказать. На это есть 2 причины.
Первая причина компьютере есть несколько ядер и  они могут работать параллельно. Это значит что переменная "i" может быть изменена во время выполнения.
Вторая причина это организация цикла.
Что тут записано.
(int i = 0; i < 1; i++) Эти строки надо трактовать как.
int i = 0; - то что выполняется до начала цикла.
i < 1; - то что выполняется в первой строчке цикла.
i++ - то что выполняется в последней строчки цикла.

Другими словами.


> for (int i = 0; i < 1; i++)
> {
>   s = "test";
> };

Трактуется как.

int i = 0;  i++)
{if (i < 1) breack;
 s = "test";
i=i++
};

Отсюда видно что если переменная i в силу работы второго ядра изменится, то до строчки s = "test"; выполнение не дойдёт.
Вот если бы вы записали цикл иначе.
for (int i = 0; i++; i < 1; )
{
  s = "test";
};
То тут гарантированно бы выполнился один раз. Так как условие выхода находиться в конце цикла.

Теперь ясно почему в первом случае компилятору правильнее считать s двумя разными переменными.

Вопрос как второе ядро может достучатся до переменной "i"? Ведь переменная имеет очень маленькую зону видимости. На самом деле это не совсем не верно.
Дело в том что C# является  интерпретируемым языком. А значит любая функция может получить доступ скажем к байт-коду который описывает эту программу. И как следствие может получить доступ к переменной i. Есть механизмы расширения кода основанные на delegate и тому подобное.


 
Pavia ©   (2011-10-19 00:04) [33]

Печатался, слегка.
Вместо

> Трактуется как.
>
>  int i = 0;  i++)
> {if (i < 1) breack;
>   s = "test";
>  i=i++
> };


Трактуется как.

int i = 0;
{if (i < 1) break;
 s = "test";
i=i++;};


 
Игорь Шевченко ©   (2011-10-19 00:24) [34]

Pavia ©   (18.10.11 23:59) [32]

Феерично!


> Цикл может выполниться 0 раз, а может бесконечно много.
> Это легко доказать. На это есть 2 причины.
> Первая причина компьютере есть несколько ядер и  они могут
> работать параллельно. Это значит что переменная "i" может
> быть изменена во время выполнения.


Дважды феерично!

Вам, сударь, книжки надо писать, Фленова за пояс заткнете. Только пунткуацию подтянуть.


 
Игорь Шевченко ©   (2011-10-19 00:24) [35]


> пунткуацию


пунктуацию


 
Джо ©   (2011-10-19 01:19) [36]

Ой-вей, зачем я это на ночь прочитал? Проснусь совсем больной и разбитый %)


 
Германн ©   (2011-10-19 01:23) [37]


> Вам, сударь, книжки надо писать

Лучше фантастику, если воображения хватит.


 
И. Павел ©   (2011-10-19 09:12) [38]

Что-то я запутался совсем…

В общем: уважаемые C# программисты, подскажите, пожалуйста, то, что написано на стр. 140, 141 в главе “Variable Scope in Other Structures” этой книги: http://www.fayloobmennik.net/1083499 - это ошибка авторов (которые предположение компилятора, что не будет ни одной итерации, приняли за какую-то проблему с областью видимости), или же тут есть какой-то хитрый смысл?

Судя по [28] хитрый смысл есть, но я так и не въехал в раздвоение переменной (авторы книги, кстати, тоже пишут про какое-то раздвоение, правда там все еще более изощренно)…

В английском тексте книги нового издания (2010г.) эта глава переписана без изменений… Т.е. никто из читателей не жаловался. А мне важно до сути докопаться: люблю, когда все в новом изучаемом языке ясно.


 
И. Павел ©   (2011-10-19 09:50) [39]

> [38] И. Павел ©   (19.10.11 09:12)

Вот ссылка на нужную главу книги, чтобы не скачивать с файлообменника: http://flylib.com/books/en/3.459.1.44/1/


 
icelex ©   (2011-10-19 11:14) [40]


> И. Павел ©   (19.10.11 09:12) [38]

нет там ничего про раздвоение
просто в цикле компилятор может быть уверен, что переменная инициализирована, т.к. использование переменной идет уже после инициализации
за пределами цикла нельзя быть уверенным, что переменная инициализирована внутри цикла
псмотри, например, на такой код
string s;
int x=0;
if (x==0)
{
s = "test";
}
Console.WriteLine(s);

у тебя возникнет та же ошибка

но стоит изменить его на
string s;
int x=0;
if (x==0)
{
s = "test";
}
else
{
s="test2";
}
Console.WriteLine(s);


как все встает на свои места



Страницы: 1 2 вся ветка

Форум: "Прочее";
Текущий архив: 2012.02.12;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.013 c
2-1320226118
igorium
2011-11-02 12:28
2012.02.12
Как встроить свой шрифт в программу?


2-1320079010
tombender
2011-10-31 19:36
2012.02.12
сетевая папка


3-1271666302
fearless
2010-04-19 12:38
2012.02.12
Получить список компьютеров, подключенных к конкретной БД


6-1250609834
raslmc
2009-08-18 19:37
2012.02.12
Проблема с WebModule1


6-1250528504
sniknik
2009-08-17 21:01
2012.02.12
Генерация файлов для получения сертификата (SSL)





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский