Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.67/21: Рейтинг темы: голосов - 21, средняя оценка - 4.67
Велосипедист...
 Аватар для Mournful Max
353 / 220 / 73
Регистрация: 15.12.2015
Сообщений: 785

Раздельная компиляция. Связывание const-переменных

07.09.2017, 13:34. Показов 5087. Ответов 70
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Не совсем понимаю процесс раздельной компиляции, или то как работает связывание const-переменных. Имеется три файла:
const_test.h
C++
1
2
3
4
5
6
7
#ifndef CONST_TEST_H_
#define CONST_TEST_H_
 
extern const int var;  // Это почему работает?
void func( void );
 
#endif  // CONST_TEST_H_

const_test.cpp
C++
1
2
3
4
5
6
7
8
9
#include <iostream>
#include "const_test.h"
 
const int var = 10;  // var имеет внутреннее связывание
 
void func( void )
{
    std::cout << &var << std::endl;
}

general.cpp
C++
1
2
3
4
5
6
7
8
#include <iostream>
#include "const_test.h"
 
int main()
{
    std::cout << &var << std::endl;
    func();
}

Собственно, почему это не вызывает ошибку компиляции? ( Компилировал в VS 2013 ) Разве не должен был произойти конфликт имен, когда в файл "const_test.cpp" включился файл "const_test.h"? Ведь, как я понимаю, extern const int var; -- это отличная переменная от const int var = 10;. Теперь немного изменим код:
const_test.h
C++
1
2
3
4
5
6
7
#ifndef CONST_TEST_H_
#define CONST_TEST_H_
 
extern int var;
void func( void );
 
#endif  // CONST_TEST_H_

const_test.cpp
C++
1
2
3
4
5
6
7
8
9
#include <iostream>
#include "const_test.h"
 
static int var = 10;
 
void func( void )
{
    std::cout << &var << std::endl;
}

general.cpp
C++
1
2
3
4
5
6
7
8
#include <iostream>
#include "const_test.h"
 
int main()
{
    std::cout << &var << std::endl;
    func();
}

Это уже ошибка. И это, по-моему нормально ( а по законам C++ нет? ), ведь инструкция extern int var; 1) порождает конфликт имен, 2) ссылается на несуществующую переменную ( ведь ни в одной единице трансляции не была объявлена внешняя int переменная с именем var ). В коде изменилось ( опять же таки, по моему мнению ) только то, что переменная var перестала быть const.

P.S.: Знаю, что не умею доступно объяснять, но у здешних жителей форума, как показывает практика, хорошо развиты телепатические способности, проблем возникнуть не должно
1
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
07.09.2017, 13:34
Ответы с готовыми решениями:

Раздельная компиляция
Здравствуйте, я перечитал на форуме информацию по этой теме, но пока что не сумел разобраться. У меня в проекте такие файлы: ...

Раздельная компиляция
Пусть есть программа, которая состоит из 2 файлов. 1 файл: основной, там где main 2 файл: содержит некоторые функции Также есть...

раздельная компиляция
Вот есть три файла: заголовочный файл с классом (.h), файл с реализацией методов класса (.cpp), и функцию main. Как, корректно, выполнить...

70
17 / 16 / 3
Регистрация: 18.08.2017
Сообщений: 54
08.09.2017, 21:44
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от hoggy Посмотреть сообщение
gcc вестимо лажает.
gcc лажает, у vc++ надо оптимизации отключить ...
Как тут не вспомнить известную русскую пословицу про танцора, которому всё мешает?

Добавлю сюда ещё clang
https://wandbox.org/permlink/VtaPdwC4zXeb0EHW
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
 
template<class type>
const type* addr(const type& val)
{
    return &val;
}
 
int main()
{
    std::cout << addr(10) << '\n';
    std::cout << addr(11) << '\n';
    
    const int* addr_of_10 = addr(10);
    const int* addr_of_11 = addr(11);
    
    std::cout << *addr_of_10 << '\n';
    std::cout << *addr_of_11 << '\n';
}
Вывод:
Code
1
2
3
4
0x7ffc0875b8e4
0x7ffc0875b8e4
0
4208112
clang тоже лажает?

Цитата Сообщение от hoggy Посмотреть сообщение
хотите сказать, что код в #35 содержит UB?
Да вроде как не содержит.

Ferrari F1, кому прояснит? Мне-то всё ясно. ;)
0
Велосипедист...
 Аватар для Mournful Max
353 / 220 / 73
Регистрация: 15.12.2015
Сообщений: 785
09.09.2017, 03:57  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
А почему бы этому и не работать? Это обычное объявление const переменной с внешним связыванием.
Я начинаю понимать. Хорошо. Можно потолковать? Возьмем пустой проект, не касающийся предоставленных выше примеров. ( Объявления/определения переменных будут на уровне единицы трансляции )
1. Следующие две переменные, определенные таким образом: int var_1 = 0;, extern const int var_2 = 0; имеют внешнее связывание, верно?
2. Если мы хотим получить доступ к определенным выше переменным в соседней единице трансляции, мы должны написать следующее: extern int var_1; extern const int var_2;, верно?
3. Когда нам нужно определить переменную с внутренним связываем, мы поступаем так: static int var_3 = 0; ( в случае с обычной переменной ) и static const int var_4 = 0; или const int var_4 = 0; ( в случае с константной переменной ), верно?

Что мне остается непонятным:
Если определение static const int var = 0; эквивалентно определению const int var = 0; ( имеет внутреннее связывание по умолчанию ), то почему последнее ( const int var = 0; ) будет трактоваться как определение выше объявленной внешней extern const int var; ( если таковая имеется )? Получается, const int var = 0; двусмысленно? Двусмысленно в том смысле, что когда выше есть объявление ( extern const int var; ) внешней константной переменной с таким же именем, const int var = 0; трактуется как определение этой переменной. Если же выше нет никаких объявлений внешних переменных, const int var = 0; трактуется как определение независимой переменной с внутренним связыванием? Вот именно этот момент мне непонятен.

Добавлено через 1 минуту
Извиняюсь перед всеми за дотошность. Ну или тупость.
Я хочу освоить этот язык, поэтому должен добиться желаемого: узнать все как есть.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13210 / 6843 / 1824
Регистрация: 18.10.2014
Сообщений: 17,306
09.09.2017, 04:13
Лучший ответ Сообщение было отмечено Captain Maxee как решение

Решение

Цитата Сообщение от Captain Maxee Посмотреть сообщение
1. Следующие две переменные, определенные таким образом: int var_1 = 0;, extern const int var_2 = 0; имеют внешнее связывание, верно?
Верно.

Оба объявления также являются определениями.

Цитата Сообщение от Captain Maxee Посмотреть сообщение
2. Если мы хотим получить доступ к определенным выше переменным в соседней единице трансляции, мы должны написать следующее: extern int var_1; extern const int var_2;, верно?
Да, верно.

Цитата Сообщение от Captain Maxee Посмотреть сообщение
3. Когда нам нужно определить переменную с внутренним связываем, мы поступаем так: static int var_3 = 0; ( в случае с обычной переменной ) и static const int var_4 = 0; ( в случае с константной переменной ), верно?
Да, верно.

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

Цитата Сообщение от Captain Maxee Посмотреть сообщение
Если определение static const int var = 0; эквивалентно определению const int var = 0; ( имеет внутреннее связывание по умолчанию ), то почему последнее ( const int var = 0; ) будет трактоваться как определение выше объявленной внешней extern const int var; ( если таковая имеется )?
Потому что совпадение имен (var в обоих случаях), говорит о том, что это - объявление одной и той же сущности, т.е. объявление одного и того же const объекта по имени var. Первое объявление явным образом назначило этому объекту внешнее связывание. После этого правило "внутреннее связывание по умолчанию" уже не применимо. Уже нет никакого "умолчания". Внешнее связывание было назначено явно.

Цитата Сообщение от Captain Maxee Посмотреть сообщение
Получается, const int var = 0; двусмысленно? Двусмысленно в том смысле, что когда выше есть объявление ( extern const int var; ) внешней константной переменной с таким же именем, const int var = 0; трактуется как определение этой переменной. Если же выше нет никаких объявлений внешних переменных, const int var = 0; трактуется как определение независимой переменной с внутренним связыванием? Вот именно этот момент мне непонятен.
Не двусмысленно, а не является самодостаточным объявлением, полный смысл которого можно понять только из анализа его самого. Его полный смысл также зависит от контекста, причем как от предыдущего, так и от последующего. То есть и в этом

C++
1
2
extern const int var;
const int var = 0;
и в этом

C++
1
2
const int var = 0;
extern const int var;
случае мы получаем const переменную с внешним связыванием. И только если переменная var в данной единице трансляции нигде явно не объявляется как extern, вступает в силу умолчательное правило - const переменная var получает внутреннее связывание.
3
Велосипедист...
 Аватар для Mournful Max
353 / 220 / 73
Регистрация: 15.12.2015
Сообщений: 785
09.09.2017, 04:25  [ТС]
TheCalligrapher, спасибо. Теперь все понятно.

Добавлено через 2 минуты

Не по теме:

GbaLog-, hoggy, теперь я понял, о чем вы писали. Мне нужно было просто разжевать, я не понимал.

0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
09.09.2017, 20:09
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
У числового литерала никакого адреса нет и быть не может.
хотите сказать, что данный код не корректен?
C++
1
2
3
const int& ref = 10;
const int* ptr = &ref;
std::cout << "address = " << ptr << std::endl;
Цитата Сообщение от УГнетатель Посмотреть сообщение
Как тут не вспомнить известную русскую пословицу про танцора, которому всё мешает?
действительно.
вы написали код, который содержит UB )))
и как тут теперь не вспомнить то про знаменитую пословицу?

Цитата Сообщение от УГнетатель Посмотреть сообщение
Да вроде как не содержит.
содержит ^^

теперь, немножко ловкости рук, и никакой фантастики:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
 
template<class type>
const type* addr(const type& val)
{
    return &val;
}
 
int main()
{
    std::cout << *addr(10) << '\n';
    std::cout << *addr(11) << '\n';
    /*
    const int* addr_of_10 = addr(10);
    const int* addr_of_11 = addr(11);
    
    std::cout << *addr_of_10 << '\n';
    std::cout << *addr_of_11 << '\n';
    */
}
теперь поведение предсказуемое и не зависит от оптимизаций

в чем здесь подвох?
в строке:
C++
1
const int* addr_of_10 = addr(10);
указатель смотрел на труп.
1
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
09.09.2017, 20:18
Цитата Сообщение от hoggy Посмотреть сообщение
хотите сказать, что данный код не корректен?
между вашим примером и &10 есть разница, как бы.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13210 / 6843 / 1824
Регистрация: 18.10.2014
Сообщений: 17,306
09.09.2017, 20:26
Цитата Сообщение от hoggy Посмотреть сообщение
хотите сказать, что данный код не корректен?
C++
1
2
3
const int& ref = 10;
const int* ptr = &ref;
std::cout << "address = " << ptr << std::endl;
Данный код совершенно корректен. Но никакого отношения к взятию "адреса числового литерала" он не имеет. Строчка

C++
1
const int& ref = 10;
создает безымянный временный объект типа int, проинициализированный значением 10. Ссылка ref привязывается к этому временному объекту и вся дальнейшая работа ведется именно с этим временным объектом. Адрес, который вы печатаете - это адрес этого временного объекта.

Роль числового литерала 10 в этом коде заканчивается сразу после инициализации временного объекта. Все остальное, происходящее в этом коде к числовому литералу 10 никакого отношения не имеет.
1
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
09.09.2017, 20:28
Цитата Сообщение от GbaLog- Посмотреть сообщение
между вашим примером и &10 есть разница, как бы.
конечно есть.
с точки зрения синтаксиса.
но не с точки зрения практического смысла.

рассмотрим ещё раз этот самый мой пример:
C++
1
2
3
const int& ref = 10;
const int* ptr = &ref;
std::cout << "address = " << ptr << std::endl;
теперь для вас будет сложный вопрос:
адрес какого объекта выводится в консоль?

если ниже вы допишите:
C++
1
std::cout << "value = " << *ptr << std::endl;
что за значение буде выведено в консоль?

ответьте себе на эти вопросы,
а затем заново внимательно прочитайте #23
который я написал специально для вас.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13210 / 6843 / 1824
Регистрация: 18.10.2014
Сообщений: 17,306
09.09.2017, 20:33
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Роль числового литерала 10 в этом коде заканчивается сразу после инициализации временного объекта. Все остальное, происходящее в этом коде к числовому литералу 10 никакого отношения не имеет.
Другими словами, ваш код - это не более чем сокращенная форма записи для

C++
1
2
3
4
5
const int __internal_temp_var = 10;
const int &ref = __internal_temp_var;
const int *ptr = &ref;
...
// И т.д.
То есть никакого "адреса числового литерала" тут нет.
1
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
09.09.2017, 20:35
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
создает безымянный временный объект типа int, проинициализированный значением 10
другими словами мы получаем доступ к объекту числовой литерной константы.
у которого можно взять адрес.

а значит,
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Но никакого отношения к "адресу числового литерала" он не имеет.
имеет прямое отношение к "адресу числового литерала"

мы имеем полное право воспринимать циферки (числовые литеральные константы )
как полноценные prvalue объекты.
0
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
09.09.2017, 20:38
hoggy, смотрите #40, #47.
0
17 / 16 / 3
Регистрация: 18.08.2017
Сообщений: 54
09.09.2017, 20:39
Цитата Сообщение от hoggy Посмотреть сообщение
содержит ^^
Тем, кого подводит память, напоминаю, что вопрос был про код в сообщении #35:
Цитата Сообщение от hoggy Посмотреть сообщение
хотите сказать, что код в #35 содержит UB?
Дублирую код из сообщения 35 здесь:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
 
template<class type>
const type* addr(const type& val)
{
    return &val;
}
 
int main()
{
    std::cout << addr(10) << '\n';
    std::cout << addr(11) << '\n';
}
Вы зачем-то подсунули вместо этого какой-то другой код.


Цитата Сообщение от hoggy Посмотреть сообщение
теперь поведение предсказуемое и не зависит от оптимизаций
Утверждение неверно, т.к. от оптимизаций зависит:
http://rextester.com/RJA75666 vc++, без оптимизаций
http://rextester.com/SNLFL9050 vc++, с оптимизациями
https://wandbox.org/permlink/ZDkg0jC8uRNrZyb1 g++, без
https://wandbox.org/permlink/fPiM0vyJanSZ4blm g++, с


Цитата Сообщение от hoggy Посмотреть сообщение
указатель смотрел на труп.
Рад, что вы хоть это понимаете.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13210 / 6843 / 1824
Регистрация: 18.10.2014
Сообщений: 17,306
09.09.2017, 20:43
Цитата Сообщение от hoggy Посмотреть сообщение
другими словами мы получаем доступ к объекту числовой литерной константы.
у которого можно взять адрес.
Нет, мы получаем доступ к временному объекту, который не имеет вообще никакого отношения к "числовой литерной константе". Он лишь в момент своего рождения на один миг случайно "встретился" с этой константой - для инициализации - а после этого забыл о ее существовании навсегда и больше не имеет к ней никакого отношения.
1
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
09.09.2017, 20:46
Цитата Сообщение от GbaLog- Посмотреть сообщение
смотрите #40, #47.
см #50

Цитата Сообщение от УГнетатель Посмотреть сообщение
Утверждение неверно, т.к. от оптимизаций зависит:
ну значит, компиляторы лажают.
0
17 / 16 / 3
Регистрация: 18.08.2017
Сообщений: 54
09.09.2017, 20:47
Цитата Сообщение от hoggy Посмотреть сообщение
ну значит, компиляторы лажают.
Я так не думаю.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
09.09.2017, 20:53
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Нет, мы получаем доступ к временному объекту, который не имеет вообще никакого отношения к "числовой литерной константе". Он лишь в момент своего рождения на один миг случайно "встретился" с этой константой - для инициализации - а после этого забыл о ее существовании навсегда и больше не имеет к ней никакого отношения.
и чем это с практической точки зрения отличается от:
"мы получаем доступ к временному объекту, который и есть объект числовой константы" ?

ничем.

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

таких ситуаций нет.

Добавлено через 2 минуты
Цитата Сообщение от УГнетатель Посмотреть сообщение
Я так не думаю.
мне без разницы как вы думаете.
значение имеет стандарт языка.

либо код содержит UB,
либо лажают компиляторы.
0
09.09.2017, 20:55  [ТС]

Не по теме:

Цитата Сообщение от hoggy Посмотреть сообщение
прочитайте #23
Цитата Сообщение от GbaLog- Посмотреть сообщение
смотрите #40, #47.
Цитата Сообщение от hoggy Посмотреть сообщение
см #50
* :D

0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13210 / 6843 / 1824
Регистрация: 18.10.2014
Сообщений: 17,306
09.09.2017, 20:55
Цитата Сообщение от hoggy Посмотреть сообщение
мы имеем полное право воспринимать циферки (числовые литеральные константы )
как полноценные prvalue объекты.
В С++ нет понятия "prvalue объекта". Объект - это объект. Prvalue - это категория результата выражения.

Единственный контекст, в котором эти понятия встречаются, т.е. в котором prvalue связано с так называемым "result object" - это prvalue типа "класс" или "массив". Prvalue типа int ни с каким объектом не связано. Но даже в тех случаях, когда "result object" существует, этот объект - это не само prvalue, а отдельный самостоятельный объект, лишь инициализированный при помощи этого prvalue.
2
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,825
09.09.2017, 20:58
Не хочу ввязываться в эти бесполезные споры, только уточню насчет этого:
Цитата Сообщение от hoggy Посмотреть сообщение
ну значит, компиляторы лажают.
Не лажают. Согласно стандарту компилятор может переиспользовать память объекта после его смерти. Это совершенно легально - см. параграф 3.8 Object Lifetime. Поэтому то, что второй объект разместился в памяти первого после его смерти, нельзя назвать лажей компилятора.
2
09.09.2017, 21:00  [ТС]

Не по теме:

Эти беседы напоминают спор про: "Стакан наполовину полон или наполовину пуст?" :stop:

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
09.09.2017, 21:00

Раздельная компиляция
В Гугле не забанен,нашел описание самого принципа,но конкретных примеров не нашел. Вопросы вот какие: 1) Если у меня есть класс,...

Шаблоны и раздельная компиляция
Здравствуйте! Если просто я пишу класс, например: class Test { private: int *pm; ... public: bool inc();

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

Раздельная компиляция и шаблоны
Есть 3 файла с кодом: 1) foo.hpp template&lt;typename T&gt; class foo { public: T bar; foo(); ~foo(); };

Раздельная компиляция и шаблоны
привет. возьму вот такой выдуманный пример #ifndef CCLASS_H #define CCLASS_H #include &lt;functional&gt; class...


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

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Новые блоги и статьи
GitDOS: Когда я решил скрестить MS-DOS, GitHub и Claude
MaGz GoLd 08.07.2026
Привет, Киберфорум! Хочу поделиться своим проектом, который, надеюсь, вас заинтересует. GitDOS — это веб-приложение, которое позволяет запускать DOS в браузере с тремя крутыми фичами: диски живут в. . .
Учебный мини "цифровой двойник" = (Сервер = Java + Flask_питон )+(3D = Гугловскаая 4T nvideo + google colab) +( WMS = Odoo на Docker)
anaschu 07.07.2026
Увидел в требованиях к вакансии создателя имитационных моделей связку (REst + Api + WMS + Anylogic + питон + 3D Nvideo), и решил посмотреть, что это такое. Из этого всего я работал более менее. . .
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
Doom для терминала без стрельбы и монстров. 3D Raycasting на ascii.
dcc0 05.07.2026
Попросил нейронную сеть deepai. org написать рейкастинг 3D с библиотекой ncurses для Linux. Чтобы можно было ходить на стрелочки. Чтобы стены были отрисованы символами. Справилась. Первый вариант. . .
Установка статуса документа по условию
Maks 05.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "НарядПутевка" разработанного в КА2. Задача: в табличной части "Материалы" документа при записи автоматически устанавливать статус. . .
Сезонность и суточность закисления почв
anaschu 04.07.2026
200 часов это все равно моловато. Есть ситуации, но нестандартные, когда смена происходит за 5 лет. Но обычно это 50 лет и более. Наверное, закисление почвы происходит сезонно в средней. . .
В чем ценность человеческого опыта в глобальном смысле?
kumehtar 03.07.2026
Возможно, ценность человека не в том, что он однажды достигает мудрости, а в том, что он становится носителем карты пути. Он знает не только истину, но и последовательность внутренних изменений,. . .
интеграция AnyLogic с самописным REST API и переход на Odoo
anaschu 03.07.2026
Успешная интеграция AnyLogic с самописным REST API и переход на промышленную Odoo WMS Сегодня проделал огромный путь от простой симуляции физических процессов до построения полноценной. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru