Стандартные библиотеки включены несколько раз в несколько файлов?

В книге K&R (стр. 59) (редактирование: второе издание, охватывающее ANSI C) предполагается, что большие проекты проще разбивать на несколько файлов. В каждом файле, как обычно, вверху включены несколько библиотек: например. getop.c нуждается в stdio.h, а также в stack.c и main.c.

Фрагменты примерно такие:

//main.c
#include <stdio.h>
#include <stdlib.h>
#include "calc.h"
int main(void)
{
    //etc
}


//getop.c
#include <stdio.h>
#include <ctype.h>
#include "calc.h"
getop()
{
    /*code*/
}

//stack.c
#include <stdio.h>
#include "calc.h"
void push(double val)
{
    //code
}

Мне трудно понять, как работает включение стандартных библиотек несколько раз в проект. Конечно, чтобы пользовательские файлы .c могли получить доступ к встроенным функциям, нам нужно включить #include <header.h>, чтобы они знали о существовании printf() и getchar() и т. д., но не увеличит ли этот подход размер файла конечная программа, если stdio.h включается четыре раза вместо одного (если бы все помещалось в один файл)?

K&R отмечает, что разделение программы на несколько файлов в конечном итоге усложняет поддержку всех файлов .h.

Я полагаю, что на самом деле я спрашиваю, как компилятор решает проблему включения одной библиотеки в проект несколько раз.

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

Файл "module.h"

#ifndef MODULE_H
#define MODULE_H

struct foo {
    int member;
};

#endif /* MODULE_H */

Файл "mod2.h"

#include "module.h"

Файл "прог.с"

#include "module.h"
#include "mod2.h"

ссылки


person Thomas E    schedule 15.02.2015    source источник
comment
Вы можете прочитать и узнать о единицах перевода.   -  person Some programmer dude    schedule 15.02.2015


Ответы (2)


Я полагаю, что на самом деле я спрашиваю, как компилятор решает проблему включения одной библиотеки в проект несколько раз.

вы не включаете библиотеку с помощью #include <stdio.h>, вы просто включаете ее объявления, поэтому компилятор знает, какие функции существуют. Компоновщик заботится о том, чтобы включить библиотеку и собрать все вместе.

person wimh    schedule 15.02.2015

Поскольку они используют что-то под названием include guards, предположим, что ваши собственные включаемые файлы должны быть включены более одного раза, тогда вы можете сделать это

MyHeader.h

#ifndef MY_HEADER_H
#define MY_HEADER_H
/* file content goes here */
#endif /* MY_HEADER_H */

Тогда у вас есть другой заголовок

**AnotherHeader.h**

#ifndef MY_ANOTHER_HEADER_H
#define MY_ANOTHER_HEADER_H
#include "MyHeader.h"
/* file content goes here */
#endif /* MY_ANOTHER_HEADER_H */

и в вашей программе

main.c

/* 
 * MY_HEADER_H is undefined so it will be defined and MyHeader.h contents 
 * will be included.
 */
#include "MyHeader.h" 
/*
 * MY_HEADER_H is defined now, so MyHeader.h contents will not be included
 */
#include "AnotherHeader.h"

int main()
{
    return 0;
}

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

person Iharob Al Asimi    schedule 15.02.2015
comment
__MY_HEADER_H__ — зарезервированное имя - person qdii; 15.02.2015