Ошибка lnk2001 неразрешенный внешний символ static

Есть класс.

class some{
public:
    static int Instances;
    static int TotalSumm;
public:
    some() {  // Конструктор
        Instances = 0;
    }
}

Этот код не собирается. Ошибка:

LNK2001 «неразрешённый внешний символ».

В чём может быть проблема?

Nicolas Chabanovsky's user avatar

задан 21 мая 2016 в 9:18

user211023's user avatar

1

Вы объявили статическое поле. Но не определили.

Добавьте

int some::Instances;
int some::TotalSumm;

вне класса.

Вдогонку — вам точно надо обнулять член класса при создании каждого экземпляра?

Update. Дабы не нарушать правило одного определения, эти определения должны располагаться в .cpp-файле, в единственном экземпляре. При их размещении в заголовочном файле они будут определены в каждом .cpp— файле проекта…

ответ дан 21 мая 2016 в 9:26

Harry's user avatar

HarryHarry

215k15 золотых знаков117 серебряных знаков228 бронзовых знаков

9

потому что нужно проинициализировать за пределами класса

class some{
public:
    static int Instances;
    static int TotalSumm;
public:
    some()
    {  
        Instances = 0;
    }
};

int some::Instances = 0;

int main(int argc, char *argv[])
{
    some::Instances = 5;
    return 0;
}

Qwertiy's user avatar

Qwertiy

121k24 золотых знака121 серебряный знак291 бронзовый знак

ответ дан 21 мая 2016 в 9:27

Александр's user avatar

АлександрАлександр

4,0301 золотой знак10 серебряных знаков19 бронзовых знаков

3

garjo_099

1 / 1 / 1

Регистрация: 08.04.2014

Сообщений: 31

1

09.04.2014, 00:39. Показов 14866. Ответов 23

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

Всем доброго времени суток.
Изучаю С++ по Шпаку и по урокам на сайте

Кликните здесь для просмотра всего текста

cppstudio.com

, предварительно хорошо ознакомившись с ассемблером…
Со всеми темами прорываюсь вперед без особых затруднений, дошел до изучения классов, начали появляться вопросы, вот решил найти себе пристанище для ответов.
По первому же запросу в гугле попал сюда =)
Ну да ладно…вообщем плохо разобрался со статической переменной static, точней понимаю, что она создаётся один раз, содержит последнее, внесённое в неё значение… а дальше, работа с классами и оператором расширения области действия :: очень запутался…

На данный момент вот такая проблема.

Visual studio 2013 выдаёт такие ошибки.

Мои догадки:
-из-за того, что интерфейс и реализация в одном файле…
-из-за компилятора, вставляю стандартный код, который был приведён для примера, выдаёт такие же ошибки.

Ошибка 1 error LNK2001: неразрешенный внешний символ «»private: static int AB::a» (?a@AB@@0HA)» C:UsersgarjoDocumentsVisual Studio 2013ProjectsПроект68Проект68Исходный код.obj Проект68

Ошибка 2 error LNK2001: неразрешенный внешний символ «»private: static int AB::b» (?b@AB@@0HA)» C:UsersgarjoDocumentsVisual Studio 2013ProjectsПроект68Проект68Исходный код.obj Проект68

Ошибка 3 error LNK1120: неразрешенных внешних элементов: 2 C:UsersgarjoDocumentsVisual Studio 2013ProjectsПроект68DebugПроект68.exe Проект68

при компиляции этого кода

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
 
using namespace std;
 
class AB //создаём класс 
{
private:
    static int a;
    static int b;
 
public:
    AB()
    {
        a = 1;
        b = 1;
        cout << "Тут сработал конструктор" << endl;//и здесь же их отобразим на экран
        cout << "a = " << a << endl;
        cout << "b = " << b << endl << endl;
    }
 
    void setAB()
    {
        cout << "Введите целое число а: ";
        cin >> a;
        cout << "Введите целое число b: ";
        cin >> b;
    }
 
    void getAB()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl << endl;
    }
 
    /*~AB() // это деструктор. не будем заставлять его чистить память, пусть просто покажет где он сработал
    {
        cout << "Тут сработал деструктор" << endl;
        system("pause");
 
    }*/
};
 
int main()
{
    setlocale(LC_ALL, "rus");
 
    AB obj1; //сработал конструктор
 
    obj1.getAB(); //для явной проверки
    obj1.setAB();   //присвоим новые значения переменным
    obj1.getAB();   //и выведем их на экран
 
    AB obj2;
    obj2.getAB;
    obj2.setAB;
    obj2.getAB;
 
    system("pause");
    return 0;
 
}



0



51 / 50 / 5

Регистрация: 14.08.2012

Сообщений: 248

09.04.2014, 00:47

2

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

int AB::a;
int AB::b;

Т.к. по сути статические переменные не принадлежат классу.
Так что ваши догадки пока неверны)



1



DrOffset

17439 / 9269 / 2266

Регистрация: 30.01.2014

Сообщений: 16,231

09.04.2014, 00:59

3

Цитата
Сообщение от AceOfSpades
Посмотреть сообщение

Т.к. по сути статические переменные не принадлежат классу.

Они принадлежат классу, но не принадлежат объекту.

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

C++
1
2
3
4
5
6
7
8
9
10
11
class A
{
public:
    void f(); // объявление
    static int a; // объявление
    static void fs(); // объявление
};
 
int A::a = 0; //определение
void A::f() {} //определение
void A::fs() {} //определение



1



5496 / 4891 / 831

Регистрация: 04.06.2011

Сообщений: 13,587

09.04.2014, 01:11

4

garjo_099, под поля класса, память, при объявлении класса, не выделяется. Память под них выделяется при создании объектов класса, но так как статические переменные класса не принадлежат объектам, то память под них необходимо выделить отдельно (определить эти переменные вне класса).



0



anonymous_

8 / 8 / 2

Регистрация: 21.11.2013

Сообщений: 22

09.04.2014, 01:17

5

статические классы члены и методы класса относяться к классу в целом, а не к отдельному объекту

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

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

C++
1
cout << "Today is " << Date::getCurrentDate() << endl;

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

C++
1
2
Date d;
cout << "Today is " << d.getCurrentDate() << endl;



0



51 / 50 / 5

Регистрация: 14.08.2012

Сообщений: 248

09.04.2014, 01:59

6

Цитата
Сообщение от DrOffset
Посмотреть сообщение

Они принадлежат классу, но не принадлежат объекту.

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

Статические данные класса по сути — глобальные переменные с видимостью в namespace класса.
Статические члены данных не является частью объектов заданного типа класса; они отдельные объекты. В результате объявление статического члена данных не является определением. Элемент данных объявлен в области класса, но определение выполняется в области файла.
Как-то так.



0



17439 / 9269 / 2266

Регистрация: 30.01.2014

Сообщений: 16,231

09.04.2014, 08:24

7

Цитата
Сообщение от AceOfSpades
Посмотреть сообщение

Не совсем так.

Storage duration и принадлежность чему-либо в С++ понятия независимые. Если нечто объявлено внутри функции — оно принадлежит функции. Если нечто объявлено внутри namespace — оно принадлежит namespace. Если нечто объявлено внутри класса — оно принадлежит классу, в частности доступ к static полям все так же разрешается спецификаторами доступа. Так что мое определение вполне верное.

Цитата
Сообщение от AceOfSpades
Посмотреть сообщение

Статические члены данных не является частью объектов заданного типа класса

Именно об этом я и написал и про определение и объявление тоже.



0



garjo_099

1 / 1 / 1

Регистрация: 08.04.2014

Сообщений: 31

09.04.2014, 16:36

 [ТС]

8

переделал код, и он показывает, что переменная в каждом экземпляре класса (при создании большого количества объектов класса) хранит последние внесённые в неё данные.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
 
using namespace std;
 
class AB //создаём класс 
{
private:
    static int a;
    static int b;
 
public:
    AB()
    {
        cout << "Тут сработал конструктор" << endl;//и здесь же их отобразим на экран
        cout << "a = " << a << endl;
        cout << "b = " << b << endl << endl;
    }
 
    void setAB()
    {
        cout << "Введите целое число а: ";
        cin >> a;
        cout << "Введите целое число b: ";
        cin >> b;
    }
 
    void getAB()
    {
        cout << "a = " << a << endl;
        cout << "b = " << b << endl << endl;
    }
 
    /*~AB() // это деструктор. не будем заставлять его чистить память, пусть просто покажет где он сработал
    {
        cout << "Тут сработал деструктор" << endl;
        system("pause");
 
    }*/
};
 
int AB::a;
int AB::b;
 
int main()
{
    setlocale(LC_ALL, "rus");
 
    AB obj1; //сработал конструктор
 
    obj1.getAB(); //для явной проверки
    obj1.setAB();   //присвоим новые значения переменным
    obj1.getAB();   //и выведем их на экран
 
    AB obj2;
    obj2.getAB();
    obj2.setAB();
    obj2.getAB();
 
    system("pause");
    return 0;
 
}

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

Добавлено через 4 минуты
И ещё вопрос. Из-за чего лучше объявлять определение за интерфейсом класса???



0



51 / 50 / 5

Регистрация: 14.08.2012

Сообщений: 248

09.04.2014, 16:44

9

Цитата
Сообщение от garjo_099
Посмотреть сообщение

И ещё вопрос. Из-за чего лучше объявлять определение за интерфейсом класса???

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



0



1 / 1 / 1

Регистрация: 08.04.2014

Сообщений: 31

09.04.2014, 20:09

 [ТС]

10

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

Добавлено через 9 минут
Через разбор строки наверное будет самый простой способ…

Добавлено через 42 минуты
а точней преобразовать строку дд.мм.гггг в числа и разбить в структуру или по отдельным переменным без точки.



0



17439 / 9269 / 2266

Регистрация: 30.01.2014

Сообщений: 16,231

09.04.2014, 20:11

11

garjo_099, scanf такое может



0



1 / 1 / 1

Регистрация: 08.04.2014

Сообщений: 31

09.04.2014, 20:33

 [ТС]

12

от примера не отказался бы =)))



0



DrOffset

17439 / 9269 / 2266

Регистрация: 30.01.2014

Сообщений: 16,231

09.04.2014, 21:12

13

Цитата
Сообщение от garjo_099
Посмотреть сообщение

от примера не отказался бы =)))

C++
1
2
3
4
5
6
7
8
9
10
11
#include <cstdio>
 
int main()
{
    int d = 0, m = 0, y = 0;
    if(scanf("%2d.%2d.%4d", &d, &m, &y) == 3)
    {
        printf("%d.%d.%dn", d, m, y);
    }
    return 0;
}



0



garjo_099

1 / 1 / 1

Регистрация: 08.04.2014

Сообщений: 31

10.04.2014, 01:34

 [ТС]

14

Написал код, только сильно не пинайте и громко не смейтесь=)) программка чисто для практики, писал код собственными алгоритмами, построенные в моей голове на основе того, что знаю на данный момент, никуда не подглядывая. Вообщем цель перевести строку даты в целочисленные тип данных. Помогите найти ошибки, с функцией, что-то не так и с оператором gets в main функции, не запрашивает повторный ввод, код компилится но выдаёт ошибку.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <iostream>
 
using namespace std;
 
bool preobrazStroki(char *); //прототип функции которая преобразует строку в символы
 
struct day
{//вообщем глобальные переменные для хранения отработаных результатов
    static char a, b;
};
struct mm
{
    static char a, b;
};
struct year
{
    static char a, b, c, d;
};
 
char day::a, day::b;
char mm::a, mm::b;
char year::a, year::b, year::c, year::d;
 
int main()
{
    setlocale(LC_ALL, "rus");
 
    char currentdate[11];//строка куда будем записывать дату
    bool stop = true;//переменная для условия выхода из цикла while
    //char *ptrcurrentdate = &currentdate[11];
    while (stop == true)
    {
        cout << "Введите дату в формате дд.мм.гггг" << endl;
 
        gets_s(currentdate);//считываем символы с клавиатуры в строку  п.с.и почему-то при повторном запуске цикла не запрашивает ввод данных
 
        char *ptrcurrentdate = &currentdate[11]; //так и не определился где лучше создать указатель вне цикла или внутри цикла...
        cout << ptrcurrentdate << endl;//для проверки, но выпуливает какой-то шлак(((
 
        if (currentdate[2] == '.' && currentdate[5] == '.')// первые условия правильности ввода даты
        {
            if (preobrazStroki(ptrcurrentdate))//если функция вернёт тру
            {
                int day1 = day::a - '0', day2 = day::b - '0', mm1 = mm::a - '0', mm2 = mm::b - '0';//конечные переменные с которымы
                //в будущем можно будет работать и вычислять количество дней с вашего дня рождения =))))
                int yy1 = year::a - '0', yy2 = year::b - '0', yy3 = year::c - '0', yy4 = year::d - '0';
                cout << day1 << day2 << "." << mm1 << mm2 << ".";//печать даты уже в формате int
                cout << yy1 << yy2 << yy3 << yy4 << endl;
            }
        }
        else
        {
            cout << "Неверный формат даты, вводите в таком формате дд.мм.гггг!n";
            cout << "Если желаете продолжить нажмите 1, если хотите выйти, нажмите 0n";//тут всё понятно я думаю
            cin >> stop;
 
        }
 
    }
 
 
    system("pause");
    return 0;
}
 
bool preobrazStroki(char *value)
{
    //day day1;
    //mm mm1;
    //year year1;
    
    while (*value != '')//пока указатель не будет указывать на asciiz
    {//собственно сама функция и с ней что-то не то.
        if (isdigit(*value))//если все if сработают функция вернёт true и дата запишется в глобальные переменные 
        {
            day::a = *value;
            value++;
        }
        else
        {
            cout << "neverniu format" << endl;
            break;//было задумано, если программа находит какой-то символ, а не цифру, то функция возвращает false
        }
 
            if (isdigit(*value))
            {
 
                day::b = *value;
                value += 2;
            }
            else
            {
                cout << "neverniu format" << endl;
                break;
            }
            if (isdigit(*value))
            {
 
                mm::a = *value;
                value++;
            }
            else
            {
                cout << "neverniu format" << endl;
                break;
            }
            if (isdigit(*value))
            {
                mm::b = *value;
                value += 2;
            }
            else
            {
                cout << "neverniu format" << endl;
                break;
            }
            if (isdigit(*value))
            {
 
                year::a = *value;
                value++;
            }
            else
            {
                cout << "neverniu format" << endl;
                break;
            }
            if (isdigit(*value))
            {
 
                year::b = *value;
                value++;
            }
            else
            {
                cout << "neverniu format" << endl;
                break;
            }
 
            if (isdigit(*value))
            {
            year::c = *value;
                value++;
            }
            else
            {
                cout << "neverniu format" << endl;
                break;
            }
    
            if (isdigit(*value))
            {
                year::d = *value;
                value++;
            }
            else
            {
            cout << "neverniu format" << endl;
            break;
            }
            return true;
    }
    return false;
}



0



alsav22

5496 / 4891 / 831

Регистрация: 04.06.2011

Сообщений: 13,587

10.04.2014, 02:14

15

Цитата
Сообщение от garjo_099
Посмотреть сообщение

не запрашивает повторный ввод

C++
1
(cin >> stop).get();



0



1 / 1 / 1

Регистрация: 08.04.2014

Сообщений: 31

10.04.2014, 10:54

 [ТС]

16

Цитата
Сообщение от alsav22
Посмотреть сообщение

Код C++
1
(cin >> stop).get();

Повторный ввод строки gets_s!



0



5496 / 4891 / 831

Регистрация: 04.06.2011

Сообщений: 13,587

10.04.2014, 11:00

17

Цитата
Сообщение от garjo_099
Посмотреть сообщение

Повторный ввод строки gets_s!

И что?



0



1 / 1 / 1

Регистрация: 08.04.2014

Сообщений: 31

10.04.2014, 11:05

 [ТС]

18

alsav22, ты вводишь дату, ввёл неправильно, цикл спрашивает ещё будете вводить или нет, если да то уже не срабатывает ввод даты, а опять же к этому месту выбора в цикле, скомпилируй код и просто посмотри



0



5496 / 4891 / 831

Регистрация: 04.06.2011

Сообщений: 13,587

10.04.2014, 11:42

19

Цитата
Сообщение от garjo_099
Посмотреть сообщение

скомпилируй код и просто посмотри

Оно мне надо? Вопрос был такой:

Цитата
Сообщение от garjo_099
Посмотреть сообщение

не запрашивает повторный ввод

Сейчас запрашивает?

Цитата
Сообщение от garjo_099
Посмотреть сообщение

Повторный ввод строки gets_s!



0



1 / 1 / 1

Регистрация: 08.04.2014

Сообщений: 31

10.04.2014, 11:51

 [ТС]

20

Да. Можно объяснить почему без добавления функции get() не работало. А с функцией, что? не подскажите?



0



description title ms.date f1_keywords helpviewer_keywords ms.assetid

Learn more about: Linker Tools Error LNK2001

Linker Tools Error LNK2001

10/22/2021

LNK2001

LNK2001

dc1cf267-c984-486c-abd2-fd07c799f7ef

Linker Tools Error LNK2001

unresolved external symbol «symbol«

The compiled code makes a reference or call to symbol. The symbol isn’t defined in any libraries or object files searched by the linker.

This error message is followed by fatal error LNK1120. To fix error LNK1120, first fix all LNK2001 and LNK2019 errors.

There are many ways to get LNK2001 errors. All of them involve a reference to a function or variable that the linker can’t resolve, or find a definition for. The compiler can identify when your code doesn’t declare a symbol, but not when it doesn’t define one. That’s because the definition may be in a different source file or library. If your code refers to a symbol, but it’s never defined, the linker generates an error.

What is an unresolved external symbol?

A symbol is the internal name for a function or global variable. It’s the form of the name used or defined in a compiled object file or library. A global variable is defined in the object file where storage is allocated for it. A function is defined in the object file where the compiled code for the function body is placed. An external symbol is one referenced in one object file, but defined in a different library or object file. An exported symbol is one that’s made publicly available by the object file or library that defines it.

To create an application or DLL, every symbol used must have a definition. The linker must resolve, or find the matching definition for, every external symbol referenced by each object file. The linker generates an error when it can’t resolve an external symbol. It means the linker couldn’t find a matching exported symbol definition in any of the linked files.

Compilation and link issues

This error can occur:

  • When the project is missing a reference to a library (.LIB) or object (.OBJ) file. To fix this issue, add a reference to the required library or object file to your project. For more information, see lib Files as linker input.

  • When the project has a reference to a library (.LIB) or object (.OBJ) file that in turn requires symbols from another library. It may happen even if you don’t call functions that cause the dependency. To fix this issue, add a reference to the other library to your project. For more information, see Understanding the classical model for linking: Taking symbols along for the ride.

  • If you use the /NODEFAULTLIB or /Zl options. When you specify these options, libraries that contain required code aren’t linked into the project unless you’ve explicitly included them. To fix this issue, explicitly include all the libraries you use on the link command line. If you see many missing CRT or Standard Library function names when you use these options, explicitly include the CRT and Standard Library DLLs or library files in the link.

  • If you compile using the /clr option. There may be a missing reference to .cctor. For more information on how to fix this issue, see Initialization of mixed assemblies.

  • If you link to the release mode libraries when building a debug version of an application. Similarly, if you use options /MTd or /MDd or define _DEBUG and then link to the release libraries, you should expect many potential unresolved externals, among other problems. Linking a release mode build with the debug libraries also causes similar problems. To fix this issue, make sure you use the debug libraries in your debug builds, and retail libraries in your retail builds.

  • If your code refers to a symbol from one library version, but you link a different version of the library. Generally, you can’t mix object files or libraries that are built for different versions of the compiler. The libraries that ship in one version may contain symbols that can’t be found in the libraries included with other versions. To fix this issue, build all the object files and libraries with the same version of the compiler before linking them together. For more information, see C++ binary compatibility between Visual Studio versions.

  • If library paths are out of date. The Tools > Options > Projects > VC++ Directories dialog, under the Library files selection, allows you to change the library search order. The Linker folder in the project’s Property Pages dialog box may also contain paths that could be out of date.

  • When a new Windows SDK is installed (perhaps to a different location). The library search order must be updated to point to the new location. Normally, you should put the path to new SDK include and lib directories in front of the default Visual C++ location. Also, a project containing embedded paths may still point to old paths that are valid, but out of date. Update the paths for new functionality added by the new version that’s installed to a different location.

  • If you build at the command line, and have created your own environment variables. Verify that the paths to tools, libraries, and header files go to a consistent version. For more information, see Use the MSVC toolset from the command line.

Coding issues

This error can be caused by:

  • Mismatched case in your source code or module-definition (.def) file. For example, if you name a variable var1 in one C++ source file and try to access it as VAR1 in another, this error is generated. To fix this issue, use consistently spelled and cased names.

  • A project that uses function inlining. It can occur when you define the functions as inline in a source file, rather than in a header file. Inlined functions can’t be seen outside the source file that defines them. To fix this issue, define the inlined functions in the headers where they’re declared.

  • Calling a C function from a C++ program without using an extern "C" declaration for the C function. The compiler uses different internal symbol naming conventions for C and C++ code. The internal symbol name is what the linker looks for when resolving symbols. To fix this issue, use an extern "C" wrapper around all declarations of C functions used in your C++ code, which causes the compiler to use the C internal naming convention for those symbols. Compiler options /Tp and /Tc cause the compiler to compile files as C++ or C, respectively, no matter what the filename extension is. These options can cause internal function names different from what you expect.

  • An attempt to reference functions or data that don’t have external linkage. In C++, inline functions and const data have internal linkage unless explicitly specified as extern. To fix this issue, use explicit extern declarations on symbols referred to outside the defining source file.

  • A missing function body or variable definition. This error is common when you declare, but don’t define, variables, functions, or classes in your code. The compiler only needs a function prototype or extern variable declaration to generate an object file without error, but the linker can’t resolve a call to the function or a reference to the variable because there’s no function code or variable space reserved. To fix this issue, make sure to define every referenced function and variable in a source file or library you link.

  • A function call that uses return and parameter types or calling conventions that don’t match the ones in the function definition. In C++ object files, Name decoration encodes the calling convention, class or namespace scope, and return and parameter types of a function. The encoded string becomes part of the final decorated function name. This name is used by the linker to resolve, or match, calls to the function from other object files. To fix this issue, make sure the function declaration, definition, and calls all use the same scopes, types, and calling conventions.

  • C++ code you call, when you include a function prototype in a class definition, but don’t include the implementation of the function. To fix this issue, be sure to provide a definition for all class members you call.

  • An attempt to call a pure virtual function from an abstract base class. A pure virtual function has no base class implementation. To fix this issue, make sure all called virtual functions are implemented.

  • Trying to use a variable declared within a function (a local variable) outside the scope of that function. To fix this issue, remove the reference to the variable that isn’t in scope, or move the variable to a higher scope.

  • When you build a Release version of an ATL project, producing a message that CRT startup code is required. To fix this issue, do one of the following,

    • Remove _ATL_MIN_CRT from the list of preprocessor defines to allow CRT startup code to be included. For more information, see General property page (Project).

    • If possible, remove calls to CRT functions that require CRT startup code. Instead, use their Win32 equivalents. For example, use lstrcmp instead of strcmp. Known functions that require CRT startup code are some of the string and floating point functions.

Consistency issues

There’s currently no standard for C++ name decoration between compiler vendors, or even between different versions of the same compiler. Object files compiled with different compilers may not use the same naming scheme. Linking them can cause error LNK2001.

Mixing inline and non-inline compile options on different modules can cause LNK2001. If a C++ library is created with function inlining turned on (/Ob1 or /Ob2) but the corresponding header file describing the functions has inlining turned off (no inline keyword), this error occurs. To fix this issue, define the functions inline in the header file you include in other source files.

If you use the #pragma inline_depth compiler directive, make sure you’ve set a value of 2 or greater, and make sure you also use the /Ob1 or /Ob2 compiler option.

This error can occur if you omit the LINK option /NOENTRY when you create a resource-only DLL. To fix this issue, add the /NOENTRY option to the link command.

This error can occur if you use incorrect /SUBSYSTEM or /ENTRY settings in your project. For example, if you write a console application and specify /SUBSYSTEM:WINDOWS, an unresolved external error is generated for WinMain. To fix this issue, make sure you match the options to the project type. For more information on these options and entry points, see the /SUBSYSTEM and /ENTRY linker options.

Exported .def file symbol issues

This error occurs when an export listed in a .def file isn’t found. It could be because the export doesn’t exist, is spelled incorrectly, or uses C++ decorated names. A .def file doesn’t take decorated names. To fix this issue, remove unneeded exports, and use extern "C" declarations for exported symbols.

Use the decorated name to find the error

The C++ compiler and linker use Name Decoration, also known as name-mangling. Name decoration encodes extra information about the type of a variable in its symbol name. The symbol name for a function encodes its return type, parameter types, scope, and calling convention. This decorated name is the symbol name the linker searches for to resolve external symbols.

A link error can result if the declaration of a function or variable doesn’t exactly match the definition of the function or variable. That’s because any difference becomes part of the symbol name to match. The error can happen even if the same header file is used in both the calling code and the defining code. One way it may occur is if you compile the source files by using different compiler flags. For example, if your code is compiled to use the __vectorcall calling convention, but you link to a library that expects clients to call it using the default __cdecl or __fastcall calling convention. In this case, the symbols don’t match because the calling conventions are different.

To help you find the cause, the error message shows you two versions of the name. It displays both the «friendly name,» the name used in source code, and the decorated name (in parentheses). You don’t need to know how to interpret the decorated name. You can still search for and compare it with other decorated names. Command-line tools can help to find and compare the expected symbol name and the actual symbol name:

  • The /EXPORTS and /SYMBOLS options of the DUMPBIN command-line tool are useful here. They can help you discover which symbols are defined in your .dll and object or library files. You can use the symbols list to verify that the exported decorated names match the decorated names the linker searches for.

  • In some cases, the linker can only report the decorated name for a symbol. You can use the UNDNAME command-line tool to get the undecorated form of a decorated name.

Additional resources

For more information, see the Stack Overflow question «What is an undefined reference/unresolved external symbol error and how do I fix it?».

Your linkage consumes libraries before the object files that refer to them

  • You are trying to compile and link your program with the GCC toolchain.
  • Your linkage specifies all of the necessary libraries and library search paths
  • If libfoo depends on libbar, then your linkage correctly puts libfoo before libbar.
  • Your linkage fails with undefined reference to something errors.
  • But all the undefined somethings are declared in the header files you have
    #included and are in fact defined in the libraries that you are linking.

Examples are in C. They could equally well be C++

A minimal example involving a static library you built yourself

my_lib.c

#include "my_lib.h"
#include <stdio.h>

void hw(void)
{
    puts("Hello World");
}

my_lib.h

#ifndef MY_LIB_H
#define MT_LIB_H

extern void hw(void);

#endif

eg1.c

#include <my_lib.h>

int main()
{
    hw();
    return 0;
}

You build your static library:

$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o

You compile your program:

$ gcc -I. -c -o eg1.o eg1.c

You try to link it with libmy_lib.a and fail:

$ gcc -o eg1 -L. -lmy_lib eg1.o 
eg1.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status

The same result if you compile and link in one step, like:

$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
/tmp/ccQk1tvs.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status

A minimal example involving a shared system library, the compression library libz

eg2.c

#include <zlib.h>
#include <stdio.h>

int main()
{
    printf("%sn",zlibVersion());
    return 0;
}

Compile your program:

$ gcc -c -o eg2.o eg2.c

Try to link your program with libz and fail:

$ gcc -o eg2 -lz eg2.o 
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status

Same if you compile and link in one go:

$ gcc -o eg2 -I. -lz eg2.c
/tmp/ccxCiGn7.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status

And a variation on example 2 involving pkg-config:

$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o 
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'

What are you doing wrong?

In the sequence of object files and libraries you want to link to make your
program, you are placing the libraries before the object files that refer to
them. You need to place the libraries after the object files that refer
to them.

Link example 1 correctly:

$ gcc -o eg1 eg1.o -L. -lmy_lib

Success:

$ ./eg1 
Hello World

Link example 2 correctly:

$ gcc -o eg2 eg2.o -lz

Success:

$ ./eg2 
1.2.8

Link the example 2 pkg-config variation correctly:

$ gcc -o eg2 eg2.o $(pkg-config --libs zlib) 
$ ./eg2
1.2.8

The explanation

Reading is optional from here on.

By default, a linkage command generated by GCC, on your distro,
consumes the files in the linkage from left to right in
commandline sequence. When it finds that a file refers to something
and does not contain a definition for it, to will search for a definition
in files further to the right. If it eventually finds a definition, the
reference is resolved. If any references remain unresolved at the end,
the linkage fails: the linker does not search backwards.

First, example 1, with static library my_lib.a

A static library is an indexed archive of object files. When the linker
finds -lmy_lib in the linkage sequence and figures out that this refers
to the static library ./libmy_lib.a, it wants to know whether your program
needs any of the object files in libmy_lib.a.

There is only object file in libmy_lib.a, namely my_lib.o, and there’s only one thing defined
in my_lib.o, namely the function hw.

The linker will decide that your program needs my_lib.o if and only if it already knows that
your program refers to hw, in one or more of the object files it has already
added to the program, and that none of the object files it has already added
contains a definition for hw.

If that is true, then the linker will extract a copy of my_lib.o from the library and
add it to your program. Then, your program contains a definition for hw, so
its references to hw are resolved.

When you try to link the program like:

$ gcc -o eg1 -L. -lmy_lib eg1.o

the linker has not added eg1.o to the program when it sees
-lmy_lib. Because at that point, it has not seen eg1.o.
Your program does not yet make any references to hw: it
does not yet make any references at all, because all the references it makes
are in eg1.o.

So the linker does not add my_lib.o to the program and has no further
use for libmy_lib.a.

Next, it finds eg1.o, and adds it to be program. An object file in the
linkage sequence is always added to the program. Now, the program makes
a reference to hw, and does not contain a definition of hw; but
there is nothing left in the linkage sequence that could provide the missing
definition. The reference to hw ends up unresolved, and the linkage fails.

Second, example 2, with shared library libz

A shared library isn’t an archive of object files or anything like it. It’s
much more like a program that doesn’t have a main function and
instead exposes multiple other symbols that it defines, so that other
programs can use them at runtime.

Many Linux distros today configure their GCC toolchain so that its language drivers (gcc,g++,gfortran etc)
instruct the system linker (ld) to link shared libraries on an as-needed basis.
You have got one of those distros.

This means that when the linker finds -lz in the linkage sequence, and figures out that this refers
to the shared library (say) /usr/lib/x86_64-linux-gnu/libz.so, it wants to know whether any references that it has added to your program that aren’t yet defined have definitions that are exported by libz

If that is true, then the linker will not copy any chunks out of libz and
add them to your program; instead, it will just doctor the code of your program
so that:-

  • At runtime, the system program loader will load a copy of libz into the
    same process as your program whenever it loads a copy of your program, to run it.

  • At runtime, whenever your program refers to something that is defined in
    libz, that reference uses the definition exported by the copy of libz in
    the same process.

Your program wants to refer to just one thing that has a definition exported by libz,
namely the function zlibVersion, which is referred to just once, in eg2.c.
If the linker adds that reference to your program, and then finds the definition
exported by libz, the reference is resolved

But when you try to link the program like:

gcc -o eg2 -lz eg2.o

the order of events is wrong in just the same way as with example 1.
At the point when the linker finds -lz, there are no references to anything
in the program: they are all in eg2.o, which has not yet been seen. So the
linker decides it has no use for libz. When it reaches eg2.o, adds it to the program,
and then has undefined reference to zlibVersion, the linkage sequence is finished;
that reference is unresolved, and the linkage fails.

Lastly, the pkg-config variation of example 2 has a now obvious explanation.
After shell-expansion:

gcc -o eg2 $(pkg-config --libs zlib) eg2.o

becomes:

gcc -o eg2 -lz eg2.o

which is just example 2 again.

I can reproduce the problem in example 1, but not in example 2

The linkage:

gcc -o eg2 -lz eg2.o

works just fine for you!

(Or: That linkage worked fine for you on, say, Fedora 23, but fails on Ubuntu 16.04)

That’s because the distro on which the linkage works is one of the ones that
does not configure its GCC toolchain to link shared libraries as-needed.

Back in the day, it was normal for unix-like systems to link static and shared
libraries by different rules. Static libraries in a linkage sequence were linked
on the as-needed basis explained in example 1, but shared libraries were linked unconditionally.

This behaviour is economical at linktime because the linker doesn’t have to ponder
whether a shared library is needed by the program: if it’s a shared library,
link it. And most libraries in most linkages are shared libraries. But there are disadvantages too:-

  • It is uneconomical at runtime, because it can cause shared libraries to be
    loaded along with a program even if doesn’t need them.

  • The different linkage rules for static and shared libraries can be confusing
    to inexpert programmers, who may not know whether -lfoo in their linkage
    is going to resolve to /some/where/libfoo.a or to /some/where/libfoo.so,
    and might not understand the difference between shared and static libraries
    anyway.

This trade-off has led to the schismatic situation today. Some distros have
changed their GCC linkage rules for shared libraries so that the as-needed
principle applies for all libraries. Some distros have stuck with the old
way.

Why do I still get this problem even if I compile-and-link at the same time?

If I just do:

$ gcc -o eg1 -I. -L. -lmy_lib eg1.c

surely gcc has to compile eg1.c first, and then link the resulting
object file with libmy_lib.a. So how can it not know that object file
is needed when it’s doing the linking?

Because compiling and linking with a single command does not change the
order of the linkage sequence.

When you run the command above, gcc figures out that you want compilation +
linkage. So behind the scenes, it generates a compilation command, and runs
it, then generates a linkage command, and runs it, as if you had run the
two commands:

$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o

So the linkage fails just as it does if you do run those two commands. The
only difference you notice in the failure is that gcc has generated a
temporary object file in the compile + link case, because you’re not telling it
to use eg1.o. We see:

/tmp/ccQk1tvs.o: In function `main'

instead of:

eg1.o: In function `main':

See also

The order in which interdependent linked libraries are specified is wrong

Putting interdependent libraries in the wrong order is just one way
in which you can get files that need definitions of things coming
later in the linkage than the files that provide the definitions. Putting libraries before the
object files that refer to them is another way of making the same mistake.

Определение

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

К таким сущностям может относиться, например, функция или переменная.


Причины и решения

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


Используются сторонние библиотеки

  • Не указана необходимая (статическая) библиотека для компоновщика.

    Например, к проекту подключен только *.h файл с объявлениями, но отсутствует код реализации, обычно это *.lib или *.a файлы (в зависимости от используемой системы).
    Требуется явно подключить библиотеку к проекту.

  • Для Visual C++ это можно сделать добавлением следующей строки прямо в код:

    #pragma comment(lib, "libname.lib")
    
  • Для gcc/clang требуется указать файл через ключ -l (эль)

  • Для Qt в .pro файле нужно использовать переменную LIBS:

    LIBS += -L[путь_к_библиотеке] -l[имя_библиотеки]
    
  • Для системы сборки cmake есть target_link_libraries.

  • Библиотека указана, но необходимая сущность, например, класс или функция фактически не экспортируется из библиотеки. Под Windows это может возникать из-за отсуствия __declspec(dllexport) перед сущностью. Обычно это решается макросами. Данная ситуация чаще всего характерна именно для библиотек, находящихся в процессе разработки, и доступных для модификации самому разработчику, нежели для каких-то стабильных версий действительно внешних для проекта библиотек. После разрешения экспортирования библиотеку, конечно же, нужно пересобрать перед непосредственным использованием проблемной сущности.

  • Библиотека указана, но не совпадает разрядность библиотеки и компилируемого кода.

    В общем случае, разрядность собираемого проекта (приложения или библиотеки) должна совпадать с разрядностью используемой сторонней библиотеки. Обычно производители библиотек предоставляют возможность выбора 32 или 64 бит версию использовать. Если библиотека поставляется в исходных кодах и собирается отдельно от текущего проекта, нужно также выбрать правильную разрядность.

  • Библиотека указана, но она собрана для другой (не совместимой) ОС.

    Например при сборке проекта в Windows осуществляется попытка использовать бинарный файл, собранный для Linux. В данном случае нужно использовать файлы, подходящие для вашей ОС.

  • Библиотека указана, но она собрана другим компилятором, не совместимым с используемым.

    Объектные файлы, полученные путем сборки C++ кода разными компиляторами для одной и той же ОС могут быть бинарно несовместимы друг с другом. Требуется использовать совместимые (скорее всего и вовсе одинаковые) компиляторы.

  • Библиотека указана, и собрана тем же компилятором, что и основной проект, но используются разные версии Run-Time библиотек.

    Например, для Visual C++ возможна ситуация, когда библиотека собрана с ключом /MDd, а основной проект с /MTd. Требуется задать ключи так, чтобы использовались одинаковые версии Run-Time библиотек.


Сторонние библиотеки не используются

  • Просто отсутствует определение функции.

    void f(int); // всего лишь объявление. Нет `тела` функции
    int main(){  
        f(42);   // undefined reference to `f(int)'
    }  
    

    Требуется добавить определение функции f:

    void f(int) {
        // тело функции
    }
    

    Может быть ещё частный случай с ошибкой вида:

    undefined reference to `vtable for <имя_класса>`
    

    Такая ошибка возникает, если объявленная виртуальная функция класса, не являющаяся чистой (=0), не содержит реализации.

    class C {
        virtual void f(int);
    };
    

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

    void C::f(int) { // виртуальная функция-член вне определения класса
        // тело функции  
    } 
    
    void f(int) { // свободная функция, не устраняющая проблему
        // тело функции 
    } 
    

    Аналогичная ситуация может возникать при использовании пространств имён, когда объявлении функции находится в пространстве имён:

    // В заголовочном файле
    namespace N {
        void f(int);
    };    
    

    а при реализации указать это пространство имён забыли:

    // В файле реализации
    void f(int) { // функция в глобальном пространстве имён, не устраняющая проблему
        // тело функции  
    }
    
    namespace N {
    void f(int) { // функция в нужном пространстве имён
        // тело функции  
    }
    } // конец пространства имён
    

    Стоит заметить, что C++ разрешает перегрузку функций (существование одноимённых функций, но с разным набором параметров), и в этом случае важно, чтобы сигнатуры функций при объявлении и определении совпадали. Например, вместо объявленной void f(int) была реализована другая:

    void f(const char*) { // const char* вместо int
        // тело функции     
    }
    

    При вызове f(42) будет ошибка:

    undefined reference to `f(int)'
    

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

    template <class T>
    struct C {
        friend void f(C<T>);   // объявляет *НЕ*шаблонную дружественную функцию
    };
    
    template <class T>         // определяет шаблонную функцию 
    void f(C<T>) { }
    
    int main() {
        C<int> c;
        f(c);                  // undefined reference to `f(C<int>)'
    }
    

    Чтобы объявить шаблонную дружественную функцию, требуется добавить указание шаблонности:

    template <class T>
    struct C {
        template <class V>     // добавили шаблонность для friend функции
        friend void f(C<T>);
    };
    

    Важно, что имя шаблонного параметра для дружественной функции должно отличаться от имени параметра шаблонного класса T, т.к. иначе будет ошибка о сокрытии имени. В частном случае имя класса можно вовсе не указывать, и оставить template <class>. Но это не всегда будет правильным решением, т.к. фактически могла потребоваться дружественная специализация шаблона функции, а не сама шаблонная функция. Подробнее об использовании дружественных функций в шаблонном классе можно ознакомиться здесь.

  • Отсутствует определение статической переменной класса.

    struct S {
        static int i;
    };
    
    int main() {
        S s;
        s.i = 42;  // undefined reference to `S::i'
    }
    

    Нужно добавить определение (выделить память) переменной:

    int S::i;
    
  • Неправильная реализация шаблонного кода.

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

    // unit.h
    #pragma once
    
    template <class T>
    T f(T);                    // объявление шаблона без реализации
    
    // unit.cpp
    #include "unit.h"
    
    template <class T>
    T f(T t) { return t + t; } // реализация шаблона
    
    template
    int f<int>(int);           // явное инстанцирование для int
    
    template
    double f<double>(double);  // явное инстанцирование для double
    
    // main.cpp
    #include "unit.h"
    
    int main() { 
        f(2);   // ok int
        f(1.5); // ok double
        f('a'); // undefined reference to `char f<char>(char)'
    }
    
  • Файл с кодом не был скомпилирован.

    Например, в случае использования make-файла не было прописано правило построения файла, а в случае использования IDE типа Visual Studio *.cpp файл не добавлен в список файлов проекта.

  • Виртуальная функция в базовом классе не объявлена как = 0 (pure-virtual).

    struct B {
        void virtual f();
    };
    
    struct D : B {
        void f() {}
    };
    
    int main() {
        D d;
    }
    

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

    struct B {
        void virtual f() = 0;
    };
    
  • Имя не имеет внешнего связывания.

    Например, есть объявление функции f в модуле А и даже ее реализация в модуле B, но реализация помечена как static:

    // A.cpp
    void f();
    int main() {
        f();   // undefined reference to `f()'
    }
    
    // B.cpp
    static void f() {}
    

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

    // B.cpp
    namespace {
       void f() {}
    }
    

    Или даже при наличии inline у функции:

    // B.cpp
    inline void f() {}
    

Понравилась статья? Поделить с друзьями:
  • Ошибка lnk2001 неразрешенный внешний символ main
  • Ошибка lnk2001 unresolved external symbol
  • Ошибка lnk1169 обнаружен многократно определенный символ один или более
  • Ошибка lnk1123 ошибка при преобразовании в coff
  • Ошибка lnk1123 в c visual studio 2010