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

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

07.09.2017, 13:34. Показов 5113. Ответов 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
807 / 534 / 158
Регистрация: 27.01.2015
Сообщений: 3,017
Записей в блоге: 1
07.09.2017, 17:35
Студворк — интернет-сервис помощи студентам
hoggy,
C++
1
2
3
4
namespace {
    
    static int a = 20;
}
тут ведь совсем не обязательно static приписывать? получается, что мы совмещаем сишный и плюсовый подход касательно внутреннего связывания, хотя достаточно выбрать одно из двух
(либо безымянный namespace, либо static)
1
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
07.09.2017, 17:35
Цитата Сообщение от Fulcrum_013 Посмотреть сообщение
Константа в отличии от переменной не имеет адреса.
что ж мой компилятор, зараза такая, тогда мне адрес показывает?
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
 
int main()
{
    int f;
    
    std::cin >> f;
    
    const int u = f;
    const int l = 123;
    
    std::cout << &f << "\n" << &l << "\n";
}
это у енумов нет адресов и вычисляются они всегда на этапе компиляции, поэтому адресов и нету.
а у констант есть, они ничем не отличаются от обычных переменных, кроме того факта, что их изменять нельзя.

P.S. если я не прав, то приведите пункт стандарта, где мой выпад распадается в пух и прах.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
07.09.2017, 18:30
Цитата Сообщение от Ferrari F1 Посмотреть сообщение
тут ведь совсем не обязательно static приписывать?
да.

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

Добавлено через 14 секунд
Цитата Сообщение от Ferrari F1 Посмотреть сообщение
тут ведь совсем не обязательно static приписывать?
да.

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

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

в этом моменте, между вторым и тьретьим разницы вообще нет никакой.
между константной переменной и числовой константой (или енумом)
различие заключается в том,
что адрес у них можно срисовать лишь опосредованно,
через ссылку:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
 
enum eVALUE { eSAMPLE };
 
template<class type>
const type* addr(const type& val)
{
    return &val; // ok
}
 
int main()
{
    const int v = 10;
    
    std::cout << &v; //ok
    std::cout << &"ololo"; //ok
    
    //error: lvalue required as unary ‘&’ operand
    //std::cout << &10;      
    
    //error: lvalue required as unary ‘&’ operand
    //std::cout << &eSAMPLE;   
    
    std::cout << addr(10); //ok
    std::cout << addr(eSAMPLE); //ok
}
0
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
07.09.2017, 19:31
Цитата Сообщение от hoggy Посмотреть сообщение
C++
1
2
3
4
5
template<class type>
const type* addr(const type& val)
{
* * return &val; // ok
}
э-хе, вообще-то тут возвращается адрес локального объекта val.
так что нет адресов у них, они подставляются в коде.
у строковых литералов можно брать адрес потому, что они особенные и это такая оптимизация, что они располагаются в статической памяти. но это тоже происходит не всегда.
слышал о случаях, когда они просто в ассемблер встраивались обычными числами.
0
807 / 534 / 158
Регистрация: 27.01.2015
Сообщений: 3,017
Записей в блоге: 1
07.09.2017, 19:32
DrOffset, Раздельная компиляция. Связывание const-переменных
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
07.09.2017, 19:37
Цитата Сообщение от GbaLog- Посмотреть сообщение
э-хе, вообще-то тут возвращается адрес локального объекта val
здесь нет никакого "локального объекта val"
есть аргумент-ссылка 'val' которая обозначает объект,
переданный в функцию снаружи.

операция взятия адреса у ссылки - есть операция
взятия адреса у оргинального объекта.

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

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

адрес литерной строки можно брать просто потому,
что можно - таковы правила игры.
всегда.
0
Любитель чаепитий
 Аватар для GbaLog-
3745 / 1801 / 566
Регистрация: 24.08.2014
Сообщений: 6,020
Записей в блоге: 1
07.09.2017, 19:46
Цитата Сообщение от hoggy Посмотреть сообщение
переданный в функцию снаружи.
передаётся rvalue, у rvalue нельзя брать адрес, как бы на этом спор можно кончить.
по не константной ссылке попробуйте передать и как бы будет как бы ошибка как бы компиляции.
константная ссылка просто продлевает жизнь объекта, который создаётся на входе в функцию.
это всё.
никаких адресов у rvalue не существует, с точки зрения стандарта.
http://en.cppreference.com/w/c... e_category
Цитата Сообщение от cppreference
rvalue
An rvalue expression is either prvalue or xvalue.
Properties:
- Address of an rvalue may not be taken: &int(), &i++[3], &42, and &std::move(x) are invalid.
Цитата Сообщение от hoggy Посмотреть сообщение
адрес литерной строки можно брать просто потому,
что можно - таковы правила игры.
я и не спорю, только вот если адрес этой константы не нужен, то его не за чем генерировать даже.
компилятор вправе применить оптимизации, если это никак не повлияет на результат программы.
0
Велосипедист...
 Аватар для Mournful Max
353 / 220 / 73
Регистрация: 15.12.2015
Сообщений: 785
07.09.2017, 20:02  [ТС]
Цитата Сообщение от hoggy Посмотреть сообщение
она итак будет с внешним связыванием
Не согласен. Приведу пример.
Вариант 1:
const_test.h
C++
1
2
3
4
5
6
7
#ifndef CONST_TEST_H_
#define CONST_TEST_H_
 
int var = 10;  // oops!
void func();
 
#endif  // CONST_TEST_H_

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

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

Вариант 2:
const_test.h
C++
1
2
3
4
5
6
7
#ifndef CONST_TEST_H_
#define CONST_TEST_H_
 
const int var = 10;  // all right!
void func();
 
#endif  // CONST_TEST_H_

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

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

Первый вариант вызывает ошибку компиляции, а второй вариант компилируется успешно, создаются две независимые переменные var.
0
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,825
07.09.2017, 21:22
Вот две полезные темы, для внимательного прочтения:
Несколько *.cpp и глобальные переменные
Extern в C/C++
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
08.09.2017, 02:08
Цитата Сообщение от GbaLog- Посмотреть сообщение
передаётся rvalue, у rvalue нельзя брать адрес, как бы на этом спор можно кончить.
rvalue - способ доступа к объекту.
и не более того.
в представленном мой способе, передается lvalue.
rvalue прекрасно конвертится в lvalue,
когда lvalue - обозначает константу.
при этом адрес самого объекта никак не меняется.

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

Добавлено через 1 минуту
Цитата Сообщение от Captain Maxee Посмотреть сообщение
Не согласен. Приведу пример.
мне не очевидно с чем именно вы не согласны,
и что именно вы хотели показать своими примерами
0
70 / 70 / 35
Регистрация: 06.07.2016
Сообщений: 415
08.09.2017, 07:52
Fulcrum_013,
Неверно.
Константа является обычной переменной, для которой выделяется память, а не подставляемым значением, если :
1.Где либо в программе берется ее адрес.
2.Она является "внешней констаной", объявленной с extern.
3.В некоторых других случаях, о которых непосредственно можно узнать из документации компилятора.

"Философия С++", Брюс Эккель.
0
17 / 16 / 3
Регистрация: 18.08.2017
Сообщений: 54
08.09.2017, 18:26
Цитата Сообщение от hoggy Посмотреть сообщение
а теперь правильный ответ:
и у литерных, и у числовых, и у енумовских констант можно брать адрес.
Числовой литерал это последовательность цифр (плюс всякие префиксы, вроде 0x, но для простоты забудем про них). Что такое адрес числового литерала? Смещение в файле с исходным кодом?

Цитата Сообщение от hoggy Посмотреть сообщение
что адрес у них можно срисовать лишь опосредованно,
через ссылку
Не работает. Вообще бред выдает. У 10 и 11 один адрес?! Почему 10 не лежит по своему адресу? Бред.....

https://wandbox.org/permlink/K7BSiLhI9m2m4327
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#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);
    
    std::cout << *addr_of_10 << '\n';
}
Вывод:
Code
1
2
3
0x7ffcd2adad7c
0x7ffcd2adad7c
0
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
08.09.2017, 18:37
Цитата Сообщение от УГнетатель Посмотреть сообщение
Что такое адрес числового литерала?
адрес объекта.
ваш К. О.

Цитата Сообщение от УГнетатель Посмотреть сообщение
Не работает.
gcc вестимо лажает.

http://rextester.com/KIKT98113
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13210 / 6843 / 1824
Регистрация: 18.10.2014
Сообщений: 17,306
08.09.2017, 18:48
Цитата Сообщение от Captain Maxee Посмотреть сообщение
C++
1
extern const int var; // Это почему работает?
А почему бы этому и не работать? Это обычное объявление const переменной с внешним связыванием.

Цитата Сообщение от Captain Maxee Посмотреть сообщение
C++
1
2
3
#include "const_test.h"
 
const int var = 10; // var имеет внутреннее связывание
Нет. var имеет внешнее связывание. Так как вы включили const_test.h в const_test.cpp, в этом месте видно предыдущее объявление var как переменной с внешним связыванием. Оно теперь так и останется внешним, как было изначально объявлено. "Отменить" внешнее связывание уже не получится.

А в main вы просто успешно линкуетесь к этой переменной с внешним связыванием.

А вот если убрать включение const_test.h именно из const_test.cpp, тогда это определение в const_test.cpp действительно создаст переменную с внутренним связыванием и программа перестанет линковаться.
1
17 / 16 / 3
Регистрация: 18.08.2017
Сообщений: 54
08.09.2017, 19:07
Цитата Сообщение от hoggy Посмотреть сообщение
адрес объекта.
Речь шла про литералы. Откуда вдруг объекты взялись?

Цитата Сообщение от hoggy Посмотреть сообщение
gcc вестимо лажает.
Не сильно больше, чем vc++
http://rextester.com/DPFY59334

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';
}
Вывод:
Code
1
2
3
4
5
/LIBPATH:C:\boost_1_60_0\stage\lib 
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
 
0000007D4EFCF930
0000007D4EFCF930
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
08.09.2017, 19:19
Цитата Сообщение от УГнетатель Посмотреть сообщение
Не сильно больше, чем vc++
очевидно жеж - оптимизации влияют.
уберите ключик /Ox

Добавлено через 1 минуту
Цитата Сообщение от УГнетатель Посмотреть сообщение
Речь шла про литералы. Откуда вдруг объекты взялись?
литералы - это такие "литеральные объекты".
вполне себе полноценные.
внезапно?
0
17 / 16 / 3
Регистрация: 18.08.2017
Сообщений: 54
08.09.2017, 19:30
Цитата Сообщение от hoggy Посмотреть сообщение
очевидно жеж - оптимизации влияют.
Очевидно только одно: у числовых литералов нельзя брать адрес. &10 не скомпилируется. Вне зависимости от оптимизаций. Спорить с этим бессмысленно.

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

Цитата Сообщение от hoggy Посмотреть сообщение
литералы - это такие "литеральные объекты".
В нашем дорогом и уважаемом стандарте языка словосочетание literal object нашлось только 1 (один) раз, в разделе про строковые литералы. Вот у них (строковых литералов) можно брать адрес, так как за строковым литералом стоит литеральный объект. причём он имеет static storage duration. Этот объект, естественно, имеет адрес.

За литералом 10 самим-по-себе нет никакого объекта. И адрес у него взять нельзя.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
08.09.2017, 19:34
Цитата Сообщение от УГнетатель Посмотреть сообщение
Очевидно только одно: у числовых литералов нельзя брать адрес
хотите сказать, что код в #35 содержит UB?

Цитата Сообщение от УГнетатель Посмотреть сообщение
За литералом 10 самим-по-себе нет никакого объекта.
функция
C++
1
2
template<class type>
const type* addr(type&& val)
с вами не согласна
0
807 / 534 / 158
Регистрация: 27.01.2015
Сообщений: 3,017
Записей в блоге: 1
08.09.2017, 19:37
УГнетатель, хз, может быть это что то прояснит:
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13210 / 6843 / 1824
Регистрация: 18.10.2014
Сообщений: 17,306
08.09.2017, 20:30
Лучший ответ Сообщение было отмечено GbaLog- как решение

Решение

Тема "литералов" и "взятия адреса" тут уже много раз обсуждалась.

Оператор взятия адреса разрешается применять только к lvalue-выражениям.

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

Выражение, состящее из имени enum-константы не является lvalue, поэтому его адрес брать нельзя. У enum-константы никакого адреса нет и быть не может.

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

Выражение, состящее из строкового литерала, является lvalue. У строковых литералов есть адреса.
3
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
08.09.2017, 20:30

Раздельная компиляция
В Гугле не забанен,нашел описание самого принципа,но конкретных примеров не нашел. Вопросы вот какие: 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...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
сукцессия 29. Переход от одних деревьев на другие делать более или менее вероятностным?
anaschu 12.07.2026
Насколько смена типов микоризы — исключительное событие в двухвековой сукцессии? Оценка вероятности в пространстве параметров В текущей версии модели успешно реализован ключевой механизм. . .
сукцессия 27. Думаю, как переделывать уже написанную статью с планами на сукцессию.
anaschu 12.07.2026
Анализ соответствия модели требованиям Реализованные компоненты: Механизм закисления почвы через протонную помпу Конкуренция между типами микориз pH как триггер сукцессии C/ P соотношение. . .
Сукцессия 26. Мат модель создана.
anaschu 12.07.2026
Модель смены растительных сукцессий посредством управления грибами работает внутри небольшой ячейки почвы, восстанавливающейся после пожара, где ненадолго бывшее царство хвойных снова захватили. . .
Решил проблему с ошибкой пагинации сообщений с сервера на алгоритме обхода дерева "Эстафета хвоста".
Hrethgir 12.07.2026
Проблема была в том, что удалялась именно новая кнопка, а не старая. Ни один ИИ не обнаружил это, а сам я смог только когда с работой стало попроще и когда заставил работать будущее автономное. . .
сукцессия 25. Хронология ошибок
anaschu 12.07.2026
# От 50-тонного гриба до устойчивого леса: хроника ошибок при построении модели вековой сукцессии микоризы ## О чём эта статья В процессе построения ОДУ-модели (система дифференциальных. . .
сукцессия 24. Промежуточное общее описание модели
anaschu 12.07.2026
Хендофф: модель АМ→ЭКМ сукцессии микоризы (ризосфера, 50 лет) Содержание проекта Симуляция вековой (50 лет) экологической сукцессии в почве леса Основные участники: АМ-гриб, ЭКМ-гриб,. . .
сукцессия 23. Более физиологичная физиология, более экологичная экология, более диффурные диффуры.
anaschu 12.07.2026
Что реально нашли и починили за эти 5 часов Правило Линдемана (КПД конверсии сахара в тело, kEff) — раньше 100% полученного углерода шло прямо в биомассу гриба; теперь только kEff=0. 5 (после. . .
сукцессия 22. От артефактов к физиологии: калибровка агентной модели грибной сукцессии для воспроизведения сезонной динамики и pH-плато
anaschu 11.07.2026
Аннотация В данной работе представлена калибровка агентной модели динамики грибных сообществ (fungal-succession), направленная на устранение нефизичных артефактов (коллапс биомассы, мгновенное. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru