Waiting for and Exiting from Processes in Linux C

Child processes are scheduled independently of parent processes. As such, we cannot predict in what order parent and child processes will execute. We often want to have a parent process wait until one or more child processes have completed. To do so, we can use the wait family of system calls. Today, we will look at one of the these system calls – the wait() function.

The wait() function waits for any child process to die.  To use wait(), we must include the sys/types.h and sys/wait.h header files.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void){

    pid_t child;
    int status;

    if((child = fork())<0){
        printf("Error in creating new process.");
        return 1;
    }


    //in parent process
    if(child>0){
        //pass wait a pointer to int
        wait(&status);
        printf("\nThat's a lot of numbers!\n");
    }

    //in child process
    if(child==0){
        int i = 0;
        while(i++ < 1000){
            printf("%d", i);
        }
    }

    return 0;

}

The wait function accepts a single argument in the form of a pointer to an int.

The wait function returns the process ID of the child process that has ended.

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void){

    int status;
    pid_t child = fork();
        
    if(child<0){
        printf("problem forking process.\n");
        return 1;
    }

    if(child==0){
        printf("in child process.\n");
        printf("process ID %d\n", getpid());
        printf("parent ID %d\n", getppid());
    }

    if(child>0){
        if(child==wait(&status)){
            printf("child process %d ended.\n", child);
        }
    }

    return 0;

}

Our next program will span multiple processes using a while loop and assign each PID to an array, which we we will use to keep track of what child processes have completed executing.

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

#define NUM_ELEMENTS 10

void removeProcess(pid_t array[], pid_t process, int size);
void printProcesses(pid_t array[], int size);

int main(void){

    pid_t parentID = getpid();

    pid_t childID;

    pid_t IDs[NUM_ELEMENTS];

    int i = 0;

    int status;
    int random;



    while(i < NUM_ELEMENTS){
        childID = fork();
        if(childID==0){
            printf("In child ID %d with parent ID %d\n", getpid(), getppid());
            //generate random number for sleep()
            srand(time(NULL)+(i*2));
            random = (rand() % 15) + 1;
            sleep(random + random);
            //break out of the loop
            break;
        }
        if(childID > 0){
            IDs[i++] = childID;
        }

    }
    
    if(parentID==getpid()){

        while(i > 0){
            childID = wait(&status);
            printf("\nProcess %d exited.\n", childID);
            //remove process from array
            removeProcess(IDs, childID, NUM_ELEMENTS);
            //print out remaining processes
            if(i!=1){
                printf("The following child processes remain active: \n");
                printProcesses(IDs, NUM_ELEMENTS);
            }
            i--;
        }

    }
    


    return 0;

}


//remove the exited process from the array
void removeProcess(pid_t array[], pid_t process, int size){
    for(int i = 0; i < size; i++){
        if(array[i]==process){
            array[i] = 0;
            return;
        }
    }
}

//print the remaining active processes
void printProcesses(pid_t array[], int size){
    for(int i = 0; i < size; i++){
        if(array[i]!=0){
            printf("%d\t", array[i]);
        }
    }
    putchar('\n');
}

Next time we will cover waitpid() as well as interpreting the exit status of child processes.

Be sure and check out my author page on Amazon.com at http://www.amazon.com/Al-Jensen/e/B008MN382O/

 

 

Advertisements

Reviewing Dynamic Memory Allocation in C

We can find four memory management functions in the stdlib.h header file: malloc(), realloc(), calloc(), and free(). The malloc() function allocates memory from the heap. The realloc() function reallocates memory relative to a previously allocated block of memory. The calloc() function is like malloc(), but zeros out the memory that is allocated from the heap. Finally, the free() function returns a block of memory to the heap.

The malloc() function allocates a block of memory from the heap; if no memory is available, NULL is returned. Since there is no guarantee that a malloc() call will work, we should always check to see if the function returned NULL.

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

int main(void){
    
    void *memPtr = (void*)malloc(8);

    if(memPtr==NULL){
        printf("Could not allocate memory.\n");
    } else {
        printf("8 bytes allocated.\n");
    }

    free(memPtr);

    return 0;

}

The free() function accepts a pointer as an argument and returns the block of memory pointed to back to the heap.

In the next program I use bitwise operators to turn on bits in the memory block. While using bitwise operators is lots of fun, at least I think so, it isn’t the most necessary thing to know how to do. The most important thing to remember when using bitwise operators is that 0 and 1 have the same value in binary as they do in the base 10 number system. As a recap, (1 << n) will turn on the bit at position n, and the | operator basically ‘adds’ the number on the left to the  number on the right. If you’re interested in learning more, take a look at my other posts at https://aljensencprogramming.wordpress.com/tag/bitwise-operators/

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

int main(void){

    void *ptr = malloc(1);

    if(ptr!=NULL){
        //turn off all bits in the memory block
        *(char*)ptr = *(char*)ptr & 255;
        printf("%d\n", *(char*)ptr);
        
        //turn on first bit
        *(char*)ptr = *(char*)ptr | 1;
        printf("%d\n", *(char*)ptr);

        //turn on the fourth bit
        *(char*)ptr = *(char*)ptr | (1<<4);
        printf("%d\n", *(char*)ptr);
    }


    //free up the memory
    free(ptr);

    
    

    return 0;

}

Note that it isn’t necessary to explicitly cast the return value of the malloc() function; whether to cast or not to cast is a minor debate topic.

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


int main(void){


    int *ptr = malloc(4);
    int *ptr2 = (int*)malloc(4);

    *ptr = 404;
    *ptr2 = 1138;
    
    if(ptr!=NULL){
        printf("%d\n", *ptr);    
    }

    if(ptr2!=NULL){
        printf("%d\n", *ptr);
    }

    return 0;

}

When using malloc() its important to use the function to allocate the correct number of bytes. So far, we have been using magic numbers to specify the number of bytes to allocate, which is generally a bad idea. Standard procedures is to use the sizeof operator when specifying the number of bytes to allocate.

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

int main(void){

    int *iPtr = (int*)malloc(sizeof(int));

    double *dPtr = (double*)malloc(sizeof(double));


    *iPtr = 3000;
    *dPtr = 255.255;

    printf("%d \t %f\n", *iPtr, *dPtr);
    
    free(iPtr);
    free(dPtr);

    return 0;

}

Sometimes we need to increase or decrease the amount of memory allocated to a pointer, which can be done using the realloc() function. The realloc() function takes two arguments, the first argument is a pointer to the original block of memory, and the second is the new size, which can be larger or smaller than the size of the original memory block.

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

int main(void){

    int *intPtr = (int*)malloc(5*sizeof(int));
    
    *intPtr = 73;
    *(intPtr+1) = 47;
    *(intPtr+2) = 1138;
    *(intPtr+3) = 80;


    printf("%d\n", *(intPtr+1));
    printf("%d\n", *(intPtr+2));

    //resize the array
    intPtr = realloc(intPtr, 4*sizeof(int));
    
    printf("%d\n", *(intPtr+3));
    
free(intPtr);

    return 0;

}

If the size of the new memory block is smaller than the current block, the memory block remains in the same location. If the memory block is larger, either a new block in a new location will be allocated, and the contents of the old block copied over, or else if there is free consecutive memory next to the current block it will be expanded to include it.

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

int main(void){

    int *ints = (int*)malloc(10*sizeof(int));

    printf("ints is located at %p\n", ints);
    
    double *dubs = (double*)malloc(5*sizeof(double));
    
    //ints will most likely be put into a new location
    ints = realloc(ints, 20*sizeof(int));
    printf("ints is located at %p\n", ints);
    
    //memory location will remain the same
    ints = realloc(ints, 5*sizeof(int));
    printf("ints is located at %p\n", ints);

    //don't forget to free the allocated memory
    free(dubs);
    free(ints);
    
    return 0;

}

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

Brief Overview of Real and Effective IDs in Linux C

A process has user and group identifiers, known as UID and GID numbers. The UIDs and GIDs control how the process interacts with files and other processes. The superuser root, identified by the UID 0, is a special case. The root superuser is allowed free reign over the system.

The real user ID is the UID of the user that forked the process. The effective user ID is the UID used for permission checking. Usually the effective and real UIDs are the same.

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

int main(void){

    int val;

    printf("This process is %d\n", getpid());
    printf("The real user ID is %d\n", getuid());
    printf("The effective user ID is %d\n", geteuid());


    if((val=fork())<0){
        printf("Problem creating new process\n");
        return 1;
    }
    //fork returns 0 to newly created process    
    if(val==0){
        printf("This process is %d\n", getpid());
        printf("The real user ID is %d\n", getuid());
        printf("The effective user ID is %d\n", geteuid());
    }

    return 0;

}

The GID is the real group ID, which is the GID of the user that created the process. The effective group ID is the GID used for permission checking. Both of these numbers are analogous to the UID and the effective UID, respectively.

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

int main(void){

    printf("Real user ID: %d\n", getuid());
    printf("Effective user ID: %d\n", geteuid());
    printf("Real group ID: %d\n", getgid());
    printf("Effective group ID: %d\n", getegid());


    return 0;

}

We can access the credentials of a process by reading the /proc/PID/status file. These files have a line each describing the UID and GID values.

Note that in the next program we use the sprintf() function to concatenate strings. In production code, we would most likely use the slightly more complicated snprintf() function.

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

int main(void){

    int fileDescriptor;
    int value = 1;
    char pathname[50] = "/proc/";
    char intString[20];
    char buffer[256];

    pid_t processID = getpid();

    printf("This process is %d\n", processID);
    
    //generate pathname---
    //convert int process ID to string
    sprintf(intString, "%d", processID);
    //append processID string to pathname
    strcat(pathname, intString);
    //append /status to pathname
    strcat(pathname, "/status");
    //pathname generated ------
    
    //print out pathname to console
    printf("Pathname = %s\n", pathname);

    fileDescriptor = open(pathname, O_RDONLY);

    if(fileDescriptor < 0){
        printf("Error accessing %s\n", pathname);
        return 1;
    }

    while(1){
        value = read(fileDescriptor, buffer, 255);
        if(value < 0){
            printf("Error reading from %s\n", pathname);
            break;
        } else if (value == 0){
            break;
        } else {
            buffer[255]='';
            printf("%s\n", buffer);
        }
    }    

    return 0;

}

We can attempt to modify the effective user and group IDs using setuid() and setgid() functions. However, unless we are root, we ca only change the values to the same value as either the real user ID or the file’s UID or GID (for instance, if we are running the program as a user other than than the program file’s creator).

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

int main(void){
    
    int returnVal;
    
    printf("current process UID %d\n", getuid());

    if((setuid(0))<0){
        printf("Failed to change effective ID.\n");
    }

    //setuid() yields EPERM error
    //if user does not have sufficient privileges 
    //to change the effective ID
    if(errno==EPERM){
        printf("That operation is not permitted.\n");
    }

    return 0;

}

We will look deeper at changing effective user IDs at a later date.

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

 

Pointers By Example in C

Pointer variables are variables that contain addresses of other variables; they contain the location of regular data variables, in effect pointing to the data because they hold that data’s location.

Pointers offer a powerful and efficient means of accessing and changing data.

Pointers follow all the normal naming rules of regular variables. We must define a pointer before we use it; there are character pointers, floating-point pointers, integer pointers, and so on.

There are two pointer operators in C, the address of operator, &, and the dereferencing operator, *. These operators are overloaded operators, since they can be used for other things, such as multiplication.  When used in the context of pointers, the address of operator always produces the memory address of whatever variable it is placed in front of.

#include <stdio.h>


int main(void){

    int a = 9;
    int b = 10;
    char c = 's';
    double d = 19.99;

    printf("%d located at %p\n", a, &a);
    printf("%d located at %p\n", b, &b);
    printf("%c located at %p\n", c, &c);
    printf("%f located at %p\n", d, &d);

    return 0;    

}

Note that the %p conversion specifier is used to print memory addresses.

To define a pointer variable, we follow the same recipe for defining a variable of the desired type, except we play a * between the type name and the variable’s identifier. Because of the addition of the dereferencing operator, the C compiler knows that we wish to define a pointer. A pointer variable must point to a variable of the same type.

#include <stdio.h>


int main(void){

    double *dPtr;
    int *iPtr;

    int i= 73;
    double d = 25.1;

    //assign memory addresses
    //to the pointers
    dPtr = &d;
    iPtr = &i;

    printf("dPtr = %p\n", dPtr);
    printf("iPtr = %p\n", iPtr);
    
    //rValue of dPtr
    //is equal to the lValue of d
    if(dPtr == &d){
        printf("dPtr == &d\n");
    }    

    return 0;

}

Note that C does not initialize pointers when we define them. We must explicitly assign the pointer variable the memory address of another variable of the same type. To print the value stored by the variable the pointer is pointer to, we use the dereferencing operator, *.

#include <stdio.h>

int main(void){

    char c = 's';

    char *cPtr = &c;

    printf("c = %c\n", c);
    printf("&c = %p\n", &c);
    printf("cPtr = %p\n", cPtr);
    printf("*cPtr = %c\n", *cPtr);    

    return 0;

}

A pointer must be of the same type as the variable its pointing to,  but, there is an exception to this rule. A pointer variable of type void can be used to point to variables of different types, however, we must cast it to the correct data type when we dereference it.

#include <stdio.h>

int main(void){

    int i = 404;
    unsigned uInt = 404405;
    double dare = 169.254;

    void * vPtr = &i;
    printf("%d\t", *(int *)vPtr);
    
    vPtr = &uInt;
    printf("%u\t", *(unsigned *)vPtr);

    vPtr = &dare;
    printf("%f\n", *(double *)vPtr);


    return 0;

}

To pass a variable to a function by reference instead of by value, place an & in front of the variable in the argument list, and an * everywhere it appears in the function body.

#include <stdio.h>

void incrementValue(int *i);

int main(void){

    int i = 46;
    int j = 73;

    printf("i = %d\t", i);

    incrementValue(&i);

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

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

    return 0;

}


void incrementValue(int *i){
    *i+=2;
}

Our next program will swap the values of two integers using a function that accepts its arguments by reference.

#include <stdio.h>

void swapInts(int *a, int *b);

int main(void){

    int i = 42;
    int j = 73;

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

    swapInts(&i, &j);


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

    return 0;

}


void swapInts(int *a, int *b){
    int swap = *a;
    *a = *b;
    *b = swap;
}

Finally, it is possible to construct an array of pointers, with each pointer being a pointer to a specific data type.

#include <stdio.h>

int main(void){


    int i, j, k, l, m;

    int *intPtrs[5];

    intPtrs[0] = &i;
    intPtrs[1] = &j;
    intPtrs[2] = &k;
    intPtrs[3] = &l;
    intPtrs[4] = &m;

    for(int counter = 0; counter < 5; counter++){
        *intPtrs[counter] = (counter + 1) *3;
    }

    printf("%d \n", k);
    printf("%d \n", m);

    return 0;

}

If you can, please take a look at my Amazon.com author page: http://www.amazon.com/Al-Jensen/e/B008MN382O/

 

 

 

 

Accesing Basic User and Group Information in Linux Using C

There are a set of environment variables consisting of name-value pairs for each program. These variables are often used to control program behavior. We can access the current environment variables using the getenv() function.

In our first program we will use the getenv() function to find out who the current user is.

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

int main(void){

char *username = getenv(“USER”);

if(username==NULL){
printf(“Could not access USER environment variable.”);
exit(EXIT_FAILURE);
} else {
printf(“User: %s\n”, username);
}

return 0;
}

We can list all users by accessing the /etc/passwd file. The /etc/passwd file is an ASCII file consisting of seven fields separated by semicolons, with one line per user.

The <pwd.h> standard header file defines the functions that read the user database. The setpwnt() and endpwent() functions open and close the password database. Neither of these two functions return a value or accept an argument.

#include <stdio.h>
#include <pwd.h>

int main(void){

//open the password database
setpwent();

//close the password database
endpwent();

return 0;

}

Remember, access to the user database is through the routines declared in pwd.h.

The getpwnam() function retrieves a record from the password file. The function returns a pointer to a passwd structure that is filled in by the function.

#include <stdio.h>
#include <pwd.h>

int main(void){

struct passwd *pwd;
char *name = “aljensen”;

pwd = getpwnam(name);

if(pwd==NULL){
printf(“Could not find user %s.\n”, name);
} else {
printf(“Found user %s.\n”, name);
}

return 0;

}

The passwd structure contains seven fields. This structure is statically allocated and overwritten on each call to the function. The pw_name field contains the username, the pw_uid contains the user ID, and the pw_gid field contains the group ID.

#include <stdio.h>
#include <pwd.h>

int main(void){

struct passwd *pw;

if((pw = getpwnam(“root”))==NULL){
printf(“Problem accessing user information for root.\n”);
}

printf(“User Name = %s\n”, pw->pw_name);
printf(“User ID = %u\n”, pw->pw_uid);
printf(“Group ID = %u\n”, pw->pw_gid);
printf(“Shell = %s\n”, pw->pw_shell);

return 0;

}

The getpwent() function can be used to read through the entire password database, one user at a time. Like the getpwnam() function,it returns a pointer to a passwd structure.

#include <stdio.h>
#include <pwd.h>

int main(void){

struct passwd *ptr;

//rewinds the file
setpwent();

while(1){
//returns NULL when it reaches the end
ptr = getpwent();

if(ptr==NULL){
break;
}

//print out user ID and username
//-5 included to left align 5 column field
printf(“%-5u %s\n”, ptr->pw_uid, ptr->pw_name);

}

//closes the file
endpwent();

return 0;

}

The format of the group database is similar to that of the individual user database, but with four fields. The last field, gr_mem, contains a list of the group’s members. We can iterate through this list using pointer arithmetic.

The group database is typically located at /etc/group. The group database APIs are declared in <grp.h>.

The getgrent() function can be used to list all of the groups in our group database.

#include <stdio.h>
#include <grp.h>

int main(void){

struct group *grp;

int i = 0;

setgrent();

while((grp=getgrent())!=NULL){
printf(“%-5u %s\t”, grp->gr_gid, grp->gr_name);
i=0;
//gr_mem is basically an array of strings
while(*(grp->gr_mem+i)){
printf(“%s “, *(grp->gr_mem+i));
i++;
}
putchar(‘\n’);
}

endgrent();

return 0;

}

Well, that’s enough for today, I think. Please take a look at my Amazon author page http://www.amazon.com/Al-Jensen/e/B008MN382O/

Working with Memory Bits in C

A bit is the smallest possible unit of storage, and can be set to either one or zero. In C, we can manipulate individual bits of data using the bitwise operators. The shift operators can move a bit in a variable to the right or the left by a specified number of positions. The << operator shifts bits to the left; shifting a bit left by one is the same multiplying by two.

#include <stdio.h>

int main(void){

    int a = 1;

    for(int i = 0; i < 8; i ++){
        printf("%d\t", a  <<  i);
    }

    return 0;

}

The right shift operator, >>, is similar to dividing an integer variable by a power of 2. Again, left shifting an integer by n places has the effect of multiplying it by 2^n, and right-shifting an integer has the effect of dividing it by 2^n. Things get a bit more complicated when we start looking at negative numbers, but for now, let’s keep it simple.

#include <stdio.h>

int main(void){

    int a = 255;
    
    //note that output is odd
    for(int i = 7; i > 0; i--){
        printf("%d\t", a >> i);
    }        

    return 0;

}

Eight bits together for a byte, represent in C by the data type char.

The ones complement operator, also know as the NOT operator, also known as the invert operator, is a unary operator that returns the inverse of its operand, meaning that it turns on all the off bits and turns off all the on bits.

#include <stdio.h>

int main(void){

    unsigned char c = 0;
    
    //prints 0
    printf("%d\n", c);

    c = ~c;

    //prints 255
    printf("%d\n", c);

    return 0;

}

Using the NOT operator, we can turn a positive number into a negative number by taking the two’s complement of the binary number. Basically, we flip all the bits and add one to arrive at the negative complement of a positive number.

#include <stdio.h>


int main(void){

    int i = 73;
    int j = ~i + 1;

    printf("%d\t", i);

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


    return 0;

}

 

The bitwise OR operator, also known as the inclusive OR operator, compares two operands. If one or the other bit is a 1, the result is 1.

#include <stdio.h>

int main(void){

    unsigned char c, d, e;

    //c = 1
    c = 1 << 0;

    printf("c = %d\t", c);
    
    //d = 2
    d = 2 << 0;

    printf("d = %d\t", d);

    // c or (inclusive) d
    // = 3
    e = c | d;

    printf("e = %d\t", e);
    
    return 0;

}

Typically the bitwise OR operator is used to turn on a bit. For instance, we could make sure all numbers are odd by turning on their least significant bit.

#include <stdio.h>

int main(void){

    unsigned char nums[] = {42, 10, 161, 53, 80, 73, 47, 110};
    
    int i = 0;

    for(i = 0; i < 8; i++){
        printf("%d\t", nums[i]);
    }

    printf("\n\n");

    //make all numbers odd
    for(i = 0; i < 8; i++){
        nums[i] = nums[i] | 1;
    }

    for(i = 0; i < 8; i++){
        printf("%d\t", nums[i]);
    }

    
    printf("\n\n");

    return 0;

}

Thus, we see that it is possible to turn on individual bits with a combination of the OR operator and the << operator.

#include <stdio.h>

int main(void){

    unsigned char c = 0;

    printf("c = %d\n", c);

    c = 1 << 0;

    //prints out 1
    printf("c = %d\n", c);

    c = 1 << 7 | 1 << 6;

    //prints out 192
    printf("c = %d\n", c);

    c |= 1 << 0;

    //prints out 193
    printf("c = %d\n", c);

    return 0;


}

The AND operator, &, compares two bits. If they are both 1, the result is 1. If either or both are 0, the result is 0. Anding is used to mask, or isolate, sections of binary numbers as well as to turn off specific bits.

#include <stdio.h>

int main(void){

    char c = 0;
    char d, e;

    //c = 64 + 16
    c = 1 << 6 | 1 << 4;
//prints 80
    printf("c = %d\n", c);

    //leaves 4th bit on
    d = c & 1 << 4;
    //leaves 6th bit on
    e = c & 1 << 6;

//prints 16
    printf("d = %d\n", d);
//prints 64
    printf("e = %d\n", e);
    

    return 0;

}

Essentially, the & operator can be used as a sort of subtraction operator, and the | operator can be used as a sort of addition operator. We should understand here that really we are not adding or subtracting anything, we are just turning things on and off mechanistically, and then logically associating the results with mathematical output. After all, the computer does not understand gain or loss, it’s just flipping switches.

#include <stdio.h>

int main(void){

    char c = 10;
    
    char d, e;

    printf("c = %d\n", c);

    //turns off nothing but the second
    //bit
    d = c & ~(1 << 1);
    //turns off everything but
    //the second bit
    e = c & (1 << 1);

    printf("d = %d\n", d);
    printf("e = %d\n", e);    

    return 0;

}

Well, this is enough shenanigans for one day; take a look at my author’s page on Amazon: http://www.amazon.com/Al-Jensen/e/B008MN382O/

 

 

 

 

 

Allocating Memory Storage at Runtime in C

The C library has functions for allocating memory storage space at runtime via a process called dynamic memory allocation. The advantage of dynamic memory allocation over static memory allocation is that we are not required to know how how much memory the program will need before executing the program. All of Cs dynamic memory allocation functions are stored in stdlib.h.

There are four dynamic memory allocation functions that we should know: calloc(), malloc(), realloc(), and free(). The two core functions are malloc() and free(). Each time malloc() is called, a section of free memory is allocated from the heap. Each time free() is called, memory is returned to the system’s heap.

Since we haven’t looked at it previously, first let’s work with calloc(). The calloc() function takes two arguments, whereas the malloc() function takes only one. The first argument is the number of items to allocate memory for, and the second argument is the size of each item in memory. The calloc() function returns a pointer to the first byte of the allocated memory. If there is not enough memory, a null pointer is returned.

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

double *getMem(int numElements);

int main(void){
    
    int i = 0;

    double *ptrToDoubles = getMem(100);
    
    double *ptrToStart = ptrToDoubles;
    
    while(i++ < 100){
        *(ptrToDoubles++) = i * .1587;
    }

    printf("double Array begins at location %p\n", ptrToStart);
    printf("and ends at location %p\n", ptrToDoubles);    

    ptrToDoubles = ptrToStart;

    //print out first item in 
    //dynamic double array
    printf("first element is %f\n",*ptrToDoubles);

    //print out last item in 
    //dynamic doubles array
    printf("last element is %f\n", *(ptrToDoubles+99));

    //free the memory!
    free(ptrToDoubles);
}

double *getMem(int numElements){

    double *ptr;
    
    ptr = calloc(numElements, sizeof(double));

    if(!ptr){
        printf("Allocation error.\n");
        exit(1);
    }

    return ptr;

}

As we have seen above, the free() function returns the memory pointed to by its argument to the heap.

Rather than allocating a group of bytes as malloc() does, calloc() allocates a group of data elements of a set size.

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


int main(void){
    
    //number of elements to create
    unsigned n;
    //index num
    int i;

    int value;    

    int *p;

    printf("How many numbers of type int do you want to allocate?\n");

    scanf(" %u", &n);

    p = calloc(n, sizeof(int));

    if(p!=NULL){
        printf("Memory allocation successful!\n");
    } else {
        printf("Memory allocaion failed!\n");
        return 1;
    }

    while(1){
        printf("What element would you like to assign a value to?");
        printf("(Enter negative number to finish assigning values)\n");

        scanf(" %d", &i);
        if(i < 0){
            break;
        }

        //check if index is within range
        if(i >= n){
            printf("Index out of range.\n");
            continue;
        }

        printf("What value would you like to assign to it?\n");
        scanf(" %d", &value);

        *(p+i) = value;

    }

    //print out values
    i = 0;
    printf("The dynamic int array contains: \n");
    while(i < n){
        printf("%d\n", *(p+i));
        i++;    
    }

    free(p);

    return 0;

}

Note that unlike malloc(), the calloc() function also initializes the stored memory to all zeros, which means that calloc() is slightly slower than malloc().

The malloc() function returns a pointer to the first byte in memory of the size specified as its argument. If there is insufficient memory, the malloc() function will return a null pointer. We should verify that the return value is not null before attempting to use it.

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

struct name{
    char firstName[50];
    char lastName[50];    
};

//returns pointer to name struct
//exits on failure to allocate
struct name *getNameStruct(void){
    struct name *ptr;
    
    if((ptr = malloc(sizeof(struct name)))==NULL){
        printf("Allocation Error.\n");
        exit(1);
    }

    return ptr;

};

int main(void){

    struct name *myName = getNameStruct();
    
    strcpy(myName->firstName, "Al");
    strcpy(myName->lastName, "Jensen");

    printf("First Name %s\n", myName->firstName);
    printf("Last Name %s\n", myName->lastName);

    free(myName);

    return 0;

}

The realloc() function changes the size of a block of memory that was previously allocated by malloc() or calloc(). The first argument is a pointer to the original block of memory. The second argument is the size, in bytes, of the new block.

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


int main(void){
    
    char *ptr1;
    char *ptr2;
    char *ptr3;

    ptr1 = malloc(18 * sizeof(char));

    if(!ptr1){
        printf("Allocation Error.\n");
        return 1;
    }

    strcpy(ptr1, "This is 17 chars.");

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

    ptr2 = malloc(22 * sizeof(char));

    if(!ptr2){
        printf("Allocation Error.\n");
        return 1;
    }

    strcpy(ptr2, "But this is 21 chars.");    

    ptr3 = realloc(ptr1, 40);
    
    strcat(ptr3, ptr2);

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

    //free memory
    //note that ptr3 is reallocated
    //storage of ptr1
    free(ptr2);
    free(ptr3);

    return 0;

}

if you can, please take the time to visit my Amazon.com author page at  http://www.amazon.com/Al-Jensen/e/B008MN382O