Почему пропуск «#include » только иногда вызывает сбои компиляции?

#c #file-io #fstream

#c #строка #включить

Вопрос:

Я новичок в C . Когда я пишу код, иногда я пишу, #include <string> и код работает, в других случаях я не пишу, #include <string> и код не работает. Но иногда это работает без #include <string> .

Так должен ли я писать #include <string> так, чтобы код работал?

Комментарии:

1. Люди, какого черта?! Конечно, это реальный, законный вопрос! Откуда берутся все отрицательные и закрывающие голоса? Возвращайтесь в постель.

2. Каким-то образом, на мой взгляд, этот вопрос был таким же эффективным, как these are not the droids you are looking for .

3. Вопрос мог бы быть сформулирован лучше, но когда они начинают с «Я новичок в C «, можно с уверенностью предположить, что они могут не совсем понимать, как работают includes, и оставить комментарий вместо голосования «закрыть» или «против».

Ответ №1:

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

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

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

Всегда включайте все необходимые заголовки. К сожалению, похоже, что нет надежной онлайн-документации, в которую необходимо включить заголовки. Обратитесь к книге или официальному стандарту C .

Например, следующий код компилируется с моим компилятором ( gcc 4.6):

 #include <iostream>

int main() {
    std::string str;
}
  

Но если я удаляю первую строку, она больше не компилируется, хотя iostream заголовок на самом деле должен быть несвязанным.

Комментарии:

1. Есть идеи, происходит ли это с файлами заголовков, отличными от <string> ?

2. @C.R. Очевидно, что это определенно так, поскольку стандартные заголовки иногда используют друг друга. Например, std::pair определено в <utility> , но нет реализации стандартной библиотеки, которая требует, чтобы вы включили этот заголовок, если вы уже включили <map> поскольку последнее требует std::pair . То же самое верно и для других типов. Это, кстати, одна из больших слабостей системы заголовков C по сравнению с надлежащей модульной системой.

3. Что касается К сожалению, похоже, что нет надежной онлайн-документации, в которую необходимо включить заголовки. вы всегда можете выполнить поиск того, что вы используете на cppreference.com и он сообщит вам, в каком заголовке он находится.

4. @NathanOliver Да, теперь они тщательно документируют это. Предыдущие версии этого не делали. cppreference.com в настоящее время это моя ссылка № 1 для перехода.

Ответ №2:

Возможно, что в них есть другие заголовки, которые вы включаете, которые вы #include <string> включаете.

Тем не менее, обычно это хорошая идея #include <string> непосредственно в вашем коде, даже если это не является строго необходимым для успешной сборки, на случай, если эти «другие» заголовки изменяются — например, из-за другой (или иной версии) реализации компилятора / стандартной библиотеки, платформы или даже просто конфигурации сборки.

(Конечно, это обсуждение применимо к любому заголовку, а не только <string> .)

Ответ №3:

Хотя прямого появления #include <string> в конкретном исходном файле нет, это не означает, что он не был включен другим заголовочным файлом. Рассмотрим это:

Файл: header.h

 #if !defined(__HEADER_H__)
#define __HEADER_H__

// more here
#include <string>
// ...and here

#endif
  

Файл: source1.cc

 #include <string>

void foo()
{
    // No error here.
    string s = "Foo";
}
  

Файл: source2.cc

 #include <header.h>

void bar()
{
    // Still no error, since there's a #include <string> in header.h
    string s = "Bar";
}
  

Файл: source3.cc

 void zoid()
{
    // Here's the error; no such thing as "string", since non of the
    // previous headers had been included.
    string s = "Zoid";
}
  

Ответ №4:

Если вы просто используете указатель / ссылку на пользовательский тип, тип должен быть только объявлен:

 class my_class;
void foo(const my_classamp; c);
  

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

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

Ответ №5:

Неверно, что строка заголовка включена в другие заголовки. Сама строка заголовка содержит только includes. Нет определений. Таким образом, все необходимые определения, необходимые для использования string, содержатся в заголовках, включенных в строку заголовка. Эти заголовки могут быть уже включены другими заголовками. Тогда все работает. Заголовок ios, например, включает stringbuf , который включает …

Ответ №6:

Даже если вы явно не включили строку, она была включена из-за другого стандартного заголовка, который вы включили. Например, vector мог содержать строку. Когда вы включаете vector, каждая вещь из vector будет включена в ваш файл.

Я думаю, что в будущих версиях Cpp должно быть ключевое слово include_module или module; которые включают только определенный модуль из файла. Итак, если файл имеет 3 класса, мы включаем только тот, который нам нужен.

например
-I «../mingw/lib/include»

  module <string>
  

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

Ответ №7:

Как сказал Бранко:

Возможно, что другие заголовки, которые вы включаете, содержат #include в них.

Давайте взглянем на iostream включает:

 #include <bits/c  config.h>
#include <ostream>
#include <istream>
  

если вы проверите, istream вы можете увидеть некоторые include
итак, вот так мы имеем:

iostream => istream => ios => iosfwd

и в iosfwd у нас есть библиотека строк! но это не стандартно, это для прямого объявления. в iosfwd мы имеем:

#включить <bits/stringfwd.h> // Для объявлений пересылки строк.

и в stringfwd.h :

 @file bits/stringfwd.h
This is an internal header file, included by other library headers.
Do not attempt to use it directly. @headername{string}
  

итак, вы можете использовать string без #include <string> .