Users Online

· Guests Online: 24

· Members Online: 0

· Total Members: 188
· Newest Member: meenachowdary055

Forum Threads

Newest Threads
No Threads created
Hottest Threads
No Threads created

Latest Articles

FAQ: C File I/O Handling

FAQ (Frequently Asked Questions) >C File I/O Handling
01 What is Concept of Portability in Context with C Programs02 What are the Steps of Compilation of a C Program on Linux System03 Explain perror() Function with Example C Program04 What is an exit() Function and What this is Used For in a C Program?
05 What are Differences Between Standard I/O Library and ANSI Library of I/O Functions06 What is Notion of Stream in C Programming?
01 What is Concept of Portability in Context with C Programs
Answer: Linux is a portable operating system which supports wide range of computer architectures. Portability refers to how easily- if at all- code can move from one system architecture to another. We know that Linux is a portable because it has already been ported to various implementations. To view which implementations Linux has ported to, type in the following command

vim /usr/src/kernels/2.6.43.8-1.fc15.x86_64/arch/

and output views as

alpha/ c6x/ hexagon/ m68k/ openrisc/ score/ um/
arm/ cris/ ia64/ microblaze/ parisc/ sh/ unicore32/
avr32/ frv/ Kconfig mips/ powerpc/ sparc/ x86/
blackfin/ h8300/ m32r/ mn10300/ s390/ tile/ xtensa/


Portability of a C program across the wide range of implementations requires to use standard library which has mandated set of functions and they will have the required interface and then work in the the prescribed manner.

ANSI C implementations include library in specification. Further, ANSI implementations aren’t prohibited from adding additional functions in the library. But if you are concerned with writing portable code use standard functions and avoid any non-standard functions.

Top
02 What are the Steps of Compilation of a C Program on Linux System
This C Tutorial explains Steps Involved in Compiling a C Program on Linux System.
Let’s understand GCC Compilation Process which comprises of following steps.

1. Preprocessing
2. Compilation
3. Assembly
4. Linking
5. Program Translation

Before talking of compiling and running C program in Linux let’s see why C is so popular ever since it was created. He was the Dennis Ritchie who developed C language in 1969 to 1973. C was developed from the beginning as the system programming language for UNIX. Most of the UNIX kernel, and all of its supporting tools and libraries, were written in C. Initially, C was designed to implement the UNIX operating system. Even today, C is the first choice for system-level programming. Here I explain compilation and execution of a simple C program in detail.

Let’s take a very simple C Program as an example and compile it in Linux – The Classic Hello World!

/* helloworld.c -- a simple C program */
#include <stdio.h>
int main()
{
printf("Hello World!n");

return 0;
}
To compile and run this C program every part of the system has to perform in concert. In order to compile above C program in Linux, we will start right from the creation of the program. The “Hello World!” program starts its life as a source file which is created with help of a text editor and saved as helloworld.c. The helloworld.c program code is stored in a file as a sequence of bytes. Each byte has a value corresponding to some character. The first byte has the value 35 that corresponds to the character ‘#’, for example. Likewise, the second byte has the integer value 105, which corresponds to the character ‘i’, and so on. The idea illustrates that all information in a system is represented as a bunch of bits.

To compile and run the C program helloworld.c, all C statements must be translated individually into a sequence of instructions that a machine can understand. These instructions are then packaged in a form called executable object program. There are other programs which perform this task to get the program running. On a Linux system, the translation from source code to object code (executable) is performed by a compiler driver. Here we will compile C program by gcc.

The following command compiles C program helloworld.c and creates an executable file called helloworld. Don’t forget to set appropriate permissions to helloworld.c, so that you won’t get execute permission errors.

gcc -o helloworld helloworld.c

While compiling helloworld.c the gcc compiler reads the source file helloworld.c and translates it into an executable helloworld. The compilation is performed in four sequential phases by the compilation system (a collection of four programs – preprocessor, compiler, assembler, and linker).

Now, let’s perform all four steps one by one and understand each independently.

1. Preprocessing

During compilation of a C program the compilation is started off with preprocessing the #directives (e.g., #include and #define). The preprocessor (cpp – c preprocessor) is a separate program in reality, but it is invoked automatically by the compiler. For example, the #include command in line 1 of helloworld.c tells the preprocessor to insert the contents of the system library header file stdio.h directly into the program text at its place. The result is another file typically with .i suffix. In practice, the preprocessed file is not saved to disk unless the -save-temps option is used.

This is the first stage of compilation process where preprocessor directives (macros and header files etc.) are expanded. To perform this step gcc executes the following command internally.

[root@host ~]# cpp helloworld.c > helloworld.i

The result is a file helloworld.i that contains the source code with all macros expanded. If you execute the above command in isolation then the file helloworld.i will be saved to disk and you can see its content using vi or any other editor you have on your Linux box.

2. Compilation

In this phase compilation proper takes place. The compiler (ccl) translates helloworld.i into helloworld.s. File helloworld.s contains assembly code. You can explicitly tell gcc to translate helloworld.i to helloworld.s by executing the following command.

[root@host ~]# gcc -S helloworld.i
The command line option -S tells the compiler to convert the preprocessed code to assembly language without creating an object file. After having created helloworld.s you can see the content of this file. While looking at assembly code you may note that the assembly code contains a call to the external function printf.

3. Assembly

Here, the assembler (as) translates helloworld.s into machine language instructions, and generates an object file helloworld.o. You can invoke the assembler at your own by executing the following command.

[root@host ~]# as helloworld.s -o helloworld.o
The above command will generate helloworld.o as it is specified with -o option. And, the resulting file contains the machine instructions for the classic “Hello World!” program, with an undefined reference to printf.

4. Linking

This is the final stage in compilation of “Hello World!” program. This phase links object files to produce final executable file. An executable file requires many external resources (system functions, C run-time libraries etc.). Regarding to our “Hello World!” program you have noticed that it calls the printf function to print the ‘Hello World!’ message on console. This function is contained in a separate pre compiled object file printf.o, which must somehow be merged with our helloworld.o file. The linker (ld) performs this task for you. Eventually, the resulting file helloworld is produced, which is an executable. This is now ready to be loaded into memory and executed by the system.

The actual link command executed by linker is rather complicated. But still, if you passionate enough you can execute the following command to produce the executable file helloworld by yourself.

[root@host ~]# ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib64/crt1.o /usr/lib64/crti.o /usr/lib64/crtn.o helloworld.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o -L /usr/lib/gcc/x86_64-redhat-linux/4.1.2/ -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o -o helloworld
And, you can then run final executable file helloword as follows:

[root@host ~]# ./helloworld


Output:

hello, world!
I executed the above command on an x86_64 system having gcc 4.6.0. It might be that above command does not work on your system as it is. It all matters that where the libraries located?

For you, there is no need to type the complex ld command directly – the entire linking process is handled transparently by gcc when invoked, as follows.

[root@host ~]# gcc helloworld.c -o helloworld
During the whole compilation process there are other files also in role along with the source code file. If you see the very first statement of helloworld.c it is #include (includes header file). Likewise, while compiling a C program you have to work with following types of files.

5. Program Translation

Source code files: These files contain high level program code which can be read and understood by programmers. Such files carry .c extension by convention.

Header files: These types of files contain function declarations (also known as function prototypes) and various preprocessor statements. They are used to allow source code files to access externally-defined functions. As a convention header files have .h extension.

Object files: These files are produced as an intermediate output by the gcc compiler during program compilation. They consist of function definitions in binary form, but they are not executable by themselves. Object files end with .o extension by convention.

Binary executables: These are produced as the output of a program called a linker. During the process of compiling and running C program the linker links together a number of object files to produce a binary file which can be directly executed. Binary executables have no special suffix on LINUX like operating systems.

Along with above four types of files, while compiling a C program you can come across with .a and .so, static and shared libraries respectively, but you would not normally deal with them directly.

Top
03 Explain perror() Function with Example C Program
Answer: Generally, we write programs to perform specific operations, for ex. opening a file for reading. Such operations are accomplished by using standard library functions. These functions in turn request O.S. to perform the requested operation. Then there’s chance that O.S. might fail for ex. attempting to open a requested file for reading that doesn’t exit. In such situations, O.S. rather to show up an error massage indicates something went wrong. This error information library
functions then pass to user program after saving an error code in the external integer variable errno (defined in errno.h) to indicate exactly why the operation failed.

perror() simplifies reporting of such specific errors to user. It’s prototype is as follows

void perror(char const *message);

Note that if message isn’t NULL and points to a non-empty string, then message is printed and is followed by a colon and a space. Message explaining the error code currently in errno is then printed. For ex., we see a simple C program below

/* perror.c -- simplifies error reporting in unifying way */
#include <stdio.h>
#include <error.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
FILE *fp;

if (argc == 1) {
puts("File Argument Missing");
exit(EXIT_FAILURE);
}

fp = fopen(argv[1], "r");

if (fp == NULL) {
perror("File Open");
exit(EXIT_FAILURE);
}

return 0;
}
Output is as follows

[root@localhost ch15_Input_Output_Functions]# ./a.out
File Argument Missing
[root@localhost ch15_Input_Output_Functions]# ./a.out bye.text
File Open: No such file or directory

Top
04 What is an exit() Function and What this is Used For in a C Program?
Answer: exit() is useful for terminating a program upon having discovered some error which prevents the program from continuing to execute normally. It’s prototype as follows,

void exit(int status);
status argument passed to exit() is returned to O.S. to inform that whether or not program succeeded normally. Notice that this status argument is as same as status main() passes to O.S. Pre defined symbols EXIT_SUCCESS and EXIT_FAILURE are used to pass successful and failure status to O.S respectively. exit() function is prototyped in stdlib.h header file. A fragment of code from stdlib.h defining EXIT_SUCCESS and EXIT_FAILURE is copied below for ref.

/* We define these the same for all machines.
Changes from this to the outside world should be done in `_exit'. */
#define EXIT_FAILURE 1 /* Failing exit status. */
#define EXIT_SUCCESS 0 /* Successful exit status. */

Notice that other values could also be used to pass to exit() but their meaning is implementation dependent. It’s a good programming practice to follow calls to perror() with a call to exit().

Let’s take a simple C prog. to understand the behaviour of exit(),

/* exit.c -- program unravels exit() behaviour */
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
FILE *fp;

/* test if file to be opened is given on command line */
if (argc == 2) {
fp = fopen(argv[1], "r");

if (fp == NULL) {
perror("File Open");
exit(EXIT_FAILURE);
}
else {
printf("File: "%s" opened successfully!n", argv[1]);
}
}
else {
/* if the file argument is not given or more than */
/* 2 arguments are given */
puts("Error File Argument");
exit(EXIT_FAILURE);
}

return 0;
}

Let’s turn ours’ attention to the output below
[root@localhost ch15_Input_Output_Functions]# ./a.out preeti.txt
File: "preeti.txt" opened successfully!
[root@localhost ch15_Input_Output_Functions]# ./a.out
Error File Argument
[root@localhost ch15_Input_Output_Functions]# ./a.out preeti
File Open: No such file or directory

Notice that this function never returns. When exit() function is completed, the program has disappeared and there’s nothing to return to.


Top
05 What are Differences Between Standard I/O Library and ANSI Library of I/O Functions
Answer: Actually, concept of library of functions came into existence with K and R C implementation. Though K & R C had very little in the way of library of functions to support input and output. Programmers who wanted sophisticated I/O functionality than that provided had to implement their own.

This situation was greatly improved in Standard I/O library, a collection of functions which implemented much of added functionality which the programmer had been implementing on their own. This library expanded on the existing functions, for ex. printf(), creating new functions which could be used in a variety of situations. Further, standard library introduced the concept of Buffered I/O which enhances the efficiency of most programs.

But standard library of I/O functions had two significant drawbacks, first, it’s implemented on one particular machine without giving much thought to other machines with different architectural features. And second was directly related to first. As soon as first drawback discovered, programmers’ attempted to fix that by modifying the library functions. And as soon as they did so, though, functions were no longer standard thereby reducing portability.

ANSI C implementations evolved through long diligence over the existing standard I/O library. Functions in ANSI C implementations were direct descendants from standard I/O library. Portability and completeness were key considerations in the design of ANSI library. Most of the differences between ANSI implementation and their counterparts are the additions that enhance the portability or functionality. However, a continuous diligence might improve them further but they will not modifying already existing functions. Instead, they will be implemented as separate functions in the library. Thus portability doesn’t suffer.
Top
06 What is Notion of Stream in C Programming?
Answer: We perform writing to and reading from some of the common devices, viz. floppy drives, CD-ROM drives, hard drives, network communication etc. Each of these devices have different characteristics and operating protocols. It’s the operating system that takes care of details of communication with these devices and provides with simpler and more uniform I/O interface to the programmer.

ANSI C abstracts all I/O as stream of bytes moving into and out of a C program. This stream of bytes is called STREAM. A C program is concerned only with creating the correct stream of bytes and interpreting the stream of bytes coming into the program as input. Mostly all I/O streams are fully buffered. This means that reading and writing data is actually copying data out of and into an area of memory called buffer. Because reading from and writing to memory is fast, this enhances the performance. Buffer for output stream is flushed, meaning physically written to device or file when it’s full. Writing full buffer is more efficient than writing data in small bits or pieces as program produces them. Similarly, input buffers are re-filled when they become empty by reading next large chunk of input from the device or file into the buffer.

The simple best strategy to debugging a C program that aborts abnormally is to sprinkle printf() statements throughout the program to identify the specific area where error occurred. Because of fully buffered I/O, the outputs of the calls to printf() are buffered and not showed up to the user when program aborts. This causes confusion to the user. In order to show up the outputs immediately before program aborts follow calls to fflush() with call to printf(). For ex.

/* fflush.c -- program explores fflush() */
#include <stdio.h>
#include <stdlib.h>
#define DEBUG fprintf(fp, "values: a = %d and b = %dn", a, b)
#define BSIZE 50
#define SUM(a,b) ((a) + (b))

int main(void)
{

int a = 10, b = 20;
int *pa = &a, *pb = &b;
int *pi = (int *)1000;

FILE *fp;

fp = fopen("fflush_out.txt", "w+");

if (fp == NULL) {
perror("File Open");
exit(EXIT_FAILURE);
}

/* Program calls fflush() with DEBUG statements */
/* fflush() forces the output buffer to be physically written */
/* whether or not it's full */

DEBUG;
fflush(fp);

fprintf(fp, "Sum of %d and %d is %dn", a, b, SUM(a,b));
fflush(fp);

/* fushing the stdin is undefined */
fflush(stdin);

/* NULL to fflush() flushes all the output buffers */
fflush(NULL);

fprintf(fp, "What value pointer *pi is pointing at, %dn", *pi);
fflush(fp);

printf("Add. of a and b is %p and %pn", pa, pb);
DEBUG;

return 0;
}

Output on the screen as

Segmentation fault (core dumped)
while output directed to file “fflush_out.txt” contains

values: a = 10 and b = 20
Sum of 10 and 20 is 30

Notice that above program aborts because of seg fault. But calls to fflush() with DEBUG statements causes the buffer to be physically written to the file identified by “fp” wherein DEBUG statements might help you trace the cause of abnormal program termination.
Top
Render time: 0.75 seconds
10,798,893 unique visits