Разное

Инициализация переменной: Инициализация, присваивание и объявление переменных в С++ | Уроки С++

Содержание

c++ — Что такое определение, объявление и инициализация переменной

Нижеизложенное сильно упрощено. Здесь не преследуется цель рассказать подробно все нюансы — для этого лучше почитать Стандарт языка.

Объявление (declaration) переменной информирует компилятор о том, что где-то, возможно, в другой единице трансляции (очень грубо, в другом cpp-файле) выделено sizeof байт под хранение переменной такого-то типа с таким-то именем. Деклараций можно писать сколько угодно в разных блоках кода, по одной на блок.

Определение (definition) переменной информирует тот же компилятор о том, что память под переменную нужно взять прямо в этом месте, где написано данное определение. Именно в этой единице трансляции. Определение на всю программу может быть одно и только одно.

Этими процессами управляют квалификаторы static, extern, thread_local и некоторые другие.

Чаще всего, происходят одновременно объявление и определение, например

int a; // вне функций - заставит компилятор создать глобальную переменную.

{
   int a;  //в блоке кода - переменная будет существовать до конца блока, а память будет выделена на стеке
}

примером чисто объявления идентификатора будет объявление его внутри структуры:

struct A
{
    int a;
};

Никакая память на данный момент не выделена, но компилятор теперь знает, что у него такое есть. Память выделится там, где определят структуру;

Использование ключевого слова extern, которое как раз говорит компилятору, что переменная объявлена где-то в другой единице трансляции

 //alpha.cpp
 int a;

 //beta.cpp
 extern int a; // указали, что будем пользоваться `int a` из `alpha.cpp`

Само связывание имен и памяти произойдет на этапе компоновки программы.

Инициализация в смысле C++ — это когда определение и объявление объединяют с присваиванием начального значения;

{
    int a = 8;
}

Вот такое:

{    
    int a;
    a=8;
}

Будет инициализацией в смысле практики программирования (самая первая запись в переменную), но не будет инициализацией в смысле C++ — это идущие подряд определение и присваивание.

Чтение из неинициализированной переменной — форменное UB. Так писать нельзя, это неправильно.

c++ — Инициализация переменной в функции

В зависимости от того как ведёт себя или как должна вести себя программа, можно решить проблему по разному. Как понимаю Login_Form получает/заполняет данные о пользователе, тогда в месте где нужно получить от этой формы user_id просто дожидаешься точки когда форма уже все данные получила и тогда получаешь этот айди, хорошо кстати получение оформить в виде метода GetUserId() тогда в этом методе можно выждать паузу пока данные не пришли и только тогда возвратить нужный айди. Если ждать сразу нет возможности тогда нужно чтобы bool GetUserId(int & user_id) возвращало например true/false если уже данные есть или нет и только если вернуло true тогда user_id можно считать заполненым нужной величиной. Т.е. нужно где-то хранить форму и её иногда опрашивать чтобы получить нужные от неё данные, при этом можно либо блокирующий сделать GetUserId() который дожидается ввода либо не блокирующий который флаг успеха вернёт. При этом обязательно пока все данные не получены где-то эта форма хранится в виде переменной или члена класса. Ваше решение когда сохраняется указатель на user_id небезопасно, кто знает живой ли объект формы или уже не живой, т.е. указатель может быть действительным или нет. Зато если хранится объект формы всё время пока она не выполнила нужные действия тогда есть гарантия, что все её методы будут работать.

Если же всё равно нужно как-то с указателем решить эту проблему, тогда это тоже решаемо, только не так как в коде у вас, а так — нужно умный указатель использовать например std::shared_ptr, т.е. в классе формы мы не храним два члена (int user_id и int * ptr_user_id), а храним один член std::shared_ptr ptr_user_id, в конструкторе под него выделяем память ptr_user_id = new int(0), потом когда нужно в форме заполняем его при этом также из формы его сделаем возможным для получения через shared_ptr<int> GetUserIdPtr() const { return ptr_user_id; }. Во всех местах где нужно потребить этот айди просто копируем себе в свой член класса shared_ptr my_copy_user_id; … после создания формы делаем my_copy_user_id = form.GetUserId() и теперь у нас в форме и в потребителях у всех shared_ptr указывающий на одну общую память, в итоге потом можно получать значение указателя в любой момент, даже когда формы объект уничтожен, при этом будет безопасно, тажке можно всегда проверять заполнелся ли user_id уже значением или ещё нет, если 0 значит ещё не заполнился, иначе заполнился. shared_ptr это стандартный С++ шаблонный класс который подсчитывает ссылки на общую память заданного типа и сам когда надо освобождает её т.е. ничего для освобождения не нужно делать, также подсчёт ссылок гарантирует что память освободится только когда форма и все её потребители завершат своё существование. Также когда форма уже уничтожена при этом указатель остаётся валидным т.к. память под него не уничтожается.

Инициализация переменной без присвоения значения. Python

Пожалуйста помогите, создал функцию, чтобы приконнектиться к БД, но есть проблема, я не могу дальше пользоваться переменной cursor, чтобы работать с бд.

def ConnectServer():
    cnxn = pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username.get()+';PWD='+ password.get())
    cursor = cnxn.cursor()

Вопрос: можно ли как-нибудь инициализировать эту переменную в основном коде, без присвоения ей значения?

P.S. Мне нужно, чтобы я мог обращаться к этой переменной при помощи global в функции и она была видна в основном коде

Код кнопки:

bb1 = Button(pas, text='Ввести', background="#555", foreground="#ccc", command=ConnectServer, font='Times 12 italic')

Полный код:


#######################################################################
#######################################################################
def ConnectServer():
    global cursor
    cnxn = pyodbc. connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER='+server+';DATABASE='+database+';UID='+username.get()+';PWD='+ password.get())
    cursor = cnxn.cursor()
    root.deiconify()
    pas.withdraw()
#######################################################################
#######################################################################
###Подключение#К#БД###
root = Tk()
root.configure(background='#888')
root.title('СУБД')
root.geometry('1200x600+370+40')
root.withdraw()

server = 'LAPTOP-C0VE2QJR\SERVER' 
database = 'Metropol'
username = StringVar()
password = StringVar()
    
username.set('admin')
password.set('password')
pas = Toplevel(background= "#888")
pas.title('Вход')
pas.geometry('300x300')

Labll = Label(pas, text='Введите логин', font='Times 12 italic', fg='#111')
Labll.grid(row=0, column=0, sticky=S, pady=30, padx=70)

Entr1 = Entry(pas, text='',  textvariable=username, font='Times 12 bold', justify='center')
Entr1.grid(row=1, column=0, sticky=S, padx=70, pady=3)

Labl2 = Label(pas, text='Введите пароль', font='Times 12 italic', fg='#111')
Labl2.grid(row=2, column=0, sticky=S, pady=30, padx=70)

Entr2 = Entry(pas, text='',  textvariable=password, font='Times 12 bold', justify='center')
Entr2.grid(row=3, column=0, sticky=S, padx=70, pady=3)

bb1 = Button(pas, text='Ввести', background="#555", foreground="#ccc", command=ConnectServer, font='Times 12 italic')
bb1.grid(row=4, column=0, sticky=S,  padx=70, pady=10)     
#######################################################################
###Вывод#Таблиц###

cursor.execute("SELECT Номер FROM ЛицевойСчет")
rows = cursor.fetchall()
print(rows)

1.6.4. Инициализация глобальных и локальных переменных

Содержание
На предыдущую страницу к разделу 1.6.3. Время жизни и область видимости программных объектов

При инициализации необходимо придерживаться следующих правил:
 

1. Объявления содержащие спецификатор класса памяти extern не могут содержать инициаторов.
 

2. Глобальные переменные всегда инициализируются, и если это не сделано явно, то они инициализируются нулевым значением.
 

3. Переменная с классом памяти static может быть инициализирована константным выражением. Инициализация для них выполняется один раз перед началом программы. Если явная инициализация отсутствует, то переменная инициализируется нулевым значением.
 

4. Инициализация переменных с классом памяти auto или register выполняется всякий раз при входе в блок, в котором они объявлены. Если инициализация переменных в объявлении отсутствует, то их начальное значение не определено.
 

5. Начальными значениями для глобальных переменных и для переменных с классом памяти static должны быть константные выражения. Адреса таких переменных являются константами и эти константы можно использовать для инициализации объявленных глобально указателей. Адреса переменных с классом памяти auto или register не являются константами и их нельзя использовать в инициаторах.
 

1
2
3
4
5
6
7
8
     Пример:
    int global_var;
    int func(void)
    { int local_var;                      /* по умолчанию auto  */
      static int *local_ptr=&local_var;   /*  так неправильно   */
      static int *global_ptr=&global_var; /*  а так правильно   */
      register int *reg_ptr=&local_var;   /*  и так правильно   */
    }

Пример:
int global_var;
int func(void)
{ int local_var; /* по умолчанию auto */
static int *local_ptr=&local_var; /* так неправильно */
static int *global_ptr=&global_var; /* а так правильно */
register int *reg_ptr=&local_var; /* и так правильно */
}

В приведенном примере глобальная переменная global_var имеет глобальное время жизни и постоянный адрес в памяти, и этот адрес можно использовать для инициализации статического указателя global_ptr. Локальная переменная local_var, имеющая класс памяти auto размещается в памяти только на время работы функции func, адрес этой переменной не является константой и не может быть использован для инициализации статической переменной local_ptr. Для инициализации локальной регистровой переменной reg_ptr можно использовать неконстантные выражения, и, в частности, адрес переменной local_ptr.
 

Читать далее. Раздел 1.7.1. Методы доступа к элементам массивов
Содержание

Golang: Спецификация Go: инициализация пакета

Внутри пакета переменные уровня пакета инициализируются в порядке объявления, но после любой из переменных, от которых они зависят.

Точнее, переменная уровня пакета считается готовой к инициализации, если она еще не инициализирована и не имеет выражения инициализации или ее выражение инициализации не зависит от неинициализированных переменных. Инициализация продолжается путем многократной инициализации следующей переменной уровня пакета, которая является самой ранней в порядке объявления и готова к инициализации, пока не будет готовых переменных для инициализации.

Если какие-либо переменные все еще неинициализированы после завершения этого процесса, эти переменные являются частью одного или нескольких циклов инициализации, и программа недействительна.

Порядок объявления переменных, объявленных в нескольких файлах, определяется порядком, в котором файлы представляются компилятору: переменные, объявленные в первом файле, объявляются перед любой из переменных, объявленных во втором файле, и так далее.

Анализ зависимостей не основывается на фактических значениях переменных, только на лексических ссылках на них в источнике, анализируемых транзитивно. Например, если выражение инициализации переменной x относится к функции, тело которой относится к переменной y, то x зависит от y. В частности:

  • Ссылка на переменную или функцию — это идентификатор, обозначающий эту переменную или функцию.
  • Ссылка на метод m — это значение метода или выражение метода в форме t. m, где (статический) тип t не является типом интерфейса, а метод m находится в наборе методов t. Неважно, вызывается ли результирующее значение функции t.m.
  • Переменная, функция или метод x зависят от переменной y, если выражение или тело инициализации x (для функций и методов) содержит ссылку на y или на функцию или метод, который зависит от y.

Анализ зависимостей выполняется для каждого пакета; рассматриваются только ссылки на переменные, функции и методы, объявленные в текущем пакете.

Например, с учетом объявлений

var (
    a = c + b
    b = f()
    c = f()
    d = 3
)

func f() int {
    d++
    return d
}

порядок инициализации d, b, c, a.

Переменные также могут быть инициализированы с использованием функций с именем init, объявленных в блоке пакета, без аргументов и без параметров результата.

func init() { … }

Несколько таких функций могут быть определены для пакета, даже в пределах одного исходного файла. В блоке пакета идентификатор init можно использовать только для объявления функций init, но сам идентификатор не объявлен. Таким образом, на функции init нельзя ссылаться из любой точки программы.

Пакет без импорта инициализируется путем присвоения начальных значений всем его переменным уровня пакета с последующим вызовом всех функций init в порядке их появления в источнике, возможно, в нескольких файлах, как представлено компилятору. Если пакет имеет импорт, импортированные пакеты инициализируются перед инициализацией самого пакета. Если несколько пакетов импортируют пакет, импортированный пакет будет инициализирован только один раз. Импорт пакетов по своей конструкции гарантирует отсутствие циклических инициализационных зависимостей.

Инициализация пакета — инициализация переменной и вызов функций init — происходит в одной последовательности, последовательно, по одному пакету за раз. Функция init может запускать другие программы, которые могут выполняться одновременно с кодом инициализации. Однако инициализация всегда выполняет последовательность функций init: она не будет вызывать следующую, пока не вернется предыдущая.

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


Читайте также:

Создание и уничтожение объектов — Переменные — Основы языка — Справочник MQL5

После загрузки на исполнение mql5-программы каждой переменной выделяется память в соответствие с типом переменной. Переменные делятся на два типа по уровню доступа — глобальные переменные и локальные переменные, и по классам памяти: входные параметры mql5-программы, статические и автоматические. Каждая переменная при необходимости инициализируется соответствующим значением. После использования переменная деинициализируется и память, использованная ею, возвращается исполняемой системе MQL5.

Инициализация и деинициализация глобальных переменных

Инициализация глобальных переменных производится автоматически сразу после загрузки mql5-программы и до вызова любой функции. При инициализации производится присвоение начальных значений переменным простых типов и вызывается конструктор для объектов, если он есть. Входные переменные всегда объявляются на глобальном уровне, инициализируются значениями, задаваемыми пользователями в диалоге при запуске mql5-программы.

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

Порядок инициализации соответствует порядку объявления переменной в программе, а деинициализация производится в обратном порядке перед выгрузкой mql5-программы. Это правило только для тех переменных, которые не были созданы оператором new. Такие переменные создаются и инициализируются автоматически сразу после загрузки, а деинициализируются непосредственно перед выгрузкой программы.

Инициализация и деинициализация локальных переменных

Если переменная, объявленная на локальном уровне, не является статической, то распределение памяти под такую переменную производится автоматически. Локальные переменные, также как и глобальные, инициализируются автоматически в тот момент, когда выполнение программы встречает объявление локальной переменной. Таким образом, порядок инициализации соответствует порядку объявления.

Локальные переменные деинициализируются в конце блока программы, в котором они объявлены, и в порядке, обратном их объявлению. Блок программы – это составной оператор, который может являться частью оператора выбора switch, цикла(for, while, do-while), телом функции или частью оператора if-else.

Инициализация локальных переменных происходит только в тот момент, когда выполнение программы доходит до объявления переменной. Если в процессе выполнения программы блок, в котором объявлена переменная, не был выполнен, то такая переменная не инициализируется.

Инициализация и деинициализация динамически размещаемых объектов

Особый случай представляют из себя указатели объектов, так как объявление указателя не влечет за собой инициализации соответствующего объекта. Динамически размещаемые объекты инициализируются только в момент создания экземпляра класса оператором new. Инициализация объекта предполагает вызов конструктора соответствующего класса. Если соответствующего конструктора в классе нет, то его члены, имеющие простой тип, не будут автоматически проинициализированы; члены типов строка, динамический массив и сложный объект будут автоматически проинициализированы.

Указатели могут быть объявлены на локальном или глобальном уровне и при этом могут быть проинициализированы пустым значением NULL или значением указателя такого же или порожденного типа. Если для указателя, объявленного на локальном уровне, был вызван оператор new, то и оператор delete для этого указателя должен быть выполнен до выхода из этого уровня. В противном случае указатель будет потерян, и объект не сможет быть удален явно.

Все объекты, созданные выражением указатель_объекта=new Имя_Класса, обязательно должны быть впоследствии уничтожены оператором delete(указатель_объекта). Если по каким то причинам такая переменная по окончании работы программы не была уничтожена оператором delete, то об этом будет выведено сообщение в журнал «Эксперты». Можно объявить несколько переменных и всем им присвоить указатель одного объекта.

Если динамически создаваемый объект имеет конструктор, то этот конструктор будет вызван в момент выполнения оператора new. Если объект имеет деструктор, то деструктор будет вызван в момент выполнения оператора delete.

Таким образом, динамически размещаемые объекты создаются только в момент создания оператором new и гарантированно уничтожаются либо оператором delete, либо автоматически исполняющей системой MQL5 в момент выгрузки программы. Порядок объявления указателей динамически создаваемых объектов не влияет на порядок их инициализации. Порядок инициализации и деинициализации полностью контролируется программистом.

Особенности работы с динамической памятью

При работе с динамическими массивами освобожденная память сразу же возвращается в систему.

При создании динамического объекта класса через new, память сначала ищется в пуле памяти классов, с которым работает менеджер памяти, и если в пуле недостаточно памяти, то память запрашивается в системе. При удалении динамического объекта через delete, память, занимаемая объектом, возвращается в пул памяти классов.

Менеджер памяти возвращает память в систему сразу после выхода из функций-обработчиков событий: OnInit(), OnDeinit(), OnStart(), OnTick(), OnCalculate(), OnTimer(), OnTrade(), OnTester(), OnTesterInit(), OnTesterPass(), OnTesterDeinit(), OnChartEvent(), OnBookEvent().

Краткая характеристика переменных

Основные сведения о порядке создания, уничтожении, вызове конструкторов и деструкторов приведены в краткой таблице.









Инициализация

сразу после загрузки mql5-программы

при достижении в ходе выполнения строки кода, где она объявлена

при выполнении оператора new

Порядок инициализации

в порядке объявления

в порядке объявления

не зависит от порядка объявления

Деинициализация

перед выгрузкой mql5-программы

при выходе выполнения из блока объявления

при выполнении оператора delete или перед выгрузкой mql5-программы

Порядок деинициализации

в порядке, обратном инициализации

в порядке, обратном инициализации

не зависит от порядка инициализации

Вызов конструктора

при загрузке mql5-программы

при инициализации

при выполнении оператора new

Вызов деструктора

при выгрузке mql5-программы

при выходе из блока, в котором переменная была инициализирована

при выполнении оператора delete

Сообщения об ошибках

сообщение в журнал «Эксперты» о попытке удаления автоматически созданного объекта

сообщение в журнал «Эксперты» о попытке удаления автоматически созданного объекта

сообщение в журнал «Эксперты» о неудаленных динамически созданных объектах при выгрузке mql5-программы

Смотри также

Типы данных, Инкапсуляция и расширяемость типов, Инициализация переменных, Область видимости и время жизни переменных

Насколько важна инициализация переменной

Как говорили другие, это зависит от языка. Но я продемонстрирую мои идеи Java (и эффективной Java) об инициализации переменных. Они должны использоваться для многих других языков более высокого уровня.

Переменные класса, отмеченные staticв Java, похожи на константы. Эти переменные обычно должны быть окончательными и инициализироваться непосредственно после определения с использованием =или из блока инициализатора класса static { // initialize here }.

Как и во многих языках более высокого уровня и сценариев, полям будет автоматически назначаться значение по умолчанию. Для цифр charэто и будет нулевое значение. Для строк и других объектов это будет null. Теперь nullэто опасно и должно использоваться экономно. Таким образом, эти поля должны быть установлены в допустимое значение как можно скорее. Конструктор обычно идеальное место для этого. Чтобы убедиться, что переменные установлены во время конструктора, а не изменены впоследствии, вы можете пометить их finalключевым словом.

Попробуйте не поддаваться искушению использовать в nullкачестве какого-либо флага или особого значения. Например, лучше включить определенное поле для хранения состояния. Поле с именем, stateкоторое использует значения Stateперечисления, будет хорошим выбором.

Поскольку изменения значений параметров (будь то ссылки на объекты или базовые типы, такие как целые числа и т. Д.) Не будут видны вызывающей стороне, параметры должны быть помечены как final. Это означает, что значения самой переменной нельзя изменить. Обратите внимание, что значение экземпляров изменяемого объекта может быть изменено, ссылка не может быть изменена, чтобы указывать на другой объект или nullхотя.

Локальные переменные не инициализируются автоматически; они должны быть инициализированы перед использованием их значения. Один из способов убедиться в том, что ваша переменная инициализирована, состоит в том, чтобы напрямую инициализировать их каким-либо значением по умолчанию. Это то, что вы не должны делать. В большинстве случаев значение по умолчанию не является ожидаемым.

Гораздо лучше определять переменную только там, где она нужна. Если переменная должна принимать только одно значение (что верно для большинства переменных в хорошем коде), вы можете пометить переменную final. Это гарантирует, что локальная переменная назначается ровно один раз, а не ноль раз или два раза. Пример:

public static doMethod(final int x) {
    final int y; // no assignment yet, it's final so it *must* be assigned
    if (x < 0) {
        y = 0;
    } else if (x > 0) {
        y = x;
    } else {
        // do nothing <- error, y not assigned if x = 0
        // throwing an exception here is acceptable though
    }
}

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

1.4 — Назначение и инициализация переменных

В предыдущем уроке (1.3 — Введение в переменные) мы рассмотрели, как определить переменную, которую мы можем использовать для хранения значений. В этом уроке мы узнаем, как на самом деле помещать значения в переменные и использовать эти значения.

Напоминаем, что вот короткий фрагмент, который сначала выделяет одну целочисленную переменную с именем x , а затем выделяет еще две целочисленные переменные с именами y и z :

внутр x; // определение целочисленной переменной с именем x

int y, z; // определяем две целочисленные переменные с именами y и z

Присвоение переменной

После того, как переменная была определена, вы можете присвоить ей значение (в отдельном операторе) с помощью оператора = .Этот процесс для краткости называется присвоением копий (или просто назначением).

внутренняя ширина; // определение целочисленной переменной с именем width

width = 5; // копируем присвоение значения 5 в переменную шириной

// ширина переменной теперь имеет значение 5

Назначение

Копировать названо так, потому что оно копирует значение с правой стороны оператора = в переменную с левой стороны оператора.Оператор = называется оператором присваивания.

Вот пример, где мы используем присваивание дважды:

#include

int main ()

{

int width;

ширина = 5; // копируем присвоение значения 5 в переменную шириной

// ширина переменной теперь имеет значение 5

width = 7; // изменить значение, хранящееся в переменной ширины, на 7

// ширина переменной теперь имеет значение 7

return 0;

}

Когда мы присваиваем значение 7 переменной width , значение 5, которое было там ранее, перезаписывается.Нормальные переменные могут содержать только одно значение за раз.

Одна из наиболее распространенных ошибок, которые делают новички, — это путать оператор присваивания ( = ) с оператором равенства ( == ). Присваивание ( = ) используется для присвоения значения переменной. Равенство ( == ) используется для проверки того, равны ли два операнда по значению.

Копирование и прямая инициализация

Обратной стороной присваивания является то, что для него требуются как минимум два оператора: один для определения переменной, а другой — для присвоения значения.

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

C ++ поддерживает три основных способа инициализации переменной. Во-первых, мы можем выполнить инициализацию копии, используя знак равенства:

int width = 5; // копируем инициализацию значения 5 в переменную шириной

Подобно назначению копирования, это копирует значение с правой стороны от равенства в переменную, создаваемую с левой стороны.

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

внутренняя ширина (5); // прямая инициализация значения 5 в переменную шириной

Для простых типов данных (например, целых чисел) копирование и прямая инициализация по существу одинаковы. Мы увидим различия между инициализацией копии и прямой инициализацией позже в руководствах.

В-третьих, инициализация списка, которую мы рассмотрим в следующем разделе.

Инициализация списка

К сожалению, прямая инициализация на основе скобок не может использоваться для всех типов инициализации (таких как инициализация объекта списком данных). Чтобы обеспечить более согласованный механизм инициализации, существует инициализация списка (также иногда называемая унифицированной инициализацией или инициализацией скобок), в которой используются фигурные скобки.

Инициализация списка осуществляется в двух формах:

внутренняя ширина {5}; // прямая инициализация списка значения 5 в переменную ширину (предпочтительно)

int height = {6}; // копируем инициализацию списка со значением 6 в переменную height

Эти две формы функционируют почти одинаково, но обычно предпочтительнее прямая форма.

Инициализация переменной с пустыми фигурными скобками указывает на инициализацию значения. В большинстве случаев инициализация значения инициализирует переменную нулевым значением (или пустым, если это более подходит для данного типа). В таких случаях, когда происходит обнуление, инициализация значения также называется инициализацией нуля.

int width {}; // инициализация значения значением 0

Инициализация списка имеет дополнительное преимущество, так как запрещает «сужающие» преобразования.Это означает, что если вы попытаетесь использовать инициализацию списка для инициализации переменной значением, которое она не может безопасно удерживать, компилятор выдаст предупреждение или ошибку. Например:

внутренняя ширина {4.5}; // ошибка: не все значения типа double помещаются в int

В приведенном выше фрагменте мы пытаемся назначить число (4.5), которое имеет дробную часть (часть .5), целочисленной переменной (которая может содержать только числа без дробных частей).Копирование и прямая инициализация отбрасывают дробную часть, что приводит к инициализации значения 4 в переменной шириной . Однако при инициализации списка это приведет к тому, что компилятор выдаст ошибку (что, как правило, хорошо, потому что потеря данных редко требуется). Разрешены преобразования, которые могут быть выполнены без потенциальной потери данных.

По возможности отдавайте предпочтение прямой инициализации списка.

Нет, C ++ не поддерживает синтаксис прямого или присвоения списков.

Используйте явное значение инициализации, если вы действительно используете это значение.

int x {0}; // явная инициализация значением 0

std :: cout << x; // мы используем это нулевое значение

Используйте инициализацию значения, если значение временное и будет заменено.

int x {}; // инициализация значения

std :: cin >> x; // мы немедленно заменяем это значение

Инициализируйте свои переменные

Инициализируйте переменные при создании.В конечном итоге вы можете найти случаи, когда вы захотите проигнорировать этот совет по определенной причине (например, критический для производительности раздел кода, который использует множество переменных), и это нормально, если выбор сделан сознательно.

Для более подробного обсуждения этой темы Бьярн Страуструп (создатель C ++) и Херб Саттер (эксперт по C ++) сами делают эту рекомендацию здесь.

Мы исследуем, что произойдет, если вы попытаетесь использовать переменную, для которой нет четко определенного значения, в уроке 1.6 — Неинициализированные переменные и неопределенное поведение.

Инициализируйте переменные при создании.

Инициализация нескольких переменных

В последнем разделе мы отметили, что можно определить несколько переменных одного типа в одном операторе, разделив имена запятой:

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

Вы можете инициализировать несколько переменных, определенных в одной строке:

int a = 5, b = 6; // копируем инициализацию

int c (7), d (8); // прямая инициализация

int e {9}, f {10}; // инициализация списка (предпочтительно)

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

int a, b = 5; // неверно (a не инициализировано!)

int a = 5, b = 5; // правильный

В верхнем операторе переменная «a» будет оставлена ​​неинициализированной, и компилятор может пожаловаться, а может и не пожаловаться. Если это не так, это отличный способ периодически давать сбой в вашей программе и давать спорадические результаты. В ближайшее время мы подробнее поговорим о том, что произойдет, если вы будете использовать неинициализированные переменные.

Лучший способ запомнить, что это неправильно, — это рассмотреть случай прямой инициализации или инициализации списка:

внутр a, b (5);

int c, d {5};

Это делает более ясным, что значение 5 используется только для инициализации переменной b или d , а не a или c .

Время викторины

В чем разница между инициализацией и назначением?

Показать решение

Инициализация дает переменной начальное значение в момент ее создания. Присваивание дает переменной значение в какой-то момент после создания переменной.

Какую форму инициализации следует использовать?

Показать решение

Прямая инициализация списка.

Инициализация — cppreference.com

Инициализация переменной обеспечивает ее начальное значение во время построения.

Начальное значение может быть предоставлено в разделе инициализатора декларатора или в новом выражении. Это также происходит во время вызовов функций: параметры функции и возвращаемые значения функции также инициализируются.

Для каждого декларатора инициализатор может быть одним из следующих:

( список выражений ) (1)
= выражение (2)
{ список инициализаторов } (3)

1) список произвольных выражений, разделенных запятыми, и списки инициализации в скобках

2) знак равенства, за которым следует выражение

3) список-инициализации в фигурных скобках: возможно, пустой список выражений, разделенных запятыми, и другие списки инициализации в фигурных скобках

В зависимости от контекста инициализатор может вызывать:

Если инициализатор не указан, применяются правила инициализации по умолчанию.

[править] Нелокальные переменные

Все нелокальные переменные со статической продолжительностью хранения инициализируются как часть запуска программы до начала выполнения основной функции (если не отложено, см. Ниже). Все нелокальные переменные с длительностью локального хранилища потока инициализируются как часть запуска потока, упорядочиваются до начала выполнения функции потока. Для обоих этих классов переменных инициализация происходит в два разных этапа:

[править] Статическая инициализация

Есть две формы статической инициализации:

На практике:

  • Постоянная инициализация обычно применяется во время компиляции.Предварительно рассчитанные представления объектов сохраняются как часть изображения программы. Если компилятор этого не делает, он все равно должен гарантировать, что инициализация произойдет до любой динамической инициализации.
  • Переменные, которые должны быть инициализированы нулем, помещаются в сегмент .bss образа программы, который не занимает места на диске и обнуляется ОС при загрузке программы.
[править] Динамическая инициализация

После завершения всей статической инициализации динамическая инициализация нелокальных переменных происходит в следующих ситуациях:

1) Неупорядоченная динамическая инициализация , которая применяется только к элементам статических данных шаблона класса (статическим / локальным для потока) и шаблонам переменных (начиная с C ++ 14), которые не являются явно специализированными.Инициализация таких статических переменных является неопределенной последовательностью относительно всех других динамических инициализаций, за исключением случаев, когда программа запускает поток до инициализации переменной, и в этом случае ее инициализация не имеет последовательности (начиная с C ++ 17). Инициализация таких локальных переменных потока не упорядочена по отношению ко всем остальным динамическим инициализациям.

2) Частично упорядоченная динамическая инициализация , которая применяется ко всем встроенным переменным, которые не являются явно или неявно конкретизированной специализацией.Если частично упорядоченный V определен перед упорядоченным или частично упорядоченным W в каждой единице трансляции, инициализация V упорядочивается до инициализации W (или происходит раньше, если программа запускает поток).

(начиная с C ++ 17)

3) Упорядоченная динамическая инициализация , которая применяется ко всем другим нелокальным переменным: в пределах одной единицы трансляции инициализация этих переменных всегда упорядочена в точном порядке, в котором их определения появляются в исходный код.Инициализация статических переменных в разных единицах трансляции имеет неопределенную последовательность. Инициализация локальных переменных потока в разных единицах трансляции неупорядочена.

Если инициализация нелокальной переменной со статической продолжительностью хранения или продолжительностью хранения потока завершается через исключение, вызывается std :: terminate.

[править] Ранняя динамическая инициализация

Компиляторам разрешается инициализировать динамически инициализированные переменные как часть статической инициализации (по сути, во время компиляции), если выполняются оба следующих условия:

1) динамическая версия инициализации не изменяет значение любого другого объекта области пространства имен до его инициализации

2) статическая версия инициализации производит такое же значение в инициализированной переменной, какое было бы получено при динамической инициализации, если бы все переменные, не требующие статической инициализации, были инициализированы динамически.

Из-за приведенного выше правила, если инициализация некоторого объекта o1 относится к объекту o2 области пространства имен, который потенциально требует динамической инициализации, но определяется позже в той же единице трансляции, не указано, является ли значение o2 будет значением полностью инициализированного o2 (поскольку компилятор продвинул инициализацию o2 для времени компиляции) или будет значением o2 , просто инициализированным нулем.

 встроенный двойной fd () {return 1.0; }
extern double d1;
двойной d2 = d1; // неопределенные:
                  // динамически инициализируется 0,0, если d1 инициализируется динамически, или
                  // динамически инициализируется значением 1.0, если d1 статически инициализирован, или
                  // статически инициализируется значением 0,0 (потому что это будет его значение
                  // если обе переменные были инициализированы динамически)
двойной d1 = fd (); // может быть инициализирован статически или динамически до 1.0 
[править] Отложенная динамическая инициализация

Это определяется реализацией, происходит ли динамическая инициализация — перед первым оператором основной функции (для статики) или начальной функцией потока (для локальных потоков), или отложенная, чтобы происходить после.

Если инициализация не встроенной переменной (начиная с C ++ 17) откладывается после первого оператора функции main / thread, это происходит до первого odr-использования любой переменной с определенной продолжительностью хранения статического / потока в той же единице трансляции, что и инициализируемая переменная.Если никакая переменная или функция не используется odr из данной единицы трансляции, нелокальные переменные, определенные в этой единице трансляции, могут никогда не инициализироваться (это моделирует поведение динамической библиотеки по запросу). Однако, пока что-либо из единицы трансляции используется odr, все нелокальные переменные, инициализация или уничтожение которых имеет побочные эффекты, будут инициализированы, даже если они не используются в программе.

Если инициализация встроенной переменной отложена, это происходит до первого odr-использования этой конкретной переменной.

(начиная с C ++ 17)
 // - Файл 1 -
#include "a.h"
#include "b.h"
B b;
A :: A () {b.Use (); }

// - Файл 2 -
#include "a.h"
А а;

// - Файл 3 -
#include "a. h"
#include "b.h"
extern A a;
extern B b;

int main () {
    a.Use ();
    б) Использовать ();
}

// Если a инициализируется до ввода main, b все еще может быть неинициализированным
// в точке, где A :: A () использует его (поскольку динамическая инициализация имеет неопределенную последовательность
// через единицы перевода)

// Если a инициализируется в какой-то момент после первого оператора main (который odr-использует
// функция, определенная в файле 1, принудительно запускающая ее динамическую инициализацию),
// тогда b будет инициализирован перед использованием в A :: A 

[править] Статические локальные переменные

Для инициализации локальных (т. Е. Области блока) статических и локальных переменных потока см. Статические локальные переменные.

Инициализатор не допускается в объявлении области блока переменной с внешней или внутренней связью. Такое объявление должно появляться с extern и не может быть определением.

[править] Члены класса

Нестатические элементы данных могут быть инициализированы списком инициализаторов элементов или инициализатором элементов по умолчанию.

[править] Примечания

Порядок уничтожения нелокальных переменных описан в std :: exit.

[править] См. Также

Объявление и инициализация переменной

| CPP

C ++ — это язык со строгой типизацией, который требует, чтобы каждая переменная была объявлена ​​с ее типом перед первым использованием.
Это информирует компилятор о размере, который следует зарезервировать в памяти для переменной, и о том, как интерпретировать ее значение.
Синтаксис объявления новой переменной в C ++ прост: мы просто пишем тип, за которым следует имя переменной (то есть ее идентификатор).

Синтаксис:

  char c; // объявление символьной переменной.
внутренняя область; // объявление целочисленной переменной.
число с плавающей запятой; // объявление переменной типа float.  

Это три действительных объявления переменных.

  • Первый объявляет переменную типа char с идентификатором c .
  • Второй — объявляет переменную типа int с идентификатором в области .
  • Третий объявляет переменную типа float с идентификатором num .

После объявления переменные c , area и num могут использоваться в остальной части их области действия в программе.


Объявление нескольких переменных

Если объявляется более одной переменной одного типа, все они могут быть объявлены в одном операторе, разделив их идентификаторы запятыми.

  int a, b, c; // более одного объявления переменной.  

Здесь объявляются три переменные ( a , b и c ), все они имеют тип int и имеют точно такое же значение, как:

  int a; // объявление целочисленной переменной.int b; // объявление целочисленной переменной.
int c; // объявление целочисленной переменной.  

Когда переменные в приведенном выше примере объявляются, они имеют неопределенное или мусорное значение, пока им не будет присвоено значение в первый раз. Но переменная может иметь определенное значение с момента ее объявления. Это называется инициализацией переменной.
В C ++ есть те же способы инициализации переменных, что и в языке C.

Синтаксис:

  идентификатор типа = начальное_значение;  

Пример:

  int a = 10; // объявление и инициализация целочисленной переменной.  

Практический:

  // Напишите программу CPP для объявления и инициализации переменной
#include 
int main ()
{
int sum; // Объявление переменной
int a = 10; // Объявление и инициализация переменной
int b = 5; // Объявление и инициализация переменной
ans = a + b;
cout << "Дополнение:" << ans << endl;
возврат 0;
}  

Выход:


Запомните

  • Объявление переменной обеспечивает компилятору гарантию того, что существует одна переменная с данным типом и именем, так что компилятор приступит к дальнейшей компиляции, не требуя полной информации о переменной.
  • Объявление переменной имеет значение только во время компиляции, компилятору требуется фактическое объявление переменной во время связывания программы.
  • Объявление переменной полезно, когда вы используете несколько файлов и определяете переменную в одном из файлов, который будет доступен во время связывания программы.
  • Вы будете использовать ключевое слово extern для объявления переменной в любом месте.
  • Хотя вы можете объявить переменную несколько раз в своей программе на C ++, но она может быть определена только один раз в файле, функции или блоке кода.
  • Та же концепция применяется к объявлению функции, где вы указываете имя функции во время ее объявления, а ее фактическое определение может быть дано где угодно.



Переменные: создание, инициализация, сохранение и загрузка

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

Этот документ ссылается на следующие классы TensorFlow. Следуйте ссылкам на их справочное руководство, чтобы получить полное описание их API:

Creation

Когда вы создаете переменную, вы передаете Tensor в качестве начального значения в конструктор Variable () . TensorFlow предоставляет набор операций, которые создают тензоры, часто используемые для инициализации из констант или случайных значений.

Обратите внимание, что все эти операции требуют, чтобы вы указали форму тензоров. Эта форма автоматически становится формой переменной. Переменные обычно имеют фиксированную форму, но TensorFlow предоставляет расширенные механизмы для изменения формы переменных.

 # Создайте две переменные.
веса = tf.Variable (tf.random_normal ([784, 200], stddev = 0,35),
                      name = "веса")
biases = tf. Variable (tf.zeros ([200]), name = "biases")
 

Вызов tf.Variable () добавляет несколько операций на график:

  • Переменная op, которая содержит значение переменной.
  • Операция инициализатора, которая устанавливает для переменной ее начальное значение. На самом деле это tf.assign op.
  • Операции для начального значения, такие как нулей op для переменной смещения в примере, также добавляются к графику.

Значение, возвращаемое функцией tf.Variable () value, является экземпляром класса Python tf.Variable .

Инициализация

Инициализаторы переменных должны запускаться явно, прежде чем другие операции в вашей модели могут быть запущены.Самый простой способ сделать это - добавить операцию, которая запускает все инициализаторы переменных, и запустить эту операцию перед использованием модели.

Вы также можете восстановить значения переменных из файла контрольной точки, см. Ниже.

Используйте tf.initialize_all_variables () , чтобы добавить операцию для запуска инициализаторов переменных. Выполняйте эту операцию только после того, как вы полностью построили свою модель и запустили ее в сеансе.

 # Создайте две переменные.
веса = tf.Variable (tf.random_normal ([784, 200], stddev = 0.35),
                      name = "веса")
biases = tf.Variable (tf.zeros ([200]), name = "biases")
...
# Добавить операцию для инициализации переменных.
init_op = tf.initialize_all_variables ()

# Позже при запуске модели
с tf.Session () как сессия:
  # Запустить операцию инициализации.
  сесс.run (init_op)
  ...
  # Используйте модель
  ...
 

Инициализация из другой переменной

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

Чтобы инициализировать новую переменную из значения другой переменной, используйте свойство initialized_value () другой переменной. Вы можете использовать инициализированное значение непосредственно как начальное значение для новой переменной или вы можете использовать его как любой другой тензор для вычисления значения для новой переменной.

 # Создать переменную со случайным значением.
веса = tf. переменная (tf.случайное_нормальное ([784, 200], стандартное отклонение = 0,35),
                      name = "веса")
# Создайте другую переменную с тем же значением, что и «веса».
w2 = tf.Variable (weights.initialized_value (), name = "w2")
# Создайте еще одну переменную с удвоенным значением 'weights'
w_twice = tf.Variable (weights.initialized_value () * 2.0, name = "w_twice")
 

Пользовательская инициализация

Вспомогательная функция tf.initialize_all_variables () добавляет операцию инициализации всех переменных в модели.Вы также можете передать ему явный список переменных для инициализации. Дополнительные параметры, включая проверку инициализации переменных, см. В документации по переменным.

Сохранение и восстановление

Самый простой способ сохранить и восстановить модель - использовать объект tf.train.Saver . Конструктор добавляет сохранить и восстановить операции к графу для всех или указанного списка переменных на графике. Объект saver предоставляет методы для выполнения этих операций, определяя пути к файлам контрольных точек для записи или чтения.

Файлы контрольных точек

Переменные сохраняются в двоичных файлах, которые примерно содержат карту от имен переменных до значений тензора.

При создании объекта Saver вы можете дополнительно выбрать имена для переменных в файлах контрольных точек. По умолчанию он использует значение свойства Variable.name для каждой переменной.

Сохранение переменных

Создайте Saver с tf.train.Saver () для управления всеми переменными в модели.

 # Создайте несколько переменных.
v1 = tf.Variable (..., name = "v1")
v2 = tf.Variable (..., name = "v2")
...
# Добавить операцию для инициализации переменных.
init_op = tf.initialize_all_variables ()

# Добавить операции для сохранения и восстановления всех переменных.
saver = tf.train.Saver ()

# Позже запустите модель, инициализируйте переменные, поработайте, сохраните
# переменные на диск.
с tf.Session () как сессия:
  сесс.run (init_op)
  # Поработайте с моделью.
  ..
  # Сохранить переменные на диск.
  save_path = saver.save (сессия, "/ tmp / model.ckpt ")
  print "Модель сохранена в файле:", save_path
 

Восстановление переменных

Тот же объект Saver используется для восстановления переменных. Обратите внимание, что когда вы восстанавливаете переменные из файла, вам не нужно инициализировать их заранее.

 # Создайте несколько переменных.
v1 = tf.Variable (..., name = "v1")
v2 = tf.Variable (..., name = "v2")
...
# Добавить операции для сохранения и восстановления всех переменных.
saver = tf.train.Saver ()

# Позже запустите модель, воспользуйтесь заставкой для восстановления переменных с диска и
# поработаем с моделью.с tf.Session () как сессия:
  # Восстановить переменные с диска.
  saver.restore (сессия, "/tmp/model.ckpt")
  печать "Модель восстановлена".
  # Поработайте с моделью
  ...
 

Выбор переменных для сохранения и восстановления

Если вы не передаете аргумент tf.train.Saver () , заставка обрабатывает все переменные на графике. Каждый из них сохраняется под именем, которое было передано при создании переменной.

Иногда бывает полезно явно указать имена переменных в файлах контрольных точек.Например, вы могли обучить модель с переменной с именем «weights» , значение которой вы хотите восстановить в новой переменной с именем «params» .

Иногда бывает полезно сохранить или восстановить только подмножество переменных, используемых моделью. Например, вы могли обучить нейронную сеть с 5 слоями, и теперь вы хотите обучить новую модель с 6 слоями, восстанавливая параметры из 5 слоев ранее обученной модели в первые 5 слоев новой модели.

Вы можете легко указать имена и переменные для сохранения, передав конструктору tf.train.Saver () словарь Python: ключи - это имена, которые нужно использовать, значения - это переменные, которыми нужно управлять.

Примечания:

  • Вы можете создать столько объектов заставки, сколько захотите, если вам нужно сохранять и восстанавливать различные подмножества переменных модели. Одна и та же переменная может быть указана в нескольких объектах заставки, ее значение изменяется только при запуске метода restore () .

  • Если вы восстанавливаете только подмножество переменных модели в начале сеанса, вы должны выполнить операцию инициализации для других переменных. См. tf.initialize_variables () для получения дополнительной информации.

 # Создайте несколько переменных.
v1 = tf.Variable (..., name = "v1")
v2 = tf.Variable (..., name = "v2")
...
# Добавить операции для сохранения и восстановления только "v2", используя имя "my_v2"
saver = tf.train.Saver ({"my_v2": v2})
# После этого использовать объект-заставку как обычно.
...
 

Подсказка: как объявлять переменные в JavaScript

Эту статью рецензировали Марк Браун и Мев-Раэль. Спасибо всем рецензентам SitePoint за то, что они сделали контент SitePoint как можно лучше!

При изучении JavaScript одна из основ - понять, как использовать переменные. Переменные - это контейнеры для значений всех возможных типов, например число, строка или массив (см. типы данных). Каждая переменная получает имя, которое позже можно использовать в вашем приложении (например,грамм. чтобы прочитать его значение).

Из этой быстрой подсказки вы узнаете, как использовать переменные и каковы различия между различными объявлениями.

Разница между объявлением, инициализацией и присвоением

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

  1. Объявление : переменная регистрируется с использованием заданного имени в соответствующей области (поясняется ниже - например, внутри функции).
  2. Инициализация : Когда вы объявляете переменную, она автоматически инициализируется, что означает, что память для переменной выделяется механизмом JavaScript.
  3. Присвоение : Это когда переменной присваивается определенное значение.

Типы деклараций

Примечание : в то время как var был доступен в JavaScript с момента его первоначального выпуска, let и const доступны только в ES6 (ES2015) и выше.См. Эту страницу, чтобы узнать о совместимости с браузером.

var

Синтаксис:

  var x;
x = "Привет, мир";


var y = "Hello World";
  

Это объявление, вероятно, является самым популярным, так как альтернативы не было до ECMAScript 6. Переменные, объявленные с помощью var , доступны в области охватывающей функции. Если закрывающая функция отсутствует, они доступны глобально.

Пример:

  function sayHello () {
  var hello = "Привет, мир";
  верни привет;
}
приставка.журнал (привет);
  

Это вызовет ошибку ReferenceError: hello is not defined , поскольку переменная hello доступна только в функции sayHello . Но сработает следующее, поскольку переменная будет объявлена ​​глобально - в той же области видимости console.log (привет) находится :

  var hello = "Привет, мир";
function sayHello () {
  верни привет;
}
console. log (привет);
  

лет

Синтаксис:

  пусть х;
x = "Привет, мир";


let y = "Hello World";
  

let является потомком var в современном JavaScript.Его область действия ограничена не только включающей функцией, но и оператором включающего блока. Оператор блока - это все, что находится внутри { и } (например, условие if или цикл). Преимущество let заключается в том, что он снижает вероятность ошибок, поскольку переменные доступны только в меньшем объеме.

Пример:

  var name = "Питер";
if (name === "Питер") {
  let hello = "Привет, Питер";
} еще {
  let hello = "Привет";
}
console.log (привет);
  

Это вызовет ошибку. ReferenceError: hello is not defined as hello is only available inside the enclosing block - в данном случае if condition.Но будет работать следующее:

  var name = "Питер";
if (name === "Питер") {
  let hello = "Привет, Питер";
  console.log (привет);
} еще {
  let hello = "Привет";
  console.log (привет);
}
  

конст.

Синтаксис:

  const x = «Привет, мир»;
  

Технически константа не является переменной. Особенность константы заключается в том, что вам нужно присвоить значение при ее объявлении, и нет возможности переназначить ее. Константа ограничена областью охватывающего блока, например let .

Константы

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

Случайное глобальное создание

Вы можете записать все названные выше объявления в глобальном контексте (т.е. вне какой-либо функции), но даже внутри функции, если вы забудете написать var , let или const перед присваиванием, переменная будет автоматически становится глобальным.

Пример:

  function sayHello () {
  hello = "Привет, мир";
  верни привет;
}
скажи привет();
console.log (привет);
  

Приведенное выше выведет на консоль Hello World , поскольку перед назначением hello = нет объявления, и, следовательно, переменная доступна глобально.

Примечание: Чтобы избежать случайного объявления глобальных переменных, вы можете использовать строгий режим.

Подъем и временная мертвая зона

Еще одно различие между var и let / const связано с подъемом переменных.Объявление переменной всегда будет внутренне поднято (перемещено) в верхнюю часть текущей области. Это означает следующее:

  console.log (привет);
вар привет;
hello = "Я переменная";
  

эквивалентно:

  var привет;
console.log (привет);
hello = "Я переменная";
  

Признаком такого поведения является то, что оба примера будут записывать undefined в консоль. Если var hello; не всегда будет наверху, это вызовет ошибку ReferenceError .

Это поведение, называемое подъемом, применяется к var , а также к let / const . Как упоминалось выше, доступ к переменной var перед ее объявлением вернет undefined , поскольку это значение, которое JavaScript присваивает при ее инициализации.

Но доступ к переменной let / const до ее объявления вызовет ошибку. Это связано с тем, что они недоступны до их объявления в коде.Период между входом в область действия переменной и достижением их объявления называется временной мертвой зоной, то есть периодом, в течение которого переменная недоступна.

Подробнее о подъеме можно прочитать в статье Демистификация области действия переменных JavaScript и подъема.

Заключение

Чтобы снизить подверженность ошибкам, по возможности следует использовать const и let . Если вам действительно нужно использовать var , обязательно переместите объявления в верхнюю часть области, так как это позволяет избежать нежелательного поведения, связанного с подъемом.

Кодирование

- Насколько важно инициализировать переменную

Неинициализированные переменные делают программу недетерминированной. Каждый раз, когда программа запускается, она может вести себя по-разному. Несвязанные изменения операционной среды, времени суток, фазы луны и их изменения влияют на то, как и когда проявляются эти демоны. Программа может запускаться миллион раз до появления дефекта, они могут делать это каждый раз или запускать еще миллион раз. Многие проблемы приписываются «сбоям» и игнорируются, либо отчеты о дефектах от клиентов закрываются как «Невоспроизводимые».Как часто вы перезагружали машину, чтобы «исправить» проблему? Как часто вы говорили клиенту: «Никогда не видели этого, дайте мне знать, если увидите это снова» - надеясь (зная), что они этого не сделают!

Поскольку воспроизведение дефекта в тестовой среде может быть практически невозможно, его практически невозможно найти и исправить.

На обнаружение ошибки могут уйти годы, обычно в коде, который считается надежным и стабильным. Предполагается, что дефект находится в более свежем коде - его отслеживание может занять значительно больше времени.Изменение компилятора, переключение компилятора, даже добавление строки кода могут изменить поведение.

Инициализация переменных имеет огромное преимущество в производительности не только потому, что программа, которая работает правильно, на бесконечность быстрее, чем та, которая не работает, но разработчики тратят меньше времени на поиск и исправление дефектов, которых не должно быть, и больше времени на выполнение «реальной» работы.

Другое важное преимущество инициализации переменных состоит в том, что исходный автор кода должен решить, чем их инициализировать. Это не всегда тривиальное упражнение, а если и нетривиальное, оно может указывать на плохой дизайн.

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

Изменить: в некоторых языках (например, C #) невозможно использовать неинициализированные переменные, поскольку программа не будет компилироваться или сообщать об ошибке при выполнении, если это сделано.Однако многие языки с такими характеристиками имеют интерфейсы для потенциально небезопасного кода, поэтому при использовании таких интерфейсов необходимо соблюдать осторожность, не вводя неинициализированные переменные.

Инициализация - cppreference.com

Инициализация переменной обеспечивает ее начальное значение во время построения.

Начальное значение может быть предоставлено в разделе инициализатора декларатора или в новом выражении. Это также происходит во время вызовов функций: параметры функции и возвращаемые значения функции также инициализируются.

Для каждого декларатора инициализатор может быть одним из следующих:

( список выражений ) (1)
= выражение (2)
{ список инициализаторов } (3)

1) список произвольных выражений, разделенных запятыми, и списки инициализации в скобках

2) знак равенства, за которым следует выражение

3) список-инициализации в фигурных скобках: возможно, пустой список выражений, разделенных запятыми, и другие списки инициализации в фигурных скобках

В зависимости от контекста инициализатор может вызывать одно из следующих действий:

Если инициализатор не указан, применяются правила инициализации по умолчанию.

Нелокальные переменные

Все нелокальные переменные со статической продолжительностью хранения инициализируются как часть запуска программы до начала выполнения основной функции (если не отложено, см. Ниже). Все переменные с длительностью локального хранилища потока инициализируются как часть запуска потока, упорядочиваются до начала выполнения функции потока. Для обоих этих классов переменных инициализация происходит в два разных этапа:

Статическая инициализация

1) Если разрешено, сначала выполняется инициализация констант (список таких ситуаций см. В разделе «Инициализация констант»).На практике постоянная инициализация обычно выполняется во время компиляции, а предварительно вычисленные представления объектов сохраняются как часть образа программы. Если компилятор этого не делает, он все равно должен гарантировать, что эта инициализация произойдет до любой динамической инициализации.
2) Для всех других нелокальных статических и локальных переменных потока выполняется инициализация нуля.
На практике переменные, которые будут инициализированы нулем, помещаются в сегмент .bss образа программы, который не занимает места на диске и обнуляется ОС при загрузке программы.

Динамическая инициализация

После завершения всей статической инициализации динамическая инициализация нелокальных переменных происходит в следующих ситуациях:

1) Неупорядоченная динамическая инициализация , которая применяется только к элементам статических данных шаблона класса (статическим / локальным для потока) и шаблонам переменных (начиная с C ++ 14), которые не являются явно специализированными. Инициализация таких статических переменных является неопределенной последовательностью относительно всех других динамических инициализаций, за исключением случаев, когда программа запускает поток до инициализации переменной, и в этом случае ее инициализация не имеет последовательности (начиная с C ++ 17).Инициализация таких локальных переменных потока не упорядочена по отношению ко всем остальным динамическим инициализациям.

2) Частично упорядоченная динамическая инициализация , которая применяется ко всем встроенным переменным, которые не являются явно или неявно конкретизированной специализацией. Если частично упорядоченный V определен перед упорядоченным или частично упорядоченным W в каждой единице трансляции, инициализация V упорядочивается до инициализации W (или происходит раньше, если программа запускает поток)

(начиная с C ++ 17)

3) Упорядоченная динамическая инициализация , которая применяется ко всем другим нелокальным переменным: в пределах одной единицы трансляции инициализация этих переменных всегда упорядочена в точном порядке, в котором их определения появляются в исходный код.Инициализация статических переменных в разных единицах трансляции имеет неопределенную последовательность. Инициализация локальных переменных потока в разных единицах трансляции неупорядочена.

Если инициализация нелокальной переменной со статической продолжительностью хранения или продолжительностью хранения потока завершается через исключение, вызывается std :: terminate.

Ранняя динамическая инициализация

Компиляторам разрешается инициализировать динамически инициализированные переменные как часть статической инициализации (по сути, во время компиляции), если выполняются оба следующих условия:

1) динамическая версия инициализации не изменяет значение любого другого объекта области пространства имен до его инициализации

2) статическая версия инициализации производит такое же значение в инициализированной переменной, какое было бы получено при динамической инициализации, если бы все переменные, не требующие статической инициализации, были инициализированы динамически.

Из-за приведенного выше правила, если инициализация некоторого объекта o1 относится к объекту o2 области пространства имен, который потенциально требует динамической инициализации, но определяется позже в той же единице трансляции, не указано, является ли значение o2 будет значением полностью инициализированного o2 (поскольку компилятор продвинул инициализацию o2 для времени компиляции) или будет значением o2 , просто инициализированным нулем.

 встроенный двойной fd () {return 1.0; }
extern double d1;
двойной d2 = d1; // неопределенные:
                  // динамически инициализируется 0,0, если d1 инициализируется динамически, или
                  // динамически инициализируется значением 1.0, если d1 статически инициализирован, или
                  // статически инициализируется значением 0,0 (потому что это будет его значение
                  // если обе переменные были инициализированы динамически)
двойной d1 = fd (); // может быть инициализирован статически или динамически до 1.0 
Отложенная динамическая инициализация

Это определяется реализацией, происходит ли динамическая инициализация - перед первым оператором основной функции (для статики) или начальной функцией потока (для локальных потоков), или отложенная, чтобы происходить после.

Если инициализация не встроенной переменной откладывается до выполнения после первого оператора функции main / thread, это происходит до первого odr-использования любой переменной со статической / поточной продолжительностью хранения, определенной в той же единице перевода, что и инициализируемая переменная.Если никакая переменная или функция не используется odr из данной единицы трансляции, нелокальные переменные, определенные в этой единице трансляции, могут никогда не инициализироваться (это моделирует поведение динамической библиотеки по запросу). Однако, пока что-либо из TU используется odr, все нелокальные переменные, инициализация или уничтожение которых имеет побочные эффекты, будут инициализированы, даже если они не используются в программе.

Если инициализация встроенной переменной отложена, это происходит до первого odr-использования этой конкретной переменной.

(начиная с C ++ 17)
 // - Файл 1 -
#include "a.h"
#include "b.h"
B b;
A :: A () {b.Use (); }

// - Файл 2 -
#include "a.h"
А а;

// - Файл 3 -
#include "a.h"
#include "b.h"
extern A a;
extern B b;

int main () {
  a. Use ();
  б) Использовать ();

// если a инициализируется до ввода main, b все еще может быть неинициализированным
// в точке, где A :: A () использует его (поскольку динамическая инициализация имеет неопределенную последовательность
// через единицы перевода)

// Если a инициализируется в какой-то момент после первого оператора main (который odr-использует
// функция, определенная в файле 1, принудительно запускающая ее динамическую инициализацию),
// тогда b будет инициализирован перед использованием в A :: A 

Статические локальные переменные

Для инициализации локальных (т. Е. Области блока) статических и локальных переменных потока см. Статические локальные переменные.

Члены класса

Нестатические элементы данных могут быть инициализированы списком инициализаторов элементов или инициализатором элементов по умолчанию.

Банкноты

Порядок уничтожения нелокальных переменных описан в std :: exit

Отчеты о дефектах

Следующие ниже отчеты о дефектах, изменяющих поведение, были применены задним числом к ​​ранее опубликованным стандартам C ++.

DR Применяется к Поведение, как опубликовано Правильное поведение
CWG 2026 C ++ 14 zero-init было указано, чтобы всегда происходить первым, даже до constant-init нет нулевой инициализации, если применяется постоянная инициализация

См. Также

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *