Russian Qt Forum
Ноябрь 23, 2024, 08:27 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: Как найти ошибку в C/C++ коде? Видимо где-то забыл ; или " или }  (Прочитано 29762 раз)
ритт
Гость
« Ответ #15 : Декабрь 23, 2008, 15:41 »

http://osdab.42cows.org/tips/predeclare.php?mode=advanced
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #16 : Декабрь 24, 2008, 10:16 »

Все, парни, всем спасибо!

Победил я эту траблу. Стал действовать жостко и планомерно. Вначале написал скрипт проверки четности " и соответствия количества () и {}. Все чисто было.

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

По поводу прописывания предварительного объявления класса class вместо include, слышал что не все компилеры поддерживат эту возможность. Так как проверить на винде компиляцию пока не могу, то стараюсь писать универсальный код. Кроме того, внятной информации по предварительному объявлению класса не нашел. В книгах, что есть у меня, про это вообще ничего не написано. Да честноговоря пока не могу найти ни одной книги по c/c++, в которой бы по-человечески было написано как компилируется проект, как пользоваться include. Почему-то этот вопрос везде обходят стороной.


Вот на лоре мне такое написали:

Цитировать
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 уже не будет инклюдится, нам побоку. Что я понял не так?
Записан

Собираю информацию по крупицам
http://webhamster.ru
BRE
Гость
« Ответ #17 : Декабрь 24, 2008, 10:34 »

Вот попробуй собрать этот проект:
classA.h
Код:
#ifndef CLASSA_H
#define CLASSA_H

#include "classB.h"

class ClassA : public ClassB
{
public:
ClassA() {}

int     val;
};

#endif // CLASSA_H

classB.h
Код:
#ifndef CLASSB_H
#define CLASSB_H

#include "classA.h" // [1]

//class ClassA; // [2]

class ClassB
{
public:
ClassB() {}

ClassA  *ptr;
};

#endif // CLASSB_H

main.cpp
Код:
#include "classA.h"

int main( int, char *[] )
{
ClassA t;

return 0;
}

Ты увидешь ту же ошибку, которая была у тебя. Если ты поменяешь комментарии в строках [1] и [2] файла classB.h, то проект собереться. Ошибка происходит из-за циклической зависимости заголовков classA.h и classB.h.

Если в описании класса используется указатель или ссылка на другой класс,  лучше не включать его хедер, а описать как class ClassA;
Этим ты явно указываешь компилятору, что знаешь такой класс, а размер указателя/ссылки компилятор знает сам для целевой машины.
Записан
ритт
Гость
« Ответ #18 : Декабрь 24, 2008, 10:39 »

бегло пробежался по заголовкам...
main.h -> mainwindow.h -> recordtabledata.h, ..., treeitem.h -> recordtabledata.h
примерно так.
второй раз recordtabledata.h вызывается из контекста treeitem.h, когда гвард RECORDTABLEDATA_H уже включён - т.е. хедер подключился, но в контексте mainwindow.h, который теперь уже дёргать снизу бесполезно.
Записан
ритт
Гость
« Ответ #19 : Декабрь 24, 2008, 10:43 »

зы. с предекларами проблем не должно быть - можешь смело пользоваться (если не веришь, загляни в любые заголовки кутэ и вспомни количество платформ и компиляторов, поддерживаемых кутэ)
и что немаловажно, с предекларами крупный проект будет собираться значительно шустрее...
Записан
BRE
Гость
« Ответ #20 : Декабрь 24, 2008, 10:51 »

Что бы проше было разобраться сделай для моего примера: g++ -E main.cpp, для каждого варианта и сравни.
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #21 : Декабрь 24, 2008, 14:22 »

Что бы проше было разобраться сделай для моего примера: g++ -E main.cpp, для каждого варианта и сравни.

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

Такие древние условности меня сильно огорчают. Это то же самое, что некоторые компилеры требуют прототипы функций, если реализация расположена ниже по коду, а другие не требуют. Казалось бы, невелика задача - пробежать код вперед и посмотреть наличие реализации. Так ведь нет - пиши прототип. Или когда синтаксический анализатор компилера не обнаруживает ошибки вида function()); а реалтаймовый анализатор IDE их видит мгновенно. Это уже вообще никуда не годится.
Записан

Собираю информацию по крупицам
http://webhamster.ru
BRE
Гость
« Ответ #22 : Декабрь 24, 2008, 14:30 »

Такие древние условности меня сильно огорчают. Это то же самое, что некоторые компилеры требуют прототипы функций, если реализация расположена ниже по коду, а другие не требуют. Казалось бы, невелика задача - пробежать код вперед и посмотреть наличие реализации. Так ведь нет - пиши прототип. Или когда синтаксический анализатор компилера не обнаруживает ошибки вида function()); а реалтаймовый анализатор IDE их видит мгновенно. Это уже вообще никуда не годится.
Стандарты это. Без них была бы вообще жопа.  Подмигивающий
А ты сейчас поставь (когда проект собирается) не нужную скобку, думаю эту ошибку ты найдешь значительно быстрей.
Записан
ритт
Гость
« Ответ #23 : Декабрь 24, 2008, 15:03 »

> Казалось бы, невелика задача - пробежать код вперед и посмотреть наличие реализации.
пробегись по-шустрому по коду glibc и найди реализацию функции strcpy...или по коду kernel.org и ткни пальцем в semtimedop...угу?

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

зы. приведи пример такого компилера? навскидку ничего подобного не вспоминается...даже какой-нибудь задрипанный паскаль имел ключ forward (или как-то так)
Записан
Eugene Efremov
Гость
« Ответ #24 : Декабрь 24, 2008, 18:46 »

По поводу прописывания предварительного объявления класса class вместо include, слышал что не все компилеры поддерживат эту возможность. Так как проверить на винде компиляцию пока не могу, то стараюсь писать универсальный код. Кроме того, внятной информации по предварительному объявлению класса не нашел. В книгах, что есть у меня, про это вообще ничего не написано.

Стандарт C++. ISO/IEC 14882:2003(E), 9.1 §2:
Цитировать
Note: Such declarations allow definition of classes that refer to each other. [Example:
Код
C++ (Qt)
class Vector;
class Matrix {
// ...
friend Vector operator*(Matrix&, Vector&);
};
class Vector {
// ...
friend Vector operator*(Matrix&, Vector&);
};
 
Declaration of friends is described in 11.4, operator functions in 13.5. ]
См. также раздел 3.1.

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

Что касается книг — подробный разбор того, в каких случаях достаточно объявления класса, а в каких — требуется полное определение, я видел не то у Саттера, не то у Мейерса. (Их, кстати, в любом случае стоит прочитать обоих).

Если кратко, то записи «class Foo;» хватит для использования указателей и ссылок на класс, а также для объявления методов, которые его используют. В общем, для всего того, что обычно лежит как раз в хедерах. Если же нужно знать размер класса, создавать объекты, вызывать методы, создавать производные классы и т.д. — нужно полное определение.

Да честноговоря пока не могу найти ни одной книги по c/c++, в которой бы по-человечески было написано как компилируется проект, как пользоваться include. Почему-то этот вопрос везде обходят стороной.

Хм... Страуструп подойдет? Разделы 7.8, 9.3.2, 9.3.3. Ну и глава 16 стандарта — для более продвинутого чтения...

Но вообще, действительно — имеется некоторый пробел в литературном освещении вопроа. Бегло просмотрел имеющиеся у меня источники — про препроцессор везде пишут довольно скупо. Единственное исключение — руководство по GCC Гриффиса. Но там много нестандартных фич.
« Последнее редактирование: Декабрь 24, 2008, 18:49 от Eugene Efremov » Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #25 : Декабрь 24, 2008, 20:35 »

> Казалось бы, невелика задача - пробежать код вперед и посмотреть наличие реализации.
пробегись по-шустрому по коду glibc и найди реализацию функции strcpy...или по коду kernel.org и ткни пальцем в semtimedop...угу?

зы. приведи пример такого компилера? навскидку ничего подобного не вспоминается...даже какой-нибудь задрипанный паскаль имел ключ forward (или как-то так)

Как ни странно, gcc 3.x версии в режиме компиляции C кода (не C++) именно это и делает.
Записан

Собираю информацию по крупицам
http://webhamster.ru
ритт
Гость
« Ответ #26 : Декабрь 25, 2008, 02:31 »

> Казалось бы, невелика задача - пробежать код вперед и посмотреть наличие реализации.
пробегись по-шустрому по коду glibc и найди реализацию функции strcpy...или по коду kernel.org и ткни пальцем в semtimedop...угу?

зы. приведи пример такого компилера? навскидку ничего подобного не вспоминается...даже какой-нибудь задрипанный паскаль имел ключ forward (или как-то так)

Как ни странно, gcc 3.x версии в режиме компиляции C кода (не C++) именно это и делает.

гоняет вперёд-назад? не знал...
т.е. что, если переложить пример BRE на си и попробовать скомпилить тройкой, всё заведётся и поедет?
Записан
xintrea
Супер активный житель
*****
Offline Offline

Сообщений: 754



Просмотр профиля WWW
« Ответ #27 : Декабрь 25, 2008, 13:46 »

Пример BRE не знаю как на си переделать, а код вида

Код:
#include <stdio.h>

int main(int argc,char **argv)
{
 printf("%d",userfunc(3));
}

int userfunc(int x)
{
 return x+pow2(x);
}

int pow2(int x)
{
 return x*x;
}

Замечательно компилится на gcc как третей так и, кстати, четвертой ветки.

gcc -L/usr/lib -x c -o userfunc userfunc.c

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

Собираю информацию по крупицам
http://webhamster.ru
ритт
Гость
« Ответ #28 : Декабрь 25, 2008, 14:35 »

я не заметил момент, в который мы перестали говорить о декларациях классов и перешли на обсуждение деклараций функций...это неравнозначные вопросы
а твой пример будет компилиться и на втором гцц
Записан
BRE
Гость
« Ответ #29 : Декабрь 25, 2008, 14:48 »


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

А ты варнинги, варнинги включи (-Wall).
А то что gcc нашел эти функции в одном файле, ни о чем не говорит. А если функцию userfunc описать в другом файле. Он ее найдет?
Записан
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.138 секунд. Запросов: 23.