Mangling in C++

In standard ANSI C, if we wish to have two functions with the same name but different number of parameters or type of parameters (called overloading), we can’t. It is not allowed.

In the other hand, in C++, we can overload functions. Overloading a function means that we can have two or more functions sharing the same function name but with different type or different number of parameters:

int print_something(void){
...
}

int print_something(char a){
...
}

int print_something(int a){
...
}

int print_something(char a, int b){
...
}

Even do all these functions share the same name, they are different; so, how does your compiler keep track of which is which? By using a method called mangling algorithm, unique names are generated as identifiers for each of these functions.

First, lets say that we have a file with some functions such as:

#include <stdio.h>

int foo(double d_number);

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

	double d_value = 4.5f;

	printf('For value %f, we get the number %d\n', d_value, foo(d_value));

  return 0;
}

int foo(double d_number){
  return (int) d_number;
}

This would show the follow:

For value 4.500000, we get the number 4

If we compile the file with gcc for example: gcc -c main.c. This will generate a file name main.o
Then we can analyse this file and see how the table is created in C for this particular function.

Type nm file.o and you will obtain something like this:

0000003f T foo
00000000 T main
 U printf

Notice the table only indicate the name of foo but doesn’t indicate any parameters types

What would append if we compile the same program using g++ instead?
A table would be build by the C++ compiler (in this case we are talking about g++) that would look like the follows:

0000003e T _Z3food
 U __gxx_personality_v0
00000000 T main
 U printf

Look the information _Z3food, the last character ‘d’ indicate that the parameter is a double

Lets say we modified main.c to main.cpp and we create another function with the same name but different parameters:

#include <stdio.h>

int foo(double d_number);
int foo(char d_character, int i_number);

int main(int argc, char* argv[]){
  double d_value = 4.5f;
  printf('For value %f, we get the number %d\n', d_value, foo(d_value));
  return 0;
}

int foo(double d_number){
  return (int) d_number;
}

int foo(char d_character, int i_number){
  return (int) d_character + i_number;
}

If we print the table using nm main.o we obtain:

0000006e T _Z3fooci
0000003e T _Z3food
 U __gxx_personality_v0
00000000 T main
 U printf

If you notice,  _Z3fooci has the last two character a ‘c’ for char and an ‘i’ for integer while _Z3food has the last character ‘d’ for double.

If we try to compile this code having both functions with the same name using gcc: gcc -g main.cpp, we would obtain an error in compilation:

/tmp/ccU0T0Co.oFrown.eh_frame+0x12): undefined reference to `__gxx_personality_v0'
collect2: ld returned 1 exit status

Using extern “C”, we can tell g++ which part of the code we wish to compile as regular C.
Let say we have to following code:

#include <stdio.h>

int foo(double d_number);

extern 'C'{
  int foo(char d_character, int i_number);
}

int main(int argc, char* argv[]){
  double d_value = 4.5f;
  printf('For value %f, we get the number %d\n', d_value, foo(d_value));
  return 0;
}

int foo(double d_number){
  return (int) d_number;
}

extern 'C'{
  int foo(char d_character, int i_number){
    return (int) d_character + i_number;
  }
}

This would compile with gcc -g main.cpp without problems. If we execute g++ -c main.cpp to create the object main.o and later executed nm main.o we obtain:

0000003e T _Z3food
 U __gxx_personality_v0
0000006e T foo
00000000 T main
 U printf

Notice that _Z3food was compiled as C++ while  T foo indicate that that function was compiled as standard C.

Share

Function Pointers in C/C++

In the previous post, we cover how to work pointers with arrays. In this post we will see how to use function pointers, and how useful they can be:

I am assuming that you read the previous postings about pointers and pointers with arrays.

As we may recall, when we declare a pointer, the pointer will store an address of a position in memory.
Normally, we wish to create pointers of the same kind as the object we want to point at. For example, if the variable is an integer then we want the pointer to be an integer:

int i_variable = 22;
int* pi_variable = &i_variable;

If you create a different variable of the same kind, you could change where the pointer is pointing at:

int i_variable = 22;
int i_variable_2 = 44;
int* pi_variable = &i_variable;
printf('Value pointed at: %d \n', *pi_variable);     /* This line print 22 */
pi_variable = &i_variable_2;                         /* pi_variable points at i_variable_2 */
printf('New Value pointed at: %d \n', *pi_variable); /* This line prints 44 */

Now the question comes, can we use this with functions? Yes, we can!

Here is an example of how this works:

/* This return the addition of value_a with value_b */
int add(int value_a, int value_b){
	return value_a + value_b;
}

/* This function return the subtraction of value_b from value_a */
int sub(int value_a, int value_b){
  return value_a - value_b;
}

int main(int argc, char* argv[]){
  int val_a = 4;   
  int val_b = 5;

  /* Function pointer  must have the same return type and parameter type */
  int (*p_function)(int, int);

  p_function = add;
  printf('ADD A: %d with B:%d to obtain %d \n', 
         val_a, 
         val_b, 
         (*p_function)(val_a, val_b));

  p_function = sub; 
  printf('SUBTRACT B: %d OF A:%d to obtain %d \n', 
         val_b, 
         val_a, 
         (*p_function)(val_a, val_b));

return 0;
}

This will print:

ADD A: 4 with B:5 to obtain 9
SUBTRACT B: 5 OF A:4 to obtain -1

As you can see this can be a very powerful feature. The function pointer will point to the address of any function we want to point at while the function have the same return type (int in this case), the same number of parameters (in this case, we have two parameters), and the same type of parameters (both parameters are int).

Share

Pointers with Arrays in C/C++

In the previous post, “Pointers in C/C++”, we talked about pointers in C/C++. We recommend to read this posting before continuing.

Lets say we declare an array of 5 elements:

int i_array[5];

Also, we could declare and instantiate each elements in the array at the same time:

int i_array[5] = {10, 20, 30, 40, 50};

Now, lets assume we created the array and we want to change the values of each element, one way could be:

i_array[0] = 15;
i_array[1] = 25;
i_array[2] = 35;
i_array[3] = 45;
i_array[4] = 55;

However, there is another way to change the values of elements in an array, and that way is by using pointers.

To begin with, first we need to understand how are the array build, each element in the array have an address in memory, the name that we give to the array is connected to the address of the first element. Each consecutive elements will have an address that is the previous address plus the size of the address. For example, if you have an array of characters, each character have a size of 8 bits (1 byte), this means that each element’s address will be different by 1 byte.

For example, this  code would print the address of each element in an array:

int main(int argc, char* argv[]){
  int c_array[5] = {'a', 'b', 'c', 'd', 'e'};
  int index;
  for (index = 0; index < 5; ++index){
    printf('c_array[%d] with value %d has address 0x%X \n',
     	     index, c_array[index], (unsigned int) &c_array[index]);
  }
  return 0;
}

This will show us:

c_array[0] with value a has address 0xBF95EAB7
c_array[1] with value b has address 0xBF95EAB8
c_array[2] with value c has address 0xBF95EAB9
c_array[3] with value d has address 0xBF95EABA
c_array[4] with value e has address 0xBF95EABB

As you can see they are separated by one byte:

0xBF95EAB7 - 0xBF95EAB8 = 1

The graphic representation of this array is:

When working with pointers, we can access to the information indirectly by just using the address of each element in the array. The following code will make a pointer to point at the first element of the array:

char* p_c_array = c_array; /* Not need &. c_array return an address. c_array[0] return a value */

The reason we are not using & when assigning the address is that arrays are accessed by reference, which means that the compiler will return the address of the first element if we write c_array while if we write c_array[0] it will return the value in that position.

If we want to print the first element:

printf('First Element: %c', *p_c_array);

If we want to print the second element, we need to increase the pointer so it will point to the next element.
By increasing it means that we must increase a total amount of one byte (because we are talking about char variables) to access to the next element:

0xBF95EAB7 + 1 byte =  0xBF95EAB8 

There are two ways to increase value in the pointer by one byte:
One ways is:

p_c_array = p_c_array + sizeof(char); /* Size of char return 1 byte */

Or by letting the compiler to increase the value:

p_c_array++;

Just take in consideration that you don’t wish to loose the original address to which you are pointing to the first element of the array; therefore it common practice to create a second pointer that will have the same address and increase that pointer, for example:

char* p_c_array = c_array;  /* Point to first element of array */
char* p_c_array_2;
p_c_array2 = p_c_array;      /* Copy address stored in p_c_array */
p_c_array2++;                /* Increase address stored by one byte to point to next element */
printf('First element: %c. Second Element %c \n',
       *p_c_array,
       *p_c_array2);

The follow example would print all the elements of the array:

int main(int argc, char* argv[]){
  char c_array[5] = {'a', 'b', 'c', 'd', 'e'};
  char* p_c_array = c_array;
  char* p_c_array_2;
  for (p_c_array_2 = p_c_array;
       *p_c_array_2 != '\0';
       ++p_c_array_2){
    printf('p_c_array_2 points to variable with value %c \n',
           *p_c_array_2);
  }
  return 0;
}

Notice that we are using in this case ‘\0’ to indicate the end of the array. This do not apply if we would be using an array of integers for example.

This code will print to screen:

p_c_array_2 points to variable with value a
p_c_array_2 points to variable with value b
p_c_array_2 points to variable with value c
p_c_array_2 points to variable with value d
p_c_array_2 points to variable with value e

We this we had cover the basic about pointers and arrays.

Next post, we are going to talk about pointer functions

Share

Pointers in C/C++

When programming in C and/or C++, you will encounter the use of pointers.
We are going to begin first doing a review of regular pointers and then we will see how to create and use function pointers.

In C/C++, when we create a static or dynamic variable, this variable will have an address in memory. For example:

If we write the following code:

int integer_variable;

An space in memory of the integer size (16-bits, 32-bits, or 64-bits depending the system) will be reserved.

When we assign a value to this variable, this value is store in the memory as a binary number that is represented base on the variable’s type.

int integer_variable = 4;
integer_variable = 5; /* Change value */

If we wish to know in which address is this value store we use ‘&’ in front of the variable.

int main(int argc, char* argv[]){
  int i_variable = 22;
  printf('i_variable has value %d at address 0x%X\n', i_variable, (unsigned int) &i_variable);
  return 0;
}

First note that we are using a downcasting “(unsigned int)” since there are no negative values in memory address else you will receive a warning from the gcc compiler. Second, in this example we are using %X to indicate printf to represent the address as an hexadecimal value.

This will give us:

i_variable has value 22 at address 0xBF9EFC28

If this would be a house for example, i_variable would be the name of the resident while the memory address would be the address of the house.

When creating a pointer in C/C++, we are declaring a variable (which have its own address) that will hold an address.

int* p_i_variable;

And we instantiate this pointer variable with the address of another variable, in this example would be i_variable.

int* p_i_variable = &i_variable;

or you could instantiate in this way:

int* p_i_variable;
p_i_variable = &i_variable;

So, we could say that the pointer p_i_variable is like a P. O. Box which many home-owners use when having companies. The P. O. Box have an address but the content of it is pointed to the real address of the owner.

If we want to ask where the p_i_variable (P.O. Box) is “pointing at” we can do:

int main(int argc, char* argv[]){
  int i_variable = 22;
  int* p_i_variable = &i_variable;
  printf('i_variable with value %d has address 0x%X \n', i_variable, (unsigned int) &i_variable);
  printf('p_i_variable (P.O Box with address 0x%X) is pointing to address 0x%X\n',
         (unsigned int) &p_i_variable,
         (unsigned int) p_i_variable);
}

You may notice that the first parameter is (unsigned int) &p_i_variable, this is to display the address of the pointer variable, while (unsigned int) p_i_variable is to display the value store in that pointer variable which would be the address of i_variable. Here is an example of the output:

i_variable with value 22 has address 0xBF877448
p_i_variable (P.O Box with address 0xBF877444) is pointing to address 0xBF877448

Lets say that want to change the value stored in i_variable. You could changed the value directly by using the variable name:

i_variable = 50;

Or you could do it indirectly by using the pointer:

*p_i_variable = 50;

First notice that we are using the asterisk ‘*’ before the name of the pointer. This tells the compiler the following: To the variable that have the address stored in p_i_variable, we wish to change the value to 50;

For example:

int main(int argc, char* argv[]){
  int i_variable = 22;
  int* p_i_variable = &i_variable;
  printf('i_variable with value %d has address 0x%X \n', i_variable, (unsigned int) &i_variable);
  printf('p_i_variable (P.O Box with address 0x%X) is pointing to address 0x%X\n',
         (unsigned int) &p_i_variable,
         (unsigned int) p_i_variable);

  /* Change value of i_variable directly */
  i_variable = 50;
  printf('i_variable with value %d has address 0x%X \n', i_variable, (unsigned int) &i_variable);

  /* Change value of i_variable indirectly using the pointer p_i_variable */
  *p_i_variable = 100;
  printf('p_i_variable (P.O Box with address 0x%X) is pointing to address 0x%X\n',
         (unsigned int) &p_i_variable,
         (unsigned int) p_i_variable);
  printf('i_variable with value %d has address 0x%X \n', i_variable, (unsigned int) &i_variable);
  printf('Obtain value of i_variable using pointer p_i_variable: %d\n', *p_i_variable);

  return 0;
}

This would show:

i_variable with value 22 has address 0xBF982BC8
p_i_variable (P.O Box with address 0xBF982BC4) is pointing to address 0xBF982BC8
i_variable with value 50 has address 0xBF982BC8
p_i_variable (P.O Box with address 0xBF982BC4) is pointing to address 0xBF982BC8
i_variable with value 100 has address 0xBF982BC8
Obtain value of i_variable using pointer p_i_variable: 100

One of the things you must have in consideration is the case of dangling pointers. A dangling pointer is a pointer that points to an invalid direction in the memory.

If we create a pointer variable for example:

/* This is a dangling pointer */
int *pointer_integer;

int integer;

/* Now pointer_integer is not a dangling pointer anymore */
pointer_integer = &integer;

and right away we try to read from it, the value inside the variable can be any value, it could be pointing to any direction in memory. If we try to write anything to that direction we could create a segmentation fault.

So if we are not going to use this variable right away, it is a good idea to instantiated with NULL (which have a value of 0):

/* this is not a dangling pointer */
int *pointer_integer = NULL;

The next post, we are going to talk about pointers with arrays, and double-pointers.

Share

Example of using Fork and pipe in Linux

The following program will create a chain of parents/child in which the last child will receive a message form the top parent through a pipe.

NOTIFICATION: These examples are provided for educational purposes. Using this code is under your own responsibility and risk. The code is given ‘as is’. I do not take responsibilities of how they are used.

The first example is without using recursion while the second example is the same thing but using example. In this way, you can see how fork and pipe behaves.

for_pipe_without_using_recursion.c:

/**
 * @author: Alejandro G. Carlstein R. M.
 * @description: This program will take an integer argument.
 *               It will create a chain of parent-child equal to the value
 *               provided by the integer argument, Example:
 *				 Top parent - forks ==> 
 * 				 child1 - forks ==> 
 *				 child1's child - forks ... ==> Nth generation child, where
 *				 N is the integer argument.
 *			     Then the top parent will then pass a message to 
 *               the child at the lowest level (Nth generation child) 
 *               through a pipe. That child will print 
 *               the received message to output stdout.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MAX_N 3
#define IDX_ARG_N 1
#define MAX_BUFF 30

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab);

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab);

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

	pid_t rtn_pid, pid[3];

	int rtn_error = 0;

	int i, j, status;

	int pfds[2];

	int i_N = (argc > 2) ? atoi(argv[IDX_ARG_N]): 0;

	char buf[MAX_BUFF];

	printf('\n');

	/* create a pipe */
    if (pipe(pfds) == -1) {
            perror('pipe');
            exit(1);
    }else{

		/* fork first child */
    	if ( (pid[0] = fork()) < 0) {
			perror('fork pid[0]');
			exit(1);
    	}// end if

    	/* Parent of first fork */
    	if (pid[0] > 0){

    		printf('Parent of first fork sending message: Hello! \n');    	

    		rtn_error = write_pipe(pfds, 'Hello! \n', MAX_BUFF, 0);	 

    		//close(pfds[0]);
    		//close(pfds[1]);

    	}//end if

    	/* Child of first fork */
    	if (pid[0] == 0){

    		printf('>>Child of first fork \n');

			/* fork second child */
	    	if ( (pid[1] = fork()) < 0) {
				perror('fork pid[1]');
				exit(1);
	    	}// end if

	    	/* Parent of second fork */
	    	if (pid[1] > 0){

	    		//printf('Parent of second fork...\n');    	

			}//end if

	    	/* Child of second fork */
	    	if (pid[1] == 0){

		    	printf('>>>>Child of second fork \n');

				/* fork third child */
		    	if ( (pid[2] = fork()) < 0) {
					perror('fork pid[2]');
					exit(1);
		    	}// end if

		    	/* Parent of third fork */
		    	if (pid[2] > 0){

		    		//printf('Parent of third fork...\n');    	

				}//end if		    	

		    	/* Child of third fork */
		    	if (pid[2] == 0){

			    	printf('>>>>>Child of third fork \n');

					rtn_error = read_pipe(pfds, buf, MAX_BUFF, 5);

					if (rtn_error == 0){
						printf('>>>>>CHILD OF THIRD FORK READ MESSAGE: %s \n',  buf);
					}//end if

		    	}//end if

	    	}//end if

    	}//end if

		wait();

	}// end if

	return rtn_error;
}

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab){

		int rtn_error = 0;

		/* close the read end */
		close(pfds[0]);

		//printf ('%*sWRITE_PIPE: %s\n', i_tab, ' ', msg);

		/* write message to parent  through the write end */
        if(write(pfds[1], msg, max_buff) <= 0) {

			printf('%*s[X] ERROR: Cannot write\n', i_tab, ' ');

			rtn_error = 1;

		}//end if

	return rtn_error;
}

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab){

		int rtn_error = 0;

		/* close the write end */
		close(pfds[1]);

		//printf ('%*sREAD_PIPE: ', i_tab, ' ');

		/* read message from child through the read end */
		// If not data in the pipe, the read will block
	    if( read(pfds[0], msg, max_buff) <= 0 ) {

			printf ( '%*s[X] ERROR: Cannot read\n', i_tab, ' ');

			rtn_error = 1;

		}else{

			//printf('%*s%s \n', i_tab, ' ', msg);		

		}//end if

	return rtn_error;
}

fork_pipe_using_recursion.c:

/**
 * @author: Alejandro G. Carlstein R. M.
 * @description: This program will take an integer argument.
 *               It will create a chain of parent-child equal to the value
 *               provided by the integer argument, Example:
 *				 Top parent - forks ==>
 * 				 child1 - forks ==>
 *				 child1's child - forks ... ==> Nth generation child, where
 *				 N is the integer argument.
 *			     Then the top parent will then pass a message to
 *               the child at the lowest level (Nth generation child)
 *               through a pipe. That child will print
 *               the received message to output stdout.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MAX_N 3
#define IDX_ARG_N 1
#define MAX_BUFF 30
#define DBG_LV1 0
#define DBG_LV2 1
#define MSG 'Hello! \n'

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab);

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab);

int recursive_f(int *pfds, int i_count, int i_max_count);

int exec_order;

int main(int argc, char *argv[])
{
	pid_t rtn_pid, pid[3];

	int rtn_error = 0;

	int i, status;

	int pfds[2];

	int i_N = (argc > 2) ? atoi(argv[IDX_ARG_N]): 0;

	exec_order = 0;

	printf('\n');

	/* create a pipe */
    if (pipe(pfds) == -1) {
            perror('pipe');
            exit(1);
    }else{

    	rtn_error = recursive_f(pfds, MAX_N, MAX_N);

	}// end if

	close(pfds[0]);
	close(pfds[1]);

	for (i = 0; i < MAX_N - 1; i++){
		wait();
	}

	return rtn_error;
}

int recursive_f(int *pfds, int i_count, int i_max_count){

	int rtn_error = 0;

	char buf[MAX_BUFF];

	pid_t pid;

	if (DBG_LV1){

   		printf('[e_o: %d][count: %d/%d] recursive_f (*pfds, %d, %d)\n',
   			  exec_order, i_count, i_max_count, i_count, i_max_count);

	}//end if

	/* fork */
    if ( ( pid = fork() ) < 0) {

		if (DBG_LV2)
			perror('error fork pid');

		rtn_error = 1;

    }else{

    	/* Parent of first fork */
    	if (pid > 0 && i_count == i_max_count){

	   		if (DBG_LV1)
	   			printf('[e_o: %d][count: %d/%d] ', exec_order, i_count, i_max_count);

    		printf('Parent of first fork sending message: %s', MSG);    	

    		rtn_error = write_pipe(pfds, MSG, MAX_BUFF, 0);	 

	   		if (DBG_LV1)
	   			printf('[e_o: %d][count: %d/%d] ', exec_order, i_count, i_max_count);

    		printf('PARENT OF FIRST FORK SENT MESSAGE: %s', MSG);

    	}else{
	    	exec_order++;   	

    	}//end if

		/* Child of last fork */
		if (pid == 0){

			if (i_count < 1){

		   		if (DBG_LV1)
		   			printf('[e_o: %d][count: %d/%d] ', exec_order, i_count, i_max_count);

				printf('Last Child of fork reading...\n');

				rtn_error = read_pipe(pfds, buf, MAX_BUFF, 5);

				if (rtn_error == 0){

			   		if (DBG_LV1)
			   			printf('[e_o: %d][count: %d/%d] ', exec_order, i_count, i_max_count);

					printf('LAST CHILD OF FORK READ MESSAGE: %s', buf);

				}//end if

			}else{

	  			i_count--;

	  			if (DBG_LV1)
		  			printf('[e_o: %d][count: %d/%d] Calling recursive_f (i_count[%d] and i_max_count[%d]) \n',
		  				   exec_order, i_count, i_max_count, i_count, i_max_count);

	  			rtn_error = recursive_f(pfds, i_count, i_max_count);

	  		}//end if

    	}//end if

    }// end if	

	return rtn_error;
}

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab){

		int rtn_error = 0;

		/* close the read end */
		close(pfds[0]);

		if (DBG_LV1) printf ('%*sWRITE_PIPE: %s\n', i_tab, ' ', msg);

		/* write message to parent  through the write end */
        if(write(pfds[1], msg, max_buff) <= 0) {

			if (DBG_LV2) printf('%*s[X] ERROR: Cannot write\n', i_tab, ' ');

			rtn_error = 1;

		}//end if

	return rtn_error;
}

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab){

		int rtn_error = 0;

		/* close the write end */
		close(pfds[1]);

		if (DBG_LV1) printf ('%*sREAD_PIPE: ', i_tab, ' ');

		/* read message from child through the read end */
		// If not data in the pipe, the read will block
	    if( read(pfds[0], msg, max_buff) <= 0 ) {

			if (DBG_LV2) printf ( '%*s[X] ERROR: Cannot read\n', i_tab, ' ');

			rtn_error = 1;

		}else{

			if (DBG_LV1) printf('%*s%s \n', i_tab, ' ', msg);		

		}//end if

	return rtn_error;
}

If you encounter any problems or errors, please let me know by providing an example of the code, input, output, and an explanation. Thanks..

/**
* @course: CS350 Lab
* @author: Alejandro G. Carlstein R. M.
* @description: This program will take an integer argument.
*               It will create a chain of parent-child equal to the value
*               provided by the integer argument, Example:
*                 Top parent – forks ==>
*                  child1 – forks ==>
*                 child1’s child – forks … ==> Nth generation child, where
*                 N is the integer argument.
*                 Then the top parent will then pass a message to
*               the child at the lowest level (Nth generation child)
*               through a pipe. That child will print
*               the received message to output stdout.
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MAX_N 3
#define IDX_ARG_N 1
#define MAX_BUFF 30
#define DBG_LV1 0
#define DBG_LV2 1
#define MSG “Hello! \n”

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab);

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab);

int recursive_f(int *pfds, int i_count, int i_max_count);

int exec_order;

int main(int argc, char *argv[])
{
pid_t rtn_pid, pid[3];

int rtn_error = 0;

int i, status;

int pfds[2];

int i_N = (argc > 2) ? atoi(argv[IDX_ARG_N]): 0;

exec_order = 0;

printf(“\n”);

/* create a pipe */
if (pipe(pfds) == -1) {
perror(“pipe”);
exit(1);
}else{

rtn_error = recursive_f(pfds, MAX_N, MAX_N);

}// end if

close(pfds[0]);
close(pfds[1]);

for (i = 0; i < MAX_N – 1; i++){
wait();
}

return rtn_error;
}

int recursive_f(int *pfds, int i_count, int i_max_count){

int rtn_error = 0;

char buf[MAX_BUFF];

pid_t pid;

if (DBG_LV1){

printf(“[e_o: %d][count: %d/%d] recursive_f (*pfds, %d, %d)\n”,
exec_order, i_count, i_max_count, i_count, i_max_count);

}//end if

/* fork */
if ( ( pid = fork() ) < 0) {

if (DBG_LV2)
perror(“error fork pid”);

rtn_error = 1;

}else{

/* Parent of first fork */
if (pid > 0 && i_count == i_max_count){

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] “, exec_order, i_count, i_max_count);

printf(“Parent of first fork sending message: %s”, MSG);

rtn_error = write_pipe(pfds, MSG, MAX_BUFF, 0);

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] “, exec_order, i_count, i_max_count);

printf(“PARENT OF FIRST FORK SENT MESSAGE: %s”, MSG);

}else{
exec_order++;

}//end if

/* Child of last fork */
if (pid == 0){

if (i_count < 1){

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] “, exec_order, i_count, i_max_count);

printf(“Last Child of fork reading…\n”);

rtn_error = read_pipe(pfds, buf, MAX_BUFF, 5);

if (rtn_error == 0){

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] “, exec_order, i_count, i_max_count);

printf(“LAST CHILD OF FORK READ MESSAGE: %s”, buf);

}//end if

}else{

i_count–;

if (DBG_LV1)
printf(“[e_o: %d][count: %d/%d] Calling recursive_f (i_count[%d] and i_max_count[%d]) \n”,
exec_order, i_count, i_max_count, i_count, i_max_count);

rtn_error = recursive_f(pfds, i_count, i_max_count);

}//end if

}//end if

}// end if

return rtn_error;
}

int write_pipe(int *pfds, const char *msg, int max_buff, int i_tab){

int rtn_error = 0;

/* close the read end */
close(pfds[0]);

if (DBG_LV1) printf (“%*sWRITE_PIPE: %s\n”, i_tab, ” “, msg);

/* write message to parent  through the write end */
if(write(pfds[1], msg, max_buff) <= 0) {

if (DBG_LV2) printf(“%*s[X] ERROR: Cannot write\n”, i_tab, ” “);

rtn_error = 1;

}//end if

return rtn_error;
}

int read_pipe(int *pfds, char msg[], int max_buff, int i_tab){

int rtn_error = 0;

/* close the write end */
close(pfds[1]);

if (DBG_LV1) printf (“%*sREAD_PIPE: “, i_tab, ” “);

/* read message from child through the read end */
// If not data in the pipe, the read will block
if( read(pfds[0], msg, max_buff) <= 0 ) {

if (DBG_LV2) printf ( “%*s[X] ERROR: Cannot read\n”, i_tab, ” “);

rtn_error = 1;

}else{

if (DBG_LV1) printf(“%*s%s \n”, i_tab, ” “, msg);

}//end if

return rtn_error;
}

Share