3

On converting an integer to a character string in C, I'm a little annoyed about including stdio.h – wouldn't that bloat the binary with unnecessary file descriptors and other code? Or are the streams only opened if you use the standard io file descriptors stdout, stdin, or stderr in your code, such as with printf, scanf, fprintf? Maybe it's still ok if my code only uses snprintf(3)?

The closest ready-made solution I have is to cast the integer as a double precision floating point for strfromd(3) with a format string that doesn't print past the decimal point.

There is a good do-it-yourself solution given by bhuwansahni in How to convert integer to string in C?

I do not see how to use (linking didn't work) itoa, _itoa, or _fitoa_word, though _fitoa_word does show up as a function with nm /lib64/libc6.so.6.

iBug
  • 35,554
  • 7
  • 89
  • 134

6 Answers6

6

They are opened even if you do NOT include stdio.h. They are the standard streams of a program and are opened already when it's run.

A program needs some initialization before it's actually executed. This includes loading symbol tables, assigning memory, initializing static data and linking dynamic libraries and so on. The standard streams are opened at this point. Then the control goes to the main() function in your program and it starts executing.

For the 2nd question, the standard IO doesn't bloat your program because your program is almost always linked against the standard C library libc, and is often link dynamically (so it doesn't add to the size of your executable).

Yes it's fine if you don't use them. You can do it all well using only with stuffs like snprintf(), but the standard streams will still be opened. Using them or not doesn't matter.

Thanks Paul Ogilvie

iBug
  • 35,554
  • 7
  • 89
  • 134
  • 1
    ...and they are opened by the operating system – Paul Ogilvie Nov 27 '17 at 11:01
  • oh yeah, forgot about that – Rory Mulvaney Nov 27 '17 at 11:25
  • 1
    Why do you think they are opened by the operating system and not, say, the shell? – Eric Postpischil Nov 27 '17 at 12:18
  • @EricPostpischil Who opens the streams for the shell? – iBug Nov 27 '17 at 12:21
  • 1
    iBug: The shell can open its own streams. – Eric Postpischil Nov 27 '17 at 12:27
  • @EricPostpischil I mean the three standard streams. – iBug Nov 27 '17 at 12:33
  • @iBug so, how does a shell implement redirection, what do you think? –  Nov 27 '17 at 12:36
  • Good to see your knowledge here. I'm confused now. – iBug Nov 27 '17 at 12:45
  • A login shell may inherit the three standard streams from its parent. For a traditional terminal shell, that might be the `login` process on Unix systems. I am not sure whether `login` would inherit its streams or open them itself, possibly with direction from its parent about which device to use. For a windowed terminal application, it might be the window manager. In that case, the window manager may need to create a pseudo-terminal device and open streams to it, which it then lets the shell inherit. For non-login shells, they inherit from their parent shells. – Eric Postpischil Nov 27 '17 at 14:02
  • However, suppose you execute a program with redirection, such as `foo output`. Then what happens may be something like: The shell forks to create a child process. That child process, still running shell code, closes streams 0 and 1 and opens new streams 0 and 1 associated with the named files. Then that child process executes the requested executable, which inherits the redirected streams. (I am somewhat filling in details from a thirty-year-old memory, so the particulars may be different.) – Eric Postpischil Nov 27 '17 at 14:05
  • In any case, it is not the operating system per se that opens the streams, at least in most cases. For the purposes of this question, it may be simpler just to say that a normal process inherits its standard streams from its parent and leave out mention of operating systems, shells, or special processes. Or, even simpler, just say that a normal process starts with the standard streams already open. (But note that not all processes have the standard streams open. If the OP is writing some special-purpose code, it might not use streams, and they might not want the library overhead.) – Eric Postpischil Nov 27 '17 at 14:07
  • I/O streams are setup even before the system starts to load the executable. I/O stream setup happens before the `exec()` call, library loading and other initialization happens after that. – cmaster - reinstate monica Nov 27 '17 at 14:54
  • Reflecting on the shell process to spawn an executable, I think it might go like this: The shell duplicates the stream to be redirected. E.g., it makes stream 4 be a duplicate of stream 0. Then it closes stream 0. Then it opens a new stream 0 associated with the desired file. (At this point, if an occur occurs, it prints an error message and stops trying to spawn a command. Being able to handle this error as desired is why we want to open streams in the parent.) Then it forks. The parent reverts its streams to normal. The child closes the duplicate in 4. Then the child executes the executable. – Eric Postpischil Nov 27 '17 at 15:09
3

No, those files are not "opened" by including a header file. Including a header file happens a compile-time, the opening of the file at run-time.

All programs needs some initialization done before the main function is called. This is usually done by the program not really starting at the main function, but at some other piece of code, a piece that is supplied by the compiler. This pre-main code will do some general initialization needed for your program to be able to function properly, and among that initialization is the setup of the standard files.

How those files are created, and if they reference some lower-level file-handle (like e.g. the FILENO_STDOUT file descriptor for stdout on a POSIX system) is largely irrelevant. All you need to know is that e.g. stdout will exist when execution starts in the main function.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
2

Including the stdio header or not is unrelated to whether these file descriptors will open or not.

These are standard streams that are opened from the Operating System (or the shell) for your program, not by the program for itself.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
2

There is a lot that goes on behind the scenes to handle files, streams, and formatted output. Yes, including <stdio.h> and using routines from it incurs a significant amount of overhead. But the standard streams are part of an entire system of things operating together. The C library provides routines for using the streams, the operating system provides calls for accessing the file system, and command-line shells or other processes provide already-open streams when your process starts, and so on.

For normal processes, you can ignore this. For executing commands (directly typed by you or in shell scripts), it is a minor cost of all the things that happen to make commands work. Additionally, your executable may be linked to the standard library or libraries dynamically rather than statically. This means the library is not built into your executable. It is available when your program runs, but it is shared with other processes in the system, and your program’s connection to it incurs little additional overhead.

If you are working on some special process or other special software, such as a network daemon or a device driver, there are ways to avoid linking in the standard C library or parts of it. However, these are all platform specific, and you would have to provide more information (likely in other Stack Overflow questions) about exactly what you are trying to do and which platforms (hardware, operating system, developer tools) you are using.

Some of the tools for developing special software include stripped-down libraries that provide simple routines without a lot of overhead.

Additionally, C allows declaring things without defining them. The <stdio.h> header declares various buffers and other data structures that it needs, but it is possible that, if you do not use streams or other features that needs these structures, the linker will not import from the library the modules that define them, and so they will not become a part of your executable. The details of this depend on the C implementation.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

I'm a little annoyed about including stdio.h -- wouldn't that bloat the binary with unnecessary file descriptors and other code?

No. Linking the C runtime adds to your code, but without it, you won't be able to do much. This runtime contains startup code which will make sure the standard streams are available. On many systems, a newly started process already gets the respective opened files from the start, possibly inherited by the parent process.

  • I do not think the answer is this clear. Sometimes we want to build streamlined special-purpose processes and not include fancy features like formatted I/O. Just because we use C does not mean we want the entire library built into our executable. The library should be subsetted into various modules so that portions not used by a process are not linked in. – Eric Postpischil Nov 27 '17 at 12:26
  • @EricPostpischil Of course, but look at the question, would you really consider this information *in scope*? –  Nov 27 '17 at 12:28
  • Yes, the OP is specifically asking about whether they must suffer the cost of including features they do not want. – Eric Postpischil Nov 27 '17 at 12:29
  • @EricPostpischil well, my opinion is a clear **no**. The question shows a lack of understanding how the standard file descriptors work on most systems and I only talked about the *C runtime* (not the standard library) for a reason, but explaining this in great detail would clearly be out of scope here. –  Nov 27 '17 at 12:33
1

Actually, these streams are opened even before your executable gets loaded. This is what's typically happening:

  • Some shell parses a command that tells it to execute your program. This command may include redirections of stdin, stdout, and `stderr.

  • The shell calls fork() to create a new process. That new process is still running shell code.

  • The shell code within the new process opens input/output streams as prescribed by the redirections it has parsed. If you said 2>/dev/null, it will open /dev/null as file descriptor 2.

  • The forked shell code calls exec(), to yield control to your code.

  • When your code writes to stdout, it simply writes to the already open file descriptor 2.

You see, the interpretation of the file descriptors 0, 1, and 2 is simply a convention for passing open file descriptors to a process. You can instruct your shell to open more file descriptors 5>myCoolStream, or to close existing file descriptors. If that is, what your process expects, that's fine. If your process expects something else, well, that's your problem.

As such, whether you include <stdio.h> or not has no effect apart from providing the usual function prototypes and type definitions for printf() etc. The streams themselves are not influenced by this at all.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106