December 30th, 2012, 03:54 AM
Why include header files?
My question might not make sense so forgive me if it is a stupid question.
We include the stdio header file for using functions like printf, scanf, putc etc.
Why do we need to include header files to give output and take input?
For eg: - To use if conditions, for loops, while loops, addition, subtraction etc does not require any header files.
So why need header files for printing and scanning?
December 30th, 2012, 11:27 AM
One reason that output and input are not built into the language directly is because they are implemented differently on different operating systems. A printf or scanf function call generally compiles into a syscall, but different operating systems can have different syscall implementations. When you install dev libraries like stdio on your computer, you install a version of that library specifically built for your operating system's syscall API.
You don't need an include for if/for/while/etc. because they are built into the language itself. The compiler is programmed to know how to handle them. These statements generally compile into the same code on common hardware architectures, even if the operating system is different.
December 30th, 2012, 11:52 AM
Oh great ... you solved a great doubt of mine. Thank you!
December 30th, 2012, 07:54 PM
And now for the facts of life.
In school, your programming assignments result in short programs that will almost always consist of one single source file. In the real world, software projects consist of many source files; one of ours has over 100 source files and it's not one of the larger ones. The code in these source files will use the same type definition, structure declarations, and #define macros and will call functions and use global variables that are defined in other source files. Furthermore, when a project is built, each source file is compiled separately and independently of all the others, yet each source file must know about all those things I just listed. How? By placing that information inside of header files and then including those header files that are needed into the source files that need them.
That is why you need to include header files. What is what header files are for.
Specifically to your question, when functions are in library files (code that's been compiled already), then your source files need to know about those functions just as it needs to know about functions you've written yourself in other source files. That information is in header files that come with the library and you need to include those header files so that the compiler will know about those library functions that you call.
C is a lean little language that has limited functionality. And it is expandable through the use of libraries. C does not know how to do I/O or work with strings or with time, so the C Standard Library was written with functions that can do those things -- the reasons for separating I/O in a library still apply. And libraries always have their associated header files that you need to include in order to call those library functions. When network programming was created, those functions were placed in a library and the information about those functions into a header file that you need to include in order to do network programming. A multitude of software vendors had developed libraries to perform all kind of tasks, including sound, graphics, and video, all of which come with the header files you need to include in order to use those libraries.
Those are the facts of life.
Comments on this post
December 31st, 2012, 01:13 PM
That's great when we can answer quickly, and good question.
Originally Posted by Avichal
January 3rd, 2013, 08:53 AM
I think I still didnt understand one thing
You say for printf and scanf the program calls the os. But then how the os prints and scans? Afterall os is also made from a programming language like c
January 3rd, 2013, 03:03 PM
I recommend that you read an operating systems book, such as O'Reilly's Understanding the Linux Kernel by Bovet and Cesati.
Originally Posted by Avichal
Ultimately, I/O (ie, "input/output processing"; eg, using scanf and printf) involves computer hardware and communication with hardware devices (eg, keyboards, graphics cards, monitors). Any self-respecting operating system will keep users from working directly with the hardware; infamously, MS-DOS allowed you to access any and all memory locations and access to the hardware, which is why system crashes were so common. What modern OSes such as UNIX, Linux, and Windows do is to use protected-mode operations. Only the OS can run in protected-mode (AKA "kernel mode"), while user processes are not allowed to and hence run in "user mode" (or whatever that particular OS calls it). Hence, what a particular process is allowed to do and to access depends on what priveleges it's given, with user processes being given the least.
On the practical side, there's a lot of processing that needs to be done in performing I/O and that processing must be done correctly. It is unreasonable to expect every programmer in every program to write all that code every time they want to perform I/O. Even if that were the case, every thinking programmer would write functions that contain the actual I/O code and then call those functions from within the program. That is actually what has been done with the Standard C Library: I/O processing functions are provided in the library, so user programs can link to and call those functions.
Another practical problem with I/O is that not all I/O and peripheral devices are the same, so somebody has to take those differences into account and to work with them. Not only is it unreasonable to make the user programs responsible for that task, but it also would destroy an important aspect of C programs: they need to be portable. Once you have written a C program on one computer, you should be able to copy that source code into a different computer, compile it, and have it run the same on that other computer. If you include code that is specific to one computer, then you have made that program non-portable, which is not a good thing. However, if you write the program so that the non-portable aspects are isolated from the rest of the code (eg, by placing that non-portable code inside of wrapper functions), then you will have simplified the task of porting that program to other systems.
The way that an OS such as UNIX does this is through the use of device drivers. A device driver is a special program that deals with the device in question, hardware and all. When the OS needs to access a hardware device, it calls the appropriate routines in that device's driver and then it's the driver that handles the gory details. This way, the OS code can be written in a higher-level manner and would not need to change as it's ported from one computer platform to another, since all the changes that would be needed in that porting would be in the device drivers. UNIX was written in C and could be ported simply by recompiling it on the new platform; the only rework that was needed was in the device drivers for that new hardware platform -- writing device drivers traditionally require assembly programming, but more and more of it can is done in embedded C. That is what allowed use and acceptance of UNIX to spread so quickly. Traditionally, every operating system for a new computer had to completely rewritten, usually in assembly language.
So here's the basic sequence for outputting (eg, with printf):
The user program calls printf in the library. printf uses the format string and arguments to generate a character string, which it passes to the OS through a system call. The OS finds the device driver that's needed and calls the appropriate routines in the driver to output that string. The device driver then works with the hardware -- eg, by loading the device's control and data registers in the appropriate sequence (eg, it may need to loop, writing one character at a time to the device).
In the case of input via the keyboard, the keyboard device would, upon receiving a keystroke from the user, send an interrupt to the processor which would call the appropriate interrupt service routine (ISR) to handle that event. The ISR would receive the input character from the device and append it to the keyboard buffer that the OS is maintaining for the appropriate user process (eg, the one that currently has focus) -- obviously, there can be multiple keyboard buffers, so the OS must decide which one to append the character to; this is beyond the scope of this message. Then when the user program calls scanf, that library routine reads characters from the keyboard buffer one by one as it performs the conversions. Of course, if there aren't enough characters yet, it will wait until there is. Once it has performed all the conversions asked for or encounters an error, it returns.
Of course, the actual details may vary. To learn the entire story, complete your computer science degree and then do graduate study. Or earn your degree so that you can then get a job doing that kind of programming.
January 3rd, 2013, 04:01 PM
Complex functions are made from simpler functions and ultimately all processing is simply moving data between memory or I/O and processor registers and performing arithmetic and logical operations on that data. All executing code is ultimately machine-instruction level code whether that code was generated by a compiler or by hand, and all but a very few operations can be implemented by C compiler generated code.
Originally Posted by Avichal
For example, printf() might be implemented using putchar(), which in turn might call an OS call to display a character glyph in the window assigned to the process. printf() itself knows nothing about windowing, or fonts, or pixels; the OS deals with that. On the other hand on a simple embedded system, perhaps with no OS at all, putchar() may ultimately write a byte to a UART transmit register that outputs data on a serial port which might be connected to a dumb-terminal or more commonly these days a terminal emulator running on a PC. In both cases the abstraction allows printf() to work without any knowledge of the execution environment.
Last edited by clifford; January 3rd, 2013 at 04:09 PM.
January 11th, 2013, 03:16 PM
Why do you wonder whether it's correct or not? What does your compiler tell you? If your compiler says it's not, what exactly does it tell you? And what compiler are you using?
January 12th, 2013, 09:51 AM
OS calls the device, right? Can we directly call the device for printing and scanning from a c program?...just asking
January 13th, 2013, 03:56 AM
That depends. In a simple OS like MS-DOS, yes you can. In a complex modern OS such Windows or Linux, the OS provides kernel level device drivers and uses MMU protection mechanisms to block user level access to I/O. This is necessary in a multi-tasking general purpose OS to allow all independent programs running to play nicely together.
Originally Posted by Avichal
If you are coding for an embedded system with either no OS or a simple RTOS kernel, then you generally have no choice other that to write directly to the device - or at least you will write your own device driver which you might hook into stdio calls or access directly. Typically in this case stdio is via a UART serial port to which you'd connect a terminal (most often a PC with terminal emulation software these days).
January 13th, 2013, 05:07 AM
Depends on if the OS will let you or not. Some OSs like MSDOS will let you do that, but not Windows or Linux.
Originally Posted by Avichal
If you are interested, a few years ago I showed how to implement I/O functions in C++ by implementing mycout to do a fair amount of what cout does (and C's printf() function can also be implemented similarly).
Note that most of that code is written in portable code with only one small non-portable part that prints a single character.
This is why it is easy to port C or C++ compilers to a new platform because the compiler is relatively small and a large part of the standard library is implemented in the language itself.
Up the Irons
What Would Jimi Do? Smash amps. Burn guitar. Take the groupies home.
"Death Before Dishonour, my Friends!!" - Bruce D ickinson, Iron Maiden Aug 20, 2005 @ OzzFest
Down with Sharon Osbourne
"I wouldn't hire a butcher to fix my car. I also wouldn't hire a marketing firm to build my website." - Nilpo
January 13th, 2013, 05:39 AM
Example. This implementation by default uses putchar() but can be changed to use a user defined function outbyte().
Originally Posted by Scorpions4ever
Similarly this example that requires only a user supplied putc() function.
In both cases these user supplied functions may invoke a low-level OS call or talk directly to the hardware or device driver. Or even (perhaps somewhat perversely) map to an existing stdio call.
Last edited by clifford; January 13th, 2013 at 05:45 AM.