LINUX.ORG.RU

Как найти ошибку в C/C++ коде? Видимо где-то забыл ; или " или }


0

0

Здравствуйте!


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

Проблема в том, что компилятор gcc, в случае забывчивости программиста поставить ; или " или }, обычно не показывает то место, где эта ошибка произошла, и догадываться приходится по коственным признакам.

Вот сейчас у меня такая же ситуация. Я столько кода наменял, что вспомнить, где надо перепроверить, нет возможности. А скомпилить не могу, из-за неизвестного места, в котором есть проблема.

Что делать? Слышал, вроде есть какие-то дополнительные анализаторы кода, но те что я нашел - это для аудита безопасности кода.


Вот вывод ошибок

src/treeitem.h:75: ошибка: ISO C++ запрещает декларации 'recordtabledata' без типа
src/treeitem.h:75: ошибка: expected ';' before '*' token
src/treeitem.h:89: ошибка: 'recordtabledata' does not name a type
src/recordtablemodel.h:38: ошибка: 'recordtabledata' не был декларирован
src/recordtablemodel.h:43: ошибка: ISO C++ запрещает декларации 'recordtabledata' без типа
src/recordtablemodel.h:43: ошибка: expected ';' before '*' token
src/recordtablescreen.h:35: ошибка: 'recordtabledata' не был декларирован
src/recordtablescreen.h:125: ошибка: ISO C++ запрещает декларации 'recordtabledata' без типа
src/recordtablescreen.h:125: ошибка: expected ';' before '*' token

src/recordtablescreen.cpp:173: ошибка: prototype for 'void recordtablescreen::set_tabledata(recordtabledata*)' does not match any in class 'recordtablescreen'
src/recordtablescreen.h:35: ошибка: претендент: void recordtablescreen::set_tabledata(int*)

src/recordtablescreen.cpp:173: предупреждение: unused parameter 'rtdata'

src/recordtablescreen.cpp: In member function 'void recordtablescreen::select(const QModelIndex&)':
src/recordtablescreen.cpp:217: ошибка: нет декларации 'table' в этой области видимости
src/recordtablescreen.cpp: In member function 'void recordtablescreen::add_new(int, QString, QString, QString, QString, QString)':
src/recordtablescreen.cpp:312: ошибка: нет декларации 'table' в этой области видимости
src/recordtablescreen.cpp: In member function 'void recordtablescreen::edit_field_context()':
src/recordtablescreen.cpp:341: ошибка: нет декларации 'table' в этой области видимости
src/recordtablescreen.cpp: In member function 'void recordtablescreen::edit_field(int, QString, QString, QString, QString)':
src/recordtablescreen.cpp:373: ошибка: нет декларации 'table' в этой области видимости
src/recordtablescreen.cpp: In member function 'void recordtablescreen::delete_records()':
src/recordtablescreen.cpp:426: ошибка: нет декларации 'table' в этой области видимости
src/recordtablescreen.cpp: In member function 'void recordtablescreen::copy()':
src/recordtablescreen.cpp:459: ошибка: нет декларации 'table' в этой области видимости
src/recordtablescreen.cpp: In member function 'void recordtablescreen::moveup()':
src/recordtablescreen.cpp:642: ошибка: нет декларации 'table' в этой области видимости
src/recordtablescreen.cpp: In member function 'void recordtablescreen::movedn()':
src/recordtablescreen.cpp:662: ошибка: нет декларации 'table' в этой области видимости


По какой-то причине невозможна работа с классом recordtabledata. Обычно такие ошибки возникают, если забыть подключить заголовок recordtabledata.h через include, или в прототипе класса забыть за закрывающей скобкой поставить ; . Везде где нужно (и ненужно) заголовок подключен. В pro файле (использую Qt)в HEADERS и SOURCES добавлены recordtabledata.h и recordtabledata.cpp соответсвенно.


Обращаю внимание на странную ошибку

"... prototype for 'void recordtablescreen::set_tabledata(recordtabledata*)' does not match any in class 'recordtablescreen'
src/recordtablescreen.h:35: ошибка: претендент: void recordtablescreen::set_tabledata(int*) ..."

Претендента set_tabledata(int*) у меня нигде на самом деле в коде нет, а есть

Прототип в классе recordtablescreen (файл recordtablescreen.h)
void set_tabledata(recordtabledata *);

Реализация (файл recordtablescreen.cpp)
void recordtablescreen::set_tabledata(recordtabledata *rtdata)
{
...
}


Поток ошибок с table связано с тем, что член класса table имеет тип recordtabledata.


В общем, все указывает на проблему в классе recordtabledata. Я его излазил вдоль и поперек, ошибки не вижу. Но почему-то с ним происходят вот такие странные вещи. И почему-то в нем компилятор ошибок не обнаруживает.


Вопрос. Что делать, как найти ошибку?


внезапный int* намекает на то что тип recordtabledata в этом месте не определен

ищи пропавшие инклюды

c:curing кстати

anonymous
()
Ответ на: комментарий от anonymous

> внезапный int* намекает на то что тип recordtabledata в этом месте не определен
> ищи пропавшие инклюды


Сделал самую бестолковую вещь - прописал во все исходники проекта (в h и cpp, кроме самого recordtabledata.h) строку

#include "recordtabledata.h"

всеравно имеем те же самые ошибки.


> c:curing кстати


Что есть "c:curing" ?

xintrea
() автор топика
Ответ на: комментарий от tailgunner

> Посмотри на препроцессированый файл (опция -E в gcc).

При использовании Qt, смотреть на это дело несколько громоздко. И что там можно увидеть?

xintrea
() автор топика
Ответ на: комментарий от xintrea

>> Посмотри на препроцессированый файл (опция -E в gcc).

> При использовании Qt, смотреть на это дело несколько громоздко.

Зато ты крепче усвоишь, что промежуточные компиляции рулят.

> И что там можно увидеть?

Есть ли определение класса recordtabledata перед строкой src/treeitem.h:75

tailgunner ★★★★★
()

проверь, есть ли ; после } в объявлении класса.

типичная ошибка

xydo ★★
()
Ответ на: комментарий от tailgunner

>> При использовании Qt, смотреть на это дело несколько громоздко.
> Зато ты крепче усвоишь, что промежуточные компиляции рулят.


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

>> И что там можно увидеть?


> Есть ли определение класса recordtabledata перед строкой src/treeitem.h:75


g++ -E treeitem.cpp > treeitem.txt

Вначале определение класса recordtabledata, только потом потом treeitem.

xintrea
() автор топика

Скорее всего была пропущена точка с запятой после фигурной скобки при описании структуры.

dont
()
Ответ на: комментарий от dont

> Скорее всего была пропущена точка с запятой после фигурной скобки при описании структуры.

Это я проверяю в первую очередь :) Нет, нет таких ошибок.


Одну уже мне помогли найти

linetmp=table.at(pos));


Но компиляция не идет, кажет всё те же ошибки. Где-то еще какаято мелочевка торчит.

xintrea
() автор топика
Ответ на: комментарий от tailgunner

> Кольцевые include в treeitem.h и recordtabledata.h, какая прелесть. Это так задумано?

Это следствие того, что "Сделал самую бестолковую вещь - прописал во все исходники проекта (в h и cpp, кроме самого recordtabledata.h) строку #include "recordtabledata.h". Всеравно имеем те же самые ошибки."

А что имеется в виду под кольцевым include?

recordtabledata.h :

#include "main.h"
#include "treeitem.h"
#include "treemodel.h"
#include "appconfig.h"
#include "mainwindow.h"


treeitem.h :

#include "main.h"
#include "recordtabledata.h"
#include "recordtablemodel.h"


Учитывая, что каждый заголовок оформлен в конструкцию вида

#ifndef TREEITEM_H
#define TREEITEM_H
...
#endif

никаких проблем из-за этого "кольцевого инклюдинга" возникать не должно.

xintrea
() автор топика
Ответ на: комментарий от xintrea

> никаких проблем из-за этого "кольцевого инклюдинга" возникать не должно.

Мде. Слушай, опубликуй препроцессированый файл. До того, как ты сделал "самую бестолковую вещь".

tailgunner ★★★★★
()
Ответ на: комментарий от xintrea

> Что есть "c:curing" ?

доктор говорит, что captcha: curing

dilmah ★★★★★
()
Ответ на: комментарий от xintrea

>никаких проблем из-за этого "кольцевого инклюдинга" возникать не должно

блажен кто верует

>А что имеется в виду под кольцевым include?

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

jtootf ★★★★★
()
Ответ на: комментарий от xintrea

> никаких проблем из-за этого "кольцевого инклюдинга" возникать не должно.

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

Короче смотри сюда, если ещё сам не догадался. Предроложим есть файл main.cpp:

#include "recordtabledata.h"
...

1. Препроцессор икнклудит тело заголовочника и делает #define RECORDTABLEDATA_H.
2. Далее он инклудит treeitem.h и делает #define TREEITEM_H.
3. Потом идёт дальше по телу treeitem.h и обнаруживает повторный #include "recordtabledata.h", заходит в него и... пропускает! Вместе с обьявлением RecordTableData. Получаешь вместо инклуда пустое место. Потому что смотри пункт 1: уже был #define RECORDTABLEDATA_H.

Ищи ошибку в логике. Скажу наперёд:

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

2. Зависимости таки могут быть, но не в типах, а инлайновых функциях. В этом случае:
а) обьединяешь два типа в один заголовочник,
б) или выносишь инлайны в отдельный файл myheader.inl, который инклудишь в .cpp-файлах отдельно.
Думаю то что тело инлайна должно быть НЕ в обьявлении типа и так понятно.

Дерзай.

> g++ -E treeitem.cpp > treeitem.txt

> Вначале определение класса recordtabledata, только потом потом treeitem.


Это потому что "удачно" препроцессил не тот .cpp файл. А теперь повтори фокус с src/recordtablescreen.cpp и удивись.

Dendy ★★★★★
()
Ответ на: комментарий от Dendy

> 1. Препроцессор икнклудит тело заголовочника и делает #define RECORDTABLEDATA_H. > 2. Далее он инклудит treeitem.h и делает #define TREEITEM_H. > 3. Потом идёт дальше по телу treeitem.h и обнаруживает повторный #include "recordtabledata.h", заходит в него и... пропускает! Вместе с обьявлением RecordTableData. Получаешь вместо инклуда пустое место. Потому что смотри пункт 1: уже был #define RECORDTABLEDATA_H.

но класс recordtabledata уже был объявлен (и определен) на шаге 1, так что порядок объявления типов не меняется

anonymous
()
Ответ на: комментарий от anonymous

> но класс recordtabledata уже был объявлен (и определен) на шаге 1, так что порядок объявления типов не меняется

Ага, а программа топикстартера компилируется без ошибок.

tailgunner ★★★★★
()
Ответ на: комментарий от Dendy

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

xydo ★★
()
Ответ на: комментарий от xydo

а то из-за кольцевых инклудов, как уже отметил Dendy, действительно класс treeitem может оказаться раньше, чем recordtabledata.

xydo ★★
()
Ответ на: комментарий от tailgunner

>> но класс recordtabledata уже был объявлен (и определен) на шаге 1, так что порядок объявления типов не меняется

>Ага, а программа топикстартера компилируется без ошибок.

я ступил. действительно, порядок объявления меняется.

в любом случае, если есть циклические зависимости надо делать
forward declaration, иначе никак.

anonymous
()
Ответ на: комментарий от xintrea

Слоупок.jpg

Попытайся исключить максимум директив #include "..." из .h файлов. Чтобы не ругался на неизвестные типы пиши в начале своих .h файлах предварительные декларации типа:

class RecordTableData;

class ClassUsingRecordTableData { ... public: const RecordTableData& getRecordTableData() const; };

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

Absurd ★★★
()
Ответ на: комментарий от Absurd

>Еще желательно все приватные поля класса спрятать в pimpl - это еше более облегчит .h файл.

а потом применить NVI и захлебнуться соплями от восхищения

не стоит плодить сущностей до необходимости в них

jtootf ★★★★★
()

Все, парни, всем спасибо! Победил я эту траблу.

Стал действовать жостко и планомерно.

Вначале написал скрипт проверки четности " и соответствия количества () и {}. Все чисто было.

Потом из всех *.h файлов все инклуды попереносил в *.cpp файлы. По мере компиляции, если в *.h файле был неизвестный тип, добавлял инклюд в *.h. В конце концов все собралось.

xintrea
() автор топика
Ответ на: комментарий от xintrea

>По мере компиляции, если в *.h файле был неизвестный тип, добавлял инклюд в *.h.

Надо было просто добавить предварительную декларацию неизвестного типа в .h файл. Почитай у Саттера статью про минимизацию времени сборки проекта на С++.

Absurd ★★★
()
Ответ на: комментарий от Absurd

> Надо было просто добавить предварительную декларацию неизвестного типа в .h файл. Почитай у Саттера статью про минимизацию времени сборки проекта на С++.

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

Dendy ★★★★★
()
Ответ на: комментарий от Absurd

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

Dendy ★★★★★
()
Ответ на: комментарий от xintrea

> Победил я эту траблу.

> ...

> Потом из всех *.h файлов все инклуды попереносил в *.cpp файлы. По мере компиляции, если в *.h файле был неизвестный тип, добавлял инклюд в *.h.

Еще вопрос, кто кого победил :/

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>Еще вопрос, кто кого победил :/

ага. "победить в битве не означает выиграть войну" (с) ментат Харконненов, Dune II

jtootf ★★★★★
()
Ответ на: комментарий от Dendy

> 1. Препроцессор икнклудит тело заголовочника recordtabledata.h и делает #define RECORDTABLEDATA_H.
2. Далее он инклудит treeitem.h и делает #define TREEITEM_H.
3. Потом идёт дальше по телу treeitem.h и обнаруживает повторный #include "recordtabledata.h", заходит в него и... пропускает! Вместе с обьявлением RecordTableData. Получаешь вместо инклуда пустое место. Потому что смотри пункт 1: уже был #define RECORDTABLEDATA_H.

Не могу понять это рассуждение. В пункте 2 у нас произошел инклуд recordtabledata.h и treeitem.h в "общий" файл. Поэтому то, что в пункте 3 инклюд recordtabledata.h уже не будет инклюдится, нам побоку. Что я понял не так?

Может, вы имеете в виду, что при компилинге все DEFINE помнятся, и при компиляции очередного *.cpp файла он конструируется с учетом include и с учетом DEFINE, сформировавшихся в текущему моменту? Тогда да, если пункт 2 и пункт 3 будут в разных *.cpp файлах, то будет проблема. Но когда это происходит в одном файле, все должно работать чотко.

xintrea
() автор топика
Ответ на: комментарий от xintrea

Да ну, сколько нудно талдонить что для того чтобы компилятор проглотил *декларацию* класса ему нужно иметь полные определения *только* классов-предков. Для остальных типов использованных в декларации, т.е типы параметров/возвращаемых значений и полей ему нужно только знать что эти типы вообще *есть*. А для того чтобы сказать компилятору что эти типы вообще *есть* достаточно предварительной декларации вида class ClassThatExists; . Т.е .h файл длжен выглядеть так:

#ifndef __DERIVED_CLASS
#define __DERIVED_CLASS

#include "base_class.h"

class Param;
class Key;

class DerivedClass : public BaseClass /* declared in base_class.h */ {
...
public:
...
const Param& getParam(const Key& key) const;
...
};

#endif // __DERIVED_CLASS

Absurd ★★★
()
Ответ на: комментарий от xintrea

> Не могу понять это рассуждение. В пункте 2 у нас произошел инклуд recordtabledata.h и treeitem.h в "общий" файл. Поэтому то, что в пункте 3 инклюд recordtabledata.h уже не будет инклюдится, нам побоку. Что я понял не так?

дело в том, что #include "treeitem.h" идет до объявления класса recordtable,
и когда ты добрался до пунктов 2 и 3 -- этот клас все еще не определен.
В итоге объявление treeitem (или какой там класс) оказывается ранее
объявления recordtable.

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

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.