Пример проекта из нескольких файлов

Пример проекта из нескольких файлов

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

Создадим каталог проекта kalkul2. В нём создадим три файла: calculate.h, calculate.c, main.c.

Файл calculate.h:

///////////////////////////////////////

// calculate.h



#ifndef CALCULATE_H_

#define CALCULATE_H_



float Calculate(float Numeral, char Operation[4]);



#endif /*CALCULATE_H_*/

Файл calculate.c:

////////////////////////////////////

// calculate.c



#include <stdio.h>

#include <math.h>

#include <string.h>

#include "calculate.h"



float Calculate(float Numeral, char Operation[4])

{

float SecondNumeral;

if(strncmp(Operation, "+", 1) == 0)

{

printf("Второе слагаемое: ");

scanf("%f",&SecondNumeral);

return(Numeral + SecondNumeral);

}

else if(strncmp(Operation, "-", 1) == 0)

{

printf("Вычитаемое: ");

scanf("%f",&SecondNumeral);

return(Numeral - SecondNumeral);

}

else if(strncmp(Operation, "*", 1) == 0)

{

printf("Множитель: ");

scanf("%f",&SecondNumeral);

return(Numeral * SecondNumeral);

}

else if(strncmp(Operation, "/", 1) == 0)

{

printf("Делитель: ");

scanf("%f",&SecondNumeral);

if(SecondNumeral == 0)

{

printf("Ошибка: деление на ноль! ");

return(HUGE_VAL);

}

else

return(Numeral / SecondNumeral);

}

else if(strncmp(Operation, "pow", 3) == 0)

{

printf("Степень: ");

scanf("%f",&SecondNumeral);

return(pow(Numeral, SecondNumeral));

}

else if(strncmp(Operation, "sqrt", 4) == 0)

return(sqrt(Numeral));



else if(strncmp(Operation, "sin", 3) == 0)

return(sin(Numeral));

else if(strncmp(Operation, "cos", 3) == 0)

return(cos(Numeral));

else if(strncmp(Operation, "tan", 3) == 0)

return(tan(Numeral));

else

{

printf("Неправильно введено действие ");

return(HUGE_VAL);

}

}

Файл main.c:

////////////////////////////////////////

// main.c



#include <stdio.h>

#include "calculate.h"



int main(void)

{

float Numeral;

char Operation[4];

float Result;

printf("Число: ");

scanf("%f",&Numeral);

printf("Арифметическое действие (+,-,*,/,pow,sqrt,sin,cos,tan): ");

scanf("%s",&Operation);

Result = Calculate(Numeral, Operation);

printf("%6.2f\n",Result);

return 0;

}

У нас есть два файла исходного кода (c-файлы) и один заголовочный (h-файл). Заголовочный включается в оба c-файла.

Скомпилируем calculate.c.

gcc -c calculate.c

Получили calculate.o. Затем main.c.

gcc -c main.c

И вот он main.o перед нами! Теперь, как вам уже, наверное, подсказывает интуиция, надо из этих двух объектных файлов сделать запускаемый.

gcc calculate.o main.o -o kalkul

Упс... и не получилось... Вместо столь желаемого запускаемого файла, в консоли появилась какая-то ругань:

calculate.o(.text+0x1b5): In function `Calculate':

calculate.c: undefined reference to `pow'

calculate.o(.text+0x21e):calculate.c: undefined reference to `sqrt'

calculate.o(.text+0x274):calculate.c: undefined reference to `sin'

calculate.o(.text+0x2c4):calculate.c: undefined reference to `cos'

calculate.o(.text+0x311):calculate.c: undefined reference to `tan'

collect2: ld returned 1 exit status

Давайте разберёмся, за что нас так отругали. Undefined reference означает ссылку на функцию, которая не определена. В данном случае gcc не нашёл определения функций pow, sqrt, sin, cos, tan. Где же их найти?

Как уже говорилось раньше, определения функций могут находиться в библиотеках. Это скомпилированные двоичные файлы, содержащие коллекции однотипных операций, которые часто вызываются из многих программ, а потому нет смысла многократно писать их код в программах. Стандартное расположение файлов библиотек – каталоги /usr/lib и /usr/local/lib (при желании можно добавить путь). Если библиотечный файл имеет расширение .a, то это статическая библиотека, то есть при компоновке весь её двоичный код включается в исполняемый файл. Если расширение .so, то это динамическая библиотека. Это значит в исполняемый файл программы помещается только ссылка на библиотечный файл, а уже из него и запускается функция.

Когда мы писали программу hello, мы использовали функцию printf для вывода текстовой строки. Однако, как вы помните, мы нигде не писали определения этой функции. Откуда же она тогда вызывается?

Просто при компоновке любой программы компилятор gcc по умолчанию включает в запускаемый файл библиотеку libc. Это стандартная библиотека языка C. Она содержит рутинные функции, необходимые абсолютно во всех программах, написанных на C, в том числе и функцию printf. Поскольку библиотека libc нужна во всех программах, она включается по умолчанию, без необходимости давать отдельное указание на её включение.

Остальные библиотеки надо требовать включать явно. Ведь нельзя же во все программы помещать абсолютно все библиотеки. Тогда исполняемый файл раздуется до немыслимо крупных размеров. Одним программам нужны одни функции, другим – другие. Зачем же засорять их ненужным кодом! Пусть остаётся только то, что реально необходимо.

Нам в данном случае нужна библиотека libm. Именно она содержит все основные математические функции. Она требует включения в текст программы заголовочного файла <math.h>.

Помимо этого дистрибутивы Linux содержат и другие библиотеки, например:

libGL Вывод трёхмерной графики в стандарте OpenGL. Требуется заголовочный файл <GL/gl.h>.

libcrypt Криптографические функции. Требуется заголовочный файл <crypt.h>.

libcurses Псевдографика в символьном режиме. Требуется заголовочный файл <curses.h>.

libform Создание экранных форм в текстовом режиме. Требуется заголовочный файл <form.h>.

libgthread Поддержка многопоточного режима. Требуется заголовочный файл <glib.h>.

libgtk Графическая библиотека в режиме X Window. Требуется заголовочный файл <gtk/gtk.h>.

libhistory Работы с журналами. Требуется заголовочный файл <readline/readline.h>.

libjpeg Работа с изображениям в формате JPEG. Требуется заголовочный файл <jpeglib.h>.

libncurses Работа с псевдографикой в символьном режиме. Требуется заголовочный файл <ncurses.h>.

libpng Работа с графикой в формате PNG. Требуется заголовочный файл <png.h>.

libpthread Многопоточная библиотека POSIX. Стандартная многопоточная библиотека для Linux. Требуется заголовочный файл <pthread.h>.

libreadline Работа с командной строкой. Требуется заголовочный файл <readline/readline.h>.

libtiff Работа с графикой в формате TIFF. Требуется заголовочный файл <tiffio.h>.

libvga Низкоуровневая работа с VGA и SVGA. Требуется заголовочный файл <vga.h>.

А также многие-многие другие.

Обратите внимание, что названия всех этих библиотек начинаются с буквосочетания lib-. Для их явного включения в исполняемый файл, нужно добавить к команде gcc опцию -l, к которой слитно прибавить название библиотеки без lib-. Например, чтобы включить библиотеку libvga надо указать опцию -lvga.

Нам нужны математические функции pow, sqrt, sin, cos, tan. Они, как уже было сказано, находятся в математической библиотеке libm. Следовательно, чтобы подключить эту библиотеку, мы должны указать опцию -lm.

gcc calculate.o main.o -o kalkul -lm

Ура! Наконец-то наш запускаемый файл создан!

Оглавление | Назад | Вперед

[ опубликовано 06/09/2006 ]

Дмитрий Пантелеичев (dimanix2006 at rambler dot ru) - Пример проекта из нескольких файлов   Версия для печати