Pointers and Functions in C

The stack is a contiguous section of memory set aside for a program.

A stack frame, sometimes known as an activation record, is created and pushed onto the stack whenever we invoke a function. Typically the stack frame consists of the return address back to the calling function, and a set amount of space for all of the parameters and local variables. When the function returns, its stack frame is removed from the stack.

When a stack frame is created, the parameters are copied into the frame in the opposite order set in the parameter list. All parameters are passed by value, which is to say a copy is made of the data and assigned to a memory location in the stack frame. Even we pass a value by reference in C using pointers, we are still making a copy of the data, it just so happens that the data being copied is the memory address of the actual variable, structure, etc we wish to manipulate.

#include <stdio.h>

void func1(int *i);

int main(void){

int a = 10;
int *intPtr = &a;

printf("intPtr holds memory address %p\n", intPtr);
printf("which points to a variable holding the value %d\n", *intPtr);
printf("and is located at %p\n", &intPtr);
//call the function
func1(intPtr);

return 0;

}


void func1(int *i){
printf("func1 parameter i holds the memory address %p\n", i);
printf("which points to a variable holding the value %d\n", *i);
printf("but it is located at %p\n", &i);
}

The thing to remember here is that all parameters, including pointers, are passed by value.

Passing pointers to functions allows the referenced data to be accessible without having to duplicate the data or make it a global variable. By passing pointers to data rather than the data itself, we can modify the data the in the function and have the modification persist beyond the life of the called function.

#include <stdio.h>

void swap(int *iPtr01, int *iPtr02);

int main(void){

int i = 404;
int j = 711;

printf("i = %d, j = %d\n", i, j);

swap(&i, &j);

printf("i = %d, j = %d\n", i, j);

return 0;

}

void swap(int *iPtr01, int *iPtr02){
int localVar;
localVar = *iPtr01;
*iPtr01 = *iPtr02;
*iPtr02 = localVar;
}

We cannot modify a value passed as a pointer to a constant. Passing  a pointer to a constant thus allows us to pass a reference to data without having to copy all of the data to the function, while not having to worry about the data being pointed to being modified.

#include <stdio.h>

void CannotModify(const int *ip);
void CanModify(int *ip);


int main(void){

int i = 42;

CanModify(&i);

CannotModify(&i);

return 0;

}


void CannotModify(const int *ip){
printf("Value = %d\n", *ip);
}


void CanModify(int *ip){
//increment value pointed to
(*ip)++;
}

To return a pointer from a function is simple enough, but we must remember to allocate the memory the pointer will point to using malloc(), so that it will be allocated on the heap, and not the stack.

#include <stdio.h>
#include <string.h>
#include <malloc.h>

double * doubleValue();
char * getString(int length);

int main(void){

double *trouble = doubleValue();
char *str = getString(50);

strcpy(str, "You have failed us, Torgo. For this, you must die!");

printf("%s\n", str);

//don't forget to free the memory
free(str);
free(trouble);

return 0;

}


double * doubleValue(){
double *dare = (double*)malloc(sizeof(double));
return dare;
}

char * getString(int length){
char *s = (char*)malloc(++length * sizeof(char));

return s;
}

When a function returns a pointer to dynamically allocated memory, it is the calling function’s responsibility to deallocate the memory when it is done with it.

As stated earlier, when a pointer is passed a function, it is passed by value. This means that if we want to modify the pointer itself, and not the parameter pointer, we must pass a pointer to the pointer.

#include <stdio.h>
#include <malloc.h>
#include <string.h>

void allocateDouble(double **d, double val);
void allocateString(char **s, char *val);

int main(void){

double *dare;
char *statement;

allocateDouble(&dare, 19.99);

allocateString(&statement, "Nice Boat!");

printf("%s\n", statement);


return 0;

}


void allocateString(char **s, char *val){
int length = strlen(val);
*s = (char*)malloc(length * sizeof(char));
strcpy(*s, val);
}

void allocateDouble(double **d, double val){
*d = (double*)malloc(sizeof(double));
}

if you are interested in learning C, take a look at by book available at:  http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

 

 

 

Advertisements

Implementing Pipes in C

Pipes are an older but still useful mechanism for interprocess communication. Basically, pipes connect one process’s output to another process’s input. Data passes through a pipe sequentially, with bytes being read from the pipe in the same order they were written to the pipe.

There are both named and unnamed pipes. Unnamed pipes never have a pathname.

We use the pipe() function to create a pipe. The pipe() system call returns -1 if an error occurs. 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void){

    int myPipe[2];

    if((pipe(myPipe))<0){
        printf("Error creating pipe.\n");
        exit(EXIT_FAILURE);
    }    

    printf("Descriptors are %d and %d\n", myPipe[0], myPipe[1]);

    return 0;

}

The pipe() system call opens two file descriptors and stores them in an int array. The first descriptor is stored in the first element of the array, and is used for reading. The second descriptor is located in the last element of the int array, and is used for writing.

Data travels in one direction through a pipe. One end is used for writing, and the other is used for reading. We can use the read() and write() call to read and write to pipes.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>


#define BUFFER_MAX 255

int main(void){

    char s[BUFFER_MAX+1] = {"With sufficient thrust, pigs fly just fine."};
    char buffer[BUFFER_MAX+1];

    int readLength, myPipe[2];

    if((pipe(myPipe))<0){
        printf("Error creating pipe.\n");
        exit(EXIT_FAILURE);
    }

    printf("writing '%s' to the pipe\n", s);
    
    //write message into the pipe
    write(myPipe[1], s, strlen(s));

    //read the message from the pipe
    readLength = read(myPipe[0], buffer, BUFFER_MAX);
    
    buffer[readLength]='';

    printf("%s\n", buffer);
    
close(myPipe[0]);
close(myPipe[1]);
    
    return 0;

}

Typically, an unnamed path is created in a parent process and then handed to a child process so that the two processes can communicate.  Because a child process inherits any open file descriptors from its parent, it can communicate with its parent. While it is possible for both parent and child processes to both read and write from the pipe, typically one process closes the write file descriptor, the reference to which is stored at index 1, and the other process closes the read file descriptor, the reference to which is stored at index 0.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void){

    int fileDescription[2];
    
    int returnVal;

    if(pipe(fileDescription)<0){
        printf("Error opening pipe.\n");
        exit(EXIT_FAILURE);
    }

    if((returnVal=fork())<0){
        printf("Error forking.\n");
        exit(EXIT_FAILURE);
    }

    if(returnVal==0){
        //close write end
        close(fileDes[1]);
    }

    if(returnVal>0){
        //close read end
        close(fileDes[0]);
    }


    return 0;

}

Remember, to connect two processes using a pipe, we follow the pipe() call with a fork() call. Remember as well that the write file descriptor is located at index 1 of the array, and the read file descriptor is located at index 0 of the array.

For our final program, we will send a simple string of characters from our parent process to our child process.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <wait.h>

#define BUFFER_MAX 255

int main(void){

    int fd[2], messageLength, returnVal;

    char buffer[BUFFER_MAX+1];
    char message[BUFFER_MAX+1];

    if((pipe(fd)<0)){
        printf("Error creating pipe.\n");
        exit(EXIT_FAILURE);
    }

    if((returnVal=fork())<0){
        printf("Error forking.\n");
        exit(EXIT_FAILURE);    
    }
    
    //in child process
    if(returnVal==0){
        //close write descriptor
        close(fd[1]);
        messageLength = read(fd[0], buffer, BUFFER_MAX);
        buffer[messageLength] = '';
        printf("received: '%s'\n", buffer);    
        //close read descriptor
        close(fd[0]);
    }
    if(returnVal>0){
        //in parent process
        //close read descriptor
        close(fd[0]);
        //copy message
        char *s = "A strange game. The only winning move is not to play.";
        strcpy(message, s);
        //send message
        write(fd[1], message, strlen(message));
        //close write descriptor
        close(fd[1]);
        //wait for child process to finish
        waitpid(returnVal, NULL, 0);

        printf("Exiting parent process.\n");
        
        return 0;
    }
    

}


If you’re interested in learning standard C, take a look at my book http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

 

The kill() Function in C

The easiest way to send a signal to a process is via the kill() signal.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

int main(void){

    pid_t retVal;

    retVal = fork();

    if(retVal > 0){
        int i = 0;
        while(i++ < 5){
            printf("in the parent process.\n");
            sleep(1);
        }
        //kill the child process
        kill(retVal, SIGKILL);

    } else if (retVal == 0){
        int i = 0;
        //will not ever get to 15, because
        //the parent process will kill it
        while(i++ < 15){
            printf("In the child process.\n");
            sleep(1);
        }
    } else {
        //something bad happened.
        printf("Something bad happened.");
        exit(EXIT_FAILURE);
    }

    return 0;

}

Remember, the fork function returns a process identifier that identifies the context in which the process is running.  If the process ID returned is zero, then the process is the newly created child process. If the return value is greater than zero, then that value is the ID of the child process, and we know that we are in the parent process.

If the kill() function fails, it returns -1, otherwise it returns 0. We can use the return value from the kill() function to determine if the process was successfully terminated.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

int main(void){

    int status;
    pid_t killReturnValue, forkReturnValue;

    if((forkReturnValue=fork())<0){
        //error occurred.
        printf("Danger! Danger, Will Robinson!\n");
        exit(EXIT_FAILURE);
    }

    if(forkReturnValue==0){
        sleep(100);
        exit(EXIT_SUCCESS);
    } else {
        killReturnValue = kill(forkReturnValue, SIGKILL);
        if(killReturnValue){
            printf("Unable to kill child process.\n");
            waitpid(forkReturnValue, &status, 0);
        } else {
            printf("Child process killed.\n");
        }
    }
    
    return 0;
}

Although the kill() function is often used to terminate methods, the function can actually send other signals as well. For instance, we can send the SIGSTOP signal to stop a process, and the SIGCONT signal to continue a process.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

int main(void){

    int status;
    pid_t killReturnValue, forkReturnValue;

    if((forkReturnValue=fork())<0){
        //error occurred.
        printf("Danger! Danger, Will Robinson!\n");
        exit(EXIT_FAILURE);
    }

    if(forkReturnValue==0){
        sleep(100);
        exit(EXIT_SUCCESS);
    } else {
        killReturnValue = kill(forkReturnValue, SIGKILL);
        if(killReturnValue){
            printf("Unable to kill child process.\n");
            waitpid(forkReturnValue, &status, 0);
        } else {
            printf("Child process killed.\n");
        }
    }
    
    return 0;
}

Next time we will cover signal handling with the signal() function.

 

 

User-defined Functions in C

In C functions must be both declared and defined. We declare a function using a function prototype statement. The function prototype consists of the function name, the function’s return type, and its parameter list. The function definition consists of all the information in the function prototype, followed by the function itself delimited by curly braces. 

Our first function is a void function with no parameters. We call the function only to get its side effect, not a return value. Because it is a void function, it can only be used as a statement, not in an expression.

#include <stdio.h>

//function declaration
//ie function prototype
void greeting();

int main(void){

    //call the function from within main()
    greeting();
    
    return 0;

}

//function definition
void greeting(){
    printf("Saluton Mundo!\n");
}


Next, let’s call a function that accepts parameters but still does not return a value to the calling function. A function that does not return a value is void. Like our previous function, we can cannot include the calling function in another expression.

#include <stdio.h>

void printMessage(int i);

int main(void){

    printMessage(5);
    printf("\n\n");
    printMessage(3);

    return 0;

}

void printMessage(int i){
    while(i--){
        printf("Do you have stairs in your house?\n");
    }
}

Note that a function can be called multiple times. This is part of the value of functions.

Prototype declarations consist of only a function header containing three elements: the return type, the function name, and the formal parameter list. Be aware that function prototypes are terminated with a semicolon.

Our next program contains a function that passes arguments and returns a value, in this case, square of the parameter. Since this function returns a value, we can use it as if it were a variable or a literal value in another expression or statement. We will use fgets() to retrieve the input from standard input, instead of scanf(), as fgets() is somewhat safer to use.

#include <stdio.h>
#include <stdlib.h>

int getNumber(void);
//return number squared
int getSquare(int a);

int main(void){

    int a = getNumber();

    int aSquare = getSquare(a);

    printf("%d squared is %d\n", a, aSquare);

    a = getNumber();

    printf("%d squared is %d\n", a, getSquare(a));


    return 0;

}

int getSquare(int a){
    return a * a;
}

int getNumber(void){

    //accept an int of up to 10 digits
    char buffer[10];

    printf("Enter a number of up to ten digits in length: ");
    fgets(buffer, sizeof(buffer), stdin);

    return atoi(buffer);


}

The function definition contains the code for a function; it has two parts – the function header and the function body. The function body is a compound statement, meaning that is a code block delimited by curly braces that can be used in place of one single statement. A function header has three parts: the return type, the function name or identifier, and the formal parameter list. Technically speaking, the parameters are the values received by a function, whereas the arguments are the values sent to the function in the actual function call; however, we often see the terms parameters and arguments used interchangeably, and in some languages this distinction is not maintained at all.

If the return type is not specified the C compiler will assume a return type of int. If the function type is void we do not have to specify a return statement; however, we can include a return statement in a void function if we want the function to conditionally return execution back to the calling function early. We will look at an example of this now.

#include <stdio.h>


void factorialNumber(int n);


int main(void){

    for(int i = 0; i < 10; i++){
        factorialNumber(i);
    }

    return 0;

}


void factorialNumber(int n){
    //local variable;
    int j = 1;
    //if n is 0, return early
    //or if n is less than zero
    if((!n) || (n<0)){
        return;
    }

    printf("!%d = ", n);

    while(n){
        j*=n--;
    }

    printf("%d\n", j);

 
}

In the definition of a function, the parameters are listed in the formal parameter list. This lists defines and declares all of the local variables that will be assigned copies of the data sent to the function via the function call. In the case of arrays, the parameter contains a copy of the memory address of the first element in the function. Multiple parameters should be separated by commas.

Function calls have an extremely high level of precedence, which means they can be easily used in other expressions as we can be sure the function will resolve before the returned value is used.

#include <stdio.h>
#include <stdlib.h>

//fuction protoypes
int getNum(void);
int getDigits(int a);

int main(void){
    
    printf("Enter a number and we will print it backwards: ");

    getDigits(getNum());

    return 0;

}


int getNum(void){

    char buffer[10];


    return atoi(fgets(buffer, sizeof(buffer), stdin));
}

int getDigits(int a){
    while(a > 0){
        printf("%d", a % 10);
        return getDigits(a /= 10);
    }
    printf("\n");
    return 0;
}

Well, that’s enough mischief for today. If you’d like to learn more about C, following a series of structured lessons with plenty of example code, purchase a copy of my book on C at http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

 

 

 

Introduction to Signals Using Alarm() in Linux C

A signal is the software equivalent of a hardware interrupt. Signals provide a means for handing asynchronous events, with asynchronous here meaning “unpredictable”.

There are several dozen different signals, each with a different meaning. Every one of these signals has a name, beginning with the three letters SIG. Each signal is specified by its number, but we mostly use signals by their names. To utilize signals, we must include the header file signal.h.

The most commonly used signals are SIGHUP, SIGQUIT, SIGKILL, and SIGTERM. Each of these signals has a numeric value. SIGKILL, 9, is perhaps the most notorious, as it issues an unblockable kill signal.

#include <stdio.h>
#include <signal.h>

int main(void){

    printf("SIGHUP = %d\n", SIGHUP);
    printf("SIGQUIT = %d\n", SIGQUIT);
    printf("SIGKILL = %d\n", SIGKILL);
    printf("SIGTERM = %d\n", SIGTERM);

    return 0;


}

Every signal has a default action associated with it; this is known as the signal’s disposition.

The alarm() function sets a timer that generates the SIGALARM signal. If we ignore or don’t catch this signal, the process is terminated.

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

static void alarmHandler(int signo);

int main(void){

    alarm(5);

    signal(SIGALRM, alarmHandler);

    for(int i = 1; i < 10; i++){
        printf("%d\n", i);
        sleep(1);    
    }

    return 0;

}

static void alarmHandler(int signo){
    printf("Alarm signal sent!\n");

}

Note that there is only one alarm clock per process. While the default action for SIGLARM is to terminate the process, usually we will want to catch this signal.

The wait() function will cause a process to wait until a child process terminates.

#include <stdio.h>
#include <stdlib.h> //for exit() function
#include <unistd.h>
#include <signal.h>
#include <wait.h>
#include <sys/types.h>

static void catchAlarm(int signo);

int main(void){

    int status;
    pid_t pid;
    
    signal(SIGALRM, catchAlarm);
    pid = fork();

    if(pid < 0){
        printf("Problem forking process.\n");
        printf("Exiting now...\n");
        exit(EXIT_FAILURE);
    }

    if(pid==0){
        alarm(5);
        for(int i = 0; i < 7; i++){
            printf("In child process... %d\n", i);
            sleep(1);
        }
    } else     {
        wait(&status);
        alarm(7);
        for(int i = 0; i < 15; i++){
            printf("In parent process... %d\n", i);
            sleep(1);
        }
    }

}


static void catchAlarm(int signo){
    printf("Caught an SIGALRM signal.\n");
    printf("Signal value = %d\n", signo);

    printf("Exiting from process...\n");

    //in stdlib.h header file
    exit(EXIT_SUCCESS);

}

Note that the alarm() function will return a value if another alarm has been previously set.

The pause() function suspends the process until a signal is caught.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

static void catchSignal(int signo);

int main(void){

    alarm(7);

    signal(SIGALRM, catchSignal);

    //remember, a process can have
    //only one alarm
    if(alarm(5) > 0){
        printf("An alarm has already been set.\n");
    }

    pause();

    printf("We should not see this text.\n");

    return 1;
}

static void catchSignal(int signo){
    printf("Caught the %d signal\n", signo);
    printf("Exiting now...\n");

    exit(EXIT_SUCCESS);
}

Remember, a process can have only one alarm.