LINUX.ORG.RU

Ошибки при сборке тренировочного задания по C

 , , , ,


1

2

Есть код на C, для выполнения тренировочного задания: напишите программу для вывода всех строк входного потока, имеющих длину более 80 символов. Вот код: http://ix.io/12jI

Собираю с помощью GCC, вот выхлоп "cc -v"

Using built-in specs.
COLLECT_GCC=cc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.9.2-10' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.9.2 (Debian 4.9.2-10)
Получаю ошибки:
hw.c:5:5: error: conflicting types for ‘getline’
 int getline(char line[], int limit);
     ^
In file included from hw.c:1:0:
/usr/include/stdio.h:678:20: note: previous declaration of ‘getline’ was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
                    ^
hw.c:16:5: error: conflicting types for ‘getline’
 int getline(char line[], int limit) {
     ^
In file included from hw.c:1:0:
/usr/include/stdio.h:678:20: note: previous declaration of ‘getline’ was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
Не могу понять, что не так. Пожалуйста, помогите разобраться.

★★
Ответ на: комментарий от xaizek

Мне с невысоких позиций середины третьей главы кажется, что функция main должна возвращать значение типа int (exit-коды), а вот про возможные аргументы я пока не в курсе. Пожалуйста, если тебе не трудно, не мог бы ты рассказать про те сигнатуры, о которых ты говорил?

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

Две стандартные сигнатуры:

int main(void);
int main(int argc, char *argv[]);
Но реализации могут предоставлять дополнительные. Обычно ещё есть такая с переменными окружения:
int main(int argc, char *argv[], char *env[]);
В стандарте про это в секции «Program startup», его, кстати, можно скачать (черновики бесплатно, сами стандарты есть на торрентах; вообще и черновики годятся, там различия с финальной версией в форматировании) и подглядывать туда (требуется практика в нахождении нужного, ибо документ написан в формальном стиле, но поиск помогает в случаях как этот).

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

Ага, всё понял. Гуглинг ещё показал, что это - для тех случаев, когда сама программа вызывается с какими-нибудь параметрами. Стандарт буду иметь в виду. Спасибо!

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

И снова я :)

Есть функция itoa, преобразующая число в строку, вот код, работающий с ней: https://gist.github.com/anonymous/2113a011f37be8be0a30274b01f77fb0

Задание 3.4 гласит: «В представлении чисел с помощью дополнения до двойки наша версия функции itoa не умеет обрабатывать самое большое по модулю отрицательное число, т.е. значение n, равное -(2^длина_слова - 1). Объясните, почему это так. Доработайте функцию так, чтобы она выводила это число правильно независимо от системы, в которой она работает.»

Я так понял, что представление чисел с помощью дополнения до двойки - это один знаковый разряд (0 = +, 1 = -), а все остальные - под модуль числа, и тогда самое большое по модулю отрицательное - это, вроде как, ~0 (все единицы, которые помещаются в переменную).

Вспомнив, что целочисленные знаковые переменные принимают значения в диапазоне [-2 ^ (n - 1); 2 ^ (n - 1) - 1], где n - кол-во бит под переменную (правда, у меня это плохо в сознании сочетается с представлением чисел с помощью дополнения до двойки. Допустим, есть 5 бит. Тогда диапазон значений - от -16 до 15, как раз всего 32 разных значения. Максимум - это 01111 (0 означает +, 1111 = 15), но как быть с минимумом? По идее, на первом месте там 1, чтобы обозначить минус, но 16-то я уже никак не наберу 4 разрядами. Похоже, я что-то не так понял. Есть подозрение, что как-то замешан 10000 или 00000 - два нуля разного знака кажутся странными немного), я предположил, что для данного числа проблема возникает в строке 21. Как я понимаю, если n - максимальное по модулю отрицательное число, -n в ту же переменную уже не поместится. Так что, я завёл отдельную переменную типа unsigned int, и модуль числа n записывал в эту новую переменную un, дальше работал именно с ней. Однако, нужного результата в строке я всё равно не получил.

Пожалуйста, помоги мне разобраться во всем этом хитросплетении, если у тебя будет время и желание.

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

Там не модуль. Отрицательное число в этой системе получается инверсией бит и добавлением единицы. Единица и все нули это минимальное отрицительное число. Числа идут так:

0 1 ... 15 -16 -15 ... -1
Отрицательного нуля тут нет (он есть в обратном и прямом кодах, модуль числа, кстати, в прямом коде).

Про 21 строку и unsigned int правильно мыслишь, только унарный минус для минимального числа даст это же самое число (можно легко в этом убедиться самому), поэтому преобразование в положительное число unsigned типа надо делать вручную.

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

Так, эту теорию я понял, спасибо!

Вот что у меня получилось: https://gist.github.com/anonymous/f78942ff74945673256494055d9ddaf9

В строках 16 - 18 я рассчитываю то самое минимальное отрицательное число, которое требует особой обработки. В строках 34 - 42 происходит следующее:

  • Если число - минимальное отрицательное, то я просто записываю его в переменную типа unsigned, потому что, как я понял, в signed (если, допустим, под переменную отведено 5 бит) значения идут в порядке: 14 15 -16, а в unsigned в порядке 14 15 16, т.е чтобы из минимального отрицательного signed сделать положительное unsigned, менять число не нужно, достаточно лишь изменить способ его «восприятия» с signed на unsigned (кстати, а не должно было в этом месте произойти изменение самих данных из-за того, что я переменной типа unsigned присваиваю значение переменной типа int?)
  • Для остальных отрицательных чисел предыдущий метод не работает, но для них унарный минус даёт положительное число, поэтому это положительное число я записываю в un.
  • Остальные числа - неотрицательные, они представляются одинаково в signed и unsigned, un = n.

Этот код нормально работает для всех чисел.

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

кстати, а не должно было в этом месте произойти изменение самих данных из-за того, что я переменной типа unsigned присваиваю значение переменной типа int?

int и unsigned int это пара близких типов, которые обладают идентичным представлением, поэтому при преобразовании сами данные не меняются, только их интерпретация.

Этот код нормально работает для всех чисел.

Работает, но можно было сделать проще. Для минимального int есть константа INT_MIN, но на самом деле и оно не нужно, так как все числа можно обрабатывать единообразно. Под преобразованием вручную я имел в виду сделать:

un = (unsigned)~n + 1;
Это даст правильное абсолютное значение для любого отрицательного.

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

А зачем указание (unsigned)? Там же, вроде, в любом случае будет преобразование типов. Или нет?

С самой сутью преобразования разобрался. Я правильно понимаю, что ~n + 1 работает в любую сторону, делая из положительного числа отрицательное и наоборот?

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

А зачем указание (unsigned)? Там же, вроде, в любом случае будет преобразование типов. Или нет?

Будет, но оно произойдёт уже после переполнения (INT_MAX + 1), а переполнение знаковых чисел это undefined behaviour.

С самой сутью преобразования разобрался. Я правильно понимаю, что ~n + 1 работает в любую сторону, делая из положительного числа отрицательное и наоборот?

Да, преобразование работает одинаково (кроме минимального числа, когда оно не работает).

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

Разобрался, спасибо!

У меня вопрос не по теме: ты ведь используешь цветовую схему lucius в vim? Мне она пришлась по нраву, но почему-то при таком содержимом .vimrc:

"GUI
if has('gui_running')
	set guioptions-=t
        colorscheme lucius
	set guifont=Liberation\ Mono\ 10
"Terminal
else
    colorscheme lucius
endif
если я запускаю vim в терминале, то у меня сначала текст просто белый, никакой подсветки. Если переключиться на какую-нибудь другую схему и опять сделать colorscheme lucius, то заработает нормально. Ты с таким не сталкивался?

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

Да, её, хоть и старую версию (новая какая-то слишком яркая). Может просто не хватает

set background=dark
для консольного варианта?

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

Не помогло, но я посмотрел на ту тему, которую использовал раньше (256-grayvim) и увидел там set t_Co=256. После добавления этого в .vimrc у меня и люциус заработал, как надо. Вопрос: как считаешь, set t_Co=256 надо в .vimrc писать в секцию для терминала, или же в саму цветовую схему добавить? И ещё - для гуя ведь это не нужно, да?)

Norong ★★
() автор топика
Последнее исправление: Norong (всего исправлений: 1)
Ответ на: комментарий от Norong

Только хотел спросить за t_Co. И ещё выяснилось, что 'background' сбрасывается командой `:hi Normal ...`.

Прописывать стоит в .vimrc так как это настройка терминала.

t_Co для gui не надо, но оно и не мешает. А вообще при правильном $TERM оно само выставляется в 256, так что может стоит $TERM установить в «что-то-256color».

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

Я практикую xfce4-terminal и zsh. В .zshrc ничего про TERM не сказано. Если я пропишу в TERM поддержку 256 цветов (сейчас у меня TERM=xterm, эмулятор - xfce4-terminal, прописать надо xterm-256color, как я понимаю), хуже не станет там, где 256 не поддерживаются (например, на tty)? И ещё: мне в .zshrc писать, или где-то в другом месте?

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

Там в настройках xfce4-terminal где-то должно быть. Он выставляет $TERM. Может просто опция «256 цветов», а может поле для ввода (xterm-256color).

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

Там можно выставить режим эмуляции, но кроме xterm там нет вариантов. Я, наверное, тогда просто в vim t_Co установлю, остальное трогать не буду. Ведь если я в vim включу 256, но терминалом 256 не поддерживаются, ничего не сломается, он просто будет показывать, как без поддержки?

И, возвращаясь к коду:

un = (unsigned) ~n + 1;

Переполнение int'а у меня ведь может произойти только для минимального отрицательного числа при выполнении ~n, а также ещё и для предыдущего числа (на 1 больше, чем минимальное) при выполнении +1, так?

(unsigned) здесь означает, что всё, что дальше, имеет тип unsigned? Кстати, может, этого в K&R не было, или я подзабыл, но вот в такой ситуации, как здесь, у меня сначала выполняется ~n, потом +1, потом результат записывается в un. Пока выполняются вычисления, где хранится результат вычислений? Как я понимаю, они происходят в чём-то, что имеет тип int (потому что n имеет тип int), из-за этого может возникнуть переполнение, а (unsigned) делает так, чтобы вычисления происходили в чём-то типа unsigned и результат был таким же.

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

Ведь если я в vim включу 256, но терминалом 256 не поддерживаются, ничего не сломается, он просто будет показывать, как без поддержки?

Чёрно-белое или цвета по соседству должны быть, хотя детали могут зависеть от терминала.

Переполнение int'а у меня ведь может произойти только для минимального отрицательного числа при выполнении ~n, а также ещё и для предыдущего числа (на 1 больше, чем минимальное) при выполнении +1, так?

От ~ переполнения не будет, только при сложении.

(unsigned) здесь означает, что всё, что дальше, имеет тип unsigned?

Только выражение, которое следует за ним.

Пока выполняются вычисления, где хранится результат вычислений?

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

un = n;
un = ~un;
un = un + 1;

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

А тип временной переменной определяется из тех же соображений, из каких определяется тип результата?

И насчёт переполнения: я правильно понимаю, что переполнение возникает, когда для нового значения не хватает бит в переменной, т.е, например, в случае вроде?

В n 5 бит.
n = 0
~n = 11111
~n + 1 = 100000 // переполнение
Если так, то, наверное, в нашем случае оно может возникнуть только для n = 0, потому что тогда ~n = все_единицы, ~n + 1 даёт выход в следующий разряд?

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

А тип временной переменной определяется из тех же соображений, из каких определяется тип результата?

Да, всё в соответствии с операциями. +, ~ и подобные тип не меняют, поэтому он остаётся исходным int.

переполнение возникает, когда для нового значения не хватает бит в переменной, т.е, например, в случае вроде?

Это для беззнаковых и такое переполнение (оборачивание, в данном случае) разрешено. Для знаковых переполнение это когда затирается знаковый разряд при операции, т.е. сложили INT_MAX и 1, оба числа положительные, а получили отрицательный результат.

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

Так, про знаковые понял. А у беззнаковых при переполнении вида INT_MAX + 1 получается ноль, да?

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

А у беззнаковых при переполнении вида INT_MAX + 1 получается ноль, да?

Да, только UINT_MAX. Ну и наоборот (unsigned)0 - 1 == UINT_MAX. ( (unsigned)0 также можно записать как 0U).

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

Здравствуй! И снова я кое-что не полностью понимаю. Вот страница: https://i.imgur.com/qgxG5PA.png

Моё непонимание начинается со слов «В свете всего сказанного...» посередине страницы. Говорится о том, что отсутствует прототип функции, и идёт неявное объявление, но ведь в самом начале функции main объявлены и типы значений, возвращаемых функциями atof, getline, и типы аргументов, принимаемых этими функциями. Кроме того, если в такой ситуации возникают какие-то проблемы, мне не ясно, зачем объявлять функции внутри main, если раньше писался прототип вне какой-либо функции и с ним всё было хорошо. Ну и наконец тут говорится про отсутствие проверки на соответствие типов, если функция компилируется отдельно. Когда я includ'ю стандартные заголовочные файлы, то для функций, содержащихся в них, проверка типов происходит. Благодаря чему так получается, или, вернее, при каком условии будет то самое отсутствие проверки, о котором говорится в тексте? Пожалуйста, помоги мне разобраться в этом, если тебе не трудно.

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

Говорится о том, что отсутствует прототип функции, и идёт неявное объявление, но ведь в самом начале функции main объявлены и типы значений, возвращаемых функциями atof, getline, и типы аргументов, принимаемых этими функциями.
Благодаря чему так получается, или, вернее, при каком условии будет то самое отсутствие проверки, о котором говорится в тексте?

Это баг перевода, в оригинале «если отсутствует прототип функции» (а не «здесь», которое они взяли не пойми откуда).

Кроме того, если в такой ситуации возникают какие-то проблемы, мне не ясно, зачем объявлять функции внутри main, если раньше писался прототип вне какой-либо функции и с ним всё было хорошо.

Сейчас так объявлять смысла особого нет. Оно сокращает область видимости объявления, но увеличивает вероятность ошибок, что плохо.

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

Спасибо! Скачал и положил рядом оригинал, буду туда смотреть, если натолкнусь на странное.

Я правильно понимаю, что проверка типов выдаст ошибку, если типы несовместимы, а если тип аргумента, с которым вызывается функция, безболезненно приводится к типу, который функция ожидает, всё будет в порядке?

Меня вот эти слова смутили: The function atof must be declared and defined consistently. If atof itself and the call to it in main have inconsistent types in the same source file...

Или там речь только об объявлении и определении на самом деле?

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

Правильно, хотя, диапазон того, что разрешено, шире чем кажется. «Указатель преобразуется в число» и в обратную сторону являются предупреждениями, а не ошибками.

Или там речь только об объявлении и определении на самом деле?

Об этом. Главное чтобы при определении объявление тоже было видно компилятору, т.е. include соответствующего заголовка, тогда компилятор будет ругаться на расхождения.

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

Т.е. если я просто скомпилирую два исходника, в одном из которых используется некая функция, но она там не объявлена никаким образом, а в другом исходнике определена эта функция, то код скомпилируется, но без проверки типов аргументов и возвращаемого значения (и может получиться хаос). Если в первом исходнике будет объявление, но не согласованное с определением во втором исходнике, то тоже может получиться хаос, а если в первом исходнике видно (напрямую или через include хедера) согласованное объявление - всё будет хорошо?

И ещё - я правильно понимаю, что объявление нужно для использования, а если у меня в некотором исходнике функция просто определяется, то в нём не нужно объявление?

И какие ошибки может вызвать объявление функции внутри другой функции, кроме как то, что она не будет видна за её пределами?

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

... всё будет хорошо?

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

И ещё - я правильно понимаю, что объявление нужно для использования, а если у меня в некотором исходнике функция просто определяется, то в нём не нужно объявление?

Да.

И какие ошибки может вызвать объявление функции внутри другой функции, кроме как то, что она не будет видна за её пределами?

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

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

Только не уверен какой статус у этого механизма вывода определения типа функции при её первом использовании

Это то самое автоматическое предположение, что функция будет возвращать int?

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

Спасибо большое! Пошёл читать дальше :)

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

Всё таки удалили в C99:

Remove implicit function declaration.

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

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

И снова я. Возник совсем небольшой вопрос: в первой главе про внешние переменные было сказано:

Внешняя переменная должна быть определена ровно один раз за пределами всех функций программы. ... Она также должна быть объявлена в каждой функции, которая к ней обращается, с указанием её типа. Объявление может быть явным, с помощью оператора extern, а может быть и неявным - по контексту.

Позже, в параграфе, посвящённом внешним переменным, много где используются внешние переменные без какого-либо объявления внутри отдельных функций, их использующих (например: https://i.imgur.com/eRJyE7E.png). Это и есть то самое неявное объявление по конексту? Если да, то в каких случаях оно уместно, а в каких лучше эти переменные всё-таки extern'ом объявлять?

Norong ★★
() автор топика
Последнее исправление: Norong (всего исправлений: 1)
Ответ на: комментарий от Norong

Наверное, имеется в виду, что extern это явное, а если оно просто объявлено до функции, то это «по контексту». Объявлять внутри функции обычно не надо, разве что с environ (man environ) иногда так делают, так как используют лишь в одной функции.

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

так как используют лишь в одной функции.

А объявление с помощью extern внутри функции на что-нибудь влияет?

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

На видимость этой переменной внутри этой самой функции, если объявления данной переменной нет в глобальном контексте.

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

Внешняя, если где-то объявлена в другом объектном файле или библиотеке и в принципе доступна для обращения. extern просто делает её доступной в какой-то конкретной области видимости.

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

Т.е. я правильно понимаю, что переменная будет внешней, если она объявлена где-то, и это место объявления видно на этапе компиляции (тот же исходник, другой объектный файл, подключённый заголовочный файл), но для её использования необходимо, чтобы в том исходнике, о котором идёт речь, она была объявлена - либо без extern вне функций, либо с extern внутри каждой функции, где она нужна?

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

Все глобальные переменные, которые не static по определению внешние. Но если они в отдельном файле, то компилятор об этом не узнает без явного объявления. include может предоставлять такое объявление, а можно сделать это самому вне/в функции. Вне функций тоже можно с extern объявлять, кстати.

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

Вне функций тоже можно с extern объявлять, кстати.

А эффект будет отличаться от объявления без extern?

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

Хотя бы одно объявление во множестве файлов должно быть без extern иначе переменная не будет создана нигде.

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

Спасибо, понял!

У меня ещё небольшое недопонимание возникло. Вот есть код из учебника:

wget "http://ix.io/161D" -O - | base64 -d | tar -x

(По ссылке - tar с несколькими файлами исходного кода, закодированный в base64. После выполнения команды в текущем каталоге будут лежать исходники, 4 файла)

Вот выхлоп от компиляции:

getch.c: In function ‘ungetch’:
getch.c:12:9: warning: incompatible implicit declaration of built-in function ‘printf’
         printf("ungetch: too many characters\n");
         ^
getop.c: In function ‘getop’:
getop.c:26:14: error: ‘EOF’ undeclared (first use in this function)
     if (c != EOF)
              ^
getop.c:26:14: note: each undeclared identifier is reported only once for each function it appears in
getop.c:28:12: error: ‘NUMBER’ undeclared (first use in this function)
     return NUMBER;
            ^
pushpop.c: In function ‘push’:
pushpop.c:11:9: warning: incompatible implicit declaration of built-in function ‘printf’
         printf("error> stack full, can't push %g\n", f);
         ^
pushpop.c: In function ‘pop’:
pushpop.c:19:9: warning: incompatible implicit declaration of built-in function ‘printf’
         printf("error: stack empty\n");
         ^
Две ошибки: в getop.c неизвестно, что есть EOF и что есть NUMBER (NUMBER - это символическая константа, созданная в main.c). Три предупреждения: в ungetch, push и pop неявное объявление printf.

Если я в getop.c добавлю

#include <stdio.h>

#define NUMBER '0'
то ошибки исчезают, остаются только предупреждения.

Я понимаю происходящее так:

Предупреждения вызваны тем, что printf из stdio.h виден для функций ungetch, push и pop через main.c, но в их файлах исходного кода не объявлен (не будет проверки аргументов; чтобы она была, надо объявить (через include)).

Ошибка с EOF вызвана тем, что EOF (видимо, являющийся символической константой) тоже определён в stdio.h, но для getop не виден (кстати,

grep -R "#define EOF" /usr/include

не находит определения EOF).

Ошибка с NUMBER вызвана теми же причинами, что и с EOF - определён в main.c, но для getop не виден.

Правильно ли я понимаю природу этих явлений и то, что константы и макросы (#define) должны объявляться непосредственно в каждом исходнике, в котором они используются (напрямую или через include)?

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

Предупреждения вызваны тем, что printf из stdio.h виден для функций ungetch, push и pop через main.c, но в их файлах исходного кода не объявлен (не будет проверки аргументов; чтобы она была, надо объявить (через include)).

Через main.c компилятору ничего не видно. При компиляции других файлов, он без понятия о том, что находится в других (если они не включены через include).

То что не объявлен без include <stdio.h> это да.

grep -R «#define EOF» /usr/include

Оно может быть где-то в /usr/lib64/gcc.

константы и макросы (#define) должны объявляться непосредственно в каждом исходнике, в котором они используются (напрямую или через include)?

Да. Они существуют только во время компиляции, в выходе компиляции есть только результат их применения.

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

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

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

Ага. И, если аргументы будут угаданы неправильно, произойдёт ошибка во время выполнения. Так что лучше так не делать.

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

И вот насчёт этого

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

У меня в некоторых файлах определены функции, которые объявляются и используются в других файлах. Получается, что компилятор, собирая исходник, в котором объявляется и используется функция из другого исходника, не может быть уверен, что такая функция вообще существует? Я попробовал закомментировать функцию push и собрать исходники - получил:

/tmp/ccOrluGk.o: In function `main':
main.c:(.text+0x38): undefined reference to `push'
main.c:(.text+0x5c): undefined reference to `push'
main.c:(.text+0x80): undefined reference to `push'
main.c:(.text+0xa2): undefined reference to `push'
main.c:(.text+0xd7): undefined reference to `push'
collect2: error: ld returned 1 exit status
Я правильно понимаю, что это уже проблемы не компиляции, а компоновки?

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

Я правильно понимаю, что это уже проблемы не компиляции, а компоновки?

Да. Компилятор там уже не участвует.

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