6
\$\begingroup\$

This is follow up question regarding merge-sort implementation to the previous post.
Recently I have implemented a command-line argument based merge-sort sorting algorithm.

Format for command-line-arguments:
name_of_the_output_program size_of_sequence sequence in ""
The fourth argument is optional, if -r or -reverse is passed then sort the sequence in decreasing order else by default sort the sequence in increasing order.

For Example:

./merge_sort 5 "25 41 11 32 45"
11 25 32 41 45 

./merge_sort 5 "25 41 11 32 45" -r
45 41 32 25 11

./merge_sort 5 "25 41 11 32 45" -reverse
45 41 32 25 11

./merge_sort 3 "1 2 0" -reverse
2 1 0

Source Code:

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

bool reverse_order = false;

typedef unsigned long long int64;
typedef long long uint64;

static const bool check_sorted(const int64 *const,const size_t,const bool);
static void merge_sort_integer_sequence(int64 *const,const size_t,const size_t);
static void merge_integer_data(int64 *const,const size_t,const size_t,const size_t,const bool (*comparator)(const void*,const void*));
static void display_sequence(const int64 *const,const size_t);
static const bool integer_comparator(const void*,const void*);

int main(int argc,char *const argv[]) {
    if(argc == 3 || argc == 4) {
        const size_t n = atoi(argv[1]);
        assert(n > 0); // Length of the array must be > 0.
        int64 sequence[n];
        char *const parse_seq = argv[2];
        char *parse_ptr = parse_seq;
        size_t data_count = 1;
        for(size_t i = 0, j = 0; (j < n && parse_seq[i] != '\0'); ++i) {
            if(parse_seq[i] == ' ') {
                ++data_count;
                sequence[j++] = atoi(parse_ptr);
                parse_ptr = parse_seq + i + 1;
            }
        }
        if((strlen(parse_seq) > 0) && (data_count == n)) {
            sequence[n - 1] = atoi(parse_ptr);
            parse_ptr = NULL;
            if((argc == 4) && ((!strcmp(argv[3],"-reverse")) || (!strcmp(argv[3],"-r")))) {
                reverse_order = true;
            }
            if(reverse_order) {
                if(!check_sorted(sequence,n,false)) {
                    merge_sort_integer_sequence(sequence,0,(n - 1));
                }
            } else {
                if(!check_sorted(sequence,n,true)) {
                    merge_sort_integer_sequence(sequence,0,(n - 1));
                }
            }
            display_sequence(sequence,n);
        } else {
            if(data_count > n) {
                fprintf(stderr,"Line number: %u: n value is less than array length.\n", __LINE__);
                return EXIT_FAILURE;
            } else {
                fprintf(stderr,"Line number: %u: n value is more than array length.\n", __LINE__);
                return EXIT_FAILURE;
            }
        }
    } else {
        if(argc > 4) {
            fprintf(stderr,"Line number: %u: More than %u arguments are passed\n", __LINE__,argc);
        } else {
            fprintf(stderr,"Line number: %u: Insufficient Number of Arguments passed\n", __LINE__);
        }
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

static void display_sequence(const int64 *const data,const size_t n) {
    for(size_t i = 0; i < n; ++i) {
        printf("%lld ", data[i]);
    }
    printf("\n");
}

static const bool check_sorted(const int64 *const data,const size_t n,const bool order) {
    bool is_sorted = true;
    if(!order) {
        for(size_t i = 0; i < (n - 1); ++i) {
            if(data[i] < data[i + 1]) {
                is_sorted = false;
                break;
            }
        }
    } else {
        for(size_t i = 0; i < (n - 1); ++i) {
            if(data[i] > data[i + 1]) {
                is_sorted = false;
                break;
            }
        }
    }
    return is_sorted;
}

static void merge_sort_integer_sequence(int64 *const data,const size_t start,const size_t end) {
    if(start < end) {
        const size_t mid = ((end - start) >> 1) + start;
        merge_sort_integer_sequence(data,start,mid);
        merge_sort_integer_sequence(data,(mid + 1),end);
        merge_integer_data(data,start,mid,end,integer_comparator);
    }
}

static void merge_integer_data(int64 *const data,size_t start,size_t mid,size_t end,const bool (*comparator)(const void *,const void *)) {
    size_t left_size = (mid - start) + 1;
    int64 left_data[left_size];
    memmove(left_data,&data[start],(sizeof(int64) * left_size));
    size_t right_size = end - mid;
    int64 right_data[right_size];
    memmove(right_data,&data[mid + 1],(sizeof(int64) * right_size));
    for(size_t k = start, i = 0, j = 0; k <= end; ++k) {
        if(i == left_size) {
            data[k] = right_data[j++];
        } else if(j == right_size) {
            data[k] = left_data[i++];
        } else if(comparator(&left_data[i],&right_data[j])) {
            if(reverse_order) {
                data[k] = right_data[j++];
            } else {
                data[k] = left_data[i++];
            }
        } else {
            if(reverse_order) {
                data[k] = left_data[i++];
            } else {
                data[k] = right_data[j++];
            }
        }
    }
}

static const bool integer_comparator(const void *a,const void *b) {
    return (*(int64*)a) < (*(int64*)b);
}

Does the above code need more improvement in terms of security, loopholes, buffer-overflows, maintainability, readability, robustness, to be production-ready?
How can I use conditional macros/directives like #if #elif #else #defined #ifndef #ifdef to make the code more compact if possible?

\$\endgroup\$
  • \$\begingroup\$ regarding: ./merge_sort 3 "1 2 0" -reverse 2 1 0 This does not match the criteria in the question, Per the OPs question, the output should be: 0 2 1. Therefore, the posted code does not work. in codereview, the code has to work \$\endgroup\$ – user3629249 Dec 19 '19 at 5:02
  • \$\begingroup\$ @user3629249: I am slightly confused - why should the reverse order be "0 2 1"? When I went to school, the reverse order would be as the OP suggests. \$\endgroup\$ – AJD Dec 19 '19 at 5:14
  • \$\begingroup\$ @user3629249 I specifically mentioned in the problem the problem that when -reverse or r flag is passed then sort in descending order. \$\endgroup\$ – strikersps Dec 19 '19 at 5:18
  • \$\begingroup\$ I see, I missed the part about 'descending order' \$\endgroup\$ – user3629249 Dec 19 '19 at 5:21
  • 1
    \$\begingroup\$ Please see What to do when someone answers. I have rolled back Rev 3 → 2 \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Dec 19 '19 at 21:29
5
\$\begingroup\$

I agree with everything user3629249 posted. Some other possible improvements are:

Use getopt() or even getopt_long() if possible

These functions take care of parsing command line arguments for you, which not only makes your life easier (especially if you add more flags), but also gives the user of your application the same command line experience as they get with other programs.

In particular, long options should start with two dashes, so your program should either accept -r or --reverse, but not -reverse.

Try to avoid having to indent too much

Indentation is very helpful, but especially when having many nested if/for/while statements, it means you are indenting a lot. At some point this will hurt readability. You can avoid unnecessary indentation by restructuring your code a bit.

For example, as user3629249 has already mentioned, instead of having a lot of code inside of if(argc == 3 || argc == 4), first check if(argc < 3 || argc > 4), and exit early if that's the case. That will remove one indentation level from most of the code in main().

Use empty lines to make the structure of your code more clear

Use empty lines to make the different sections of your functions stand out. In particular, add empty lines right above and below (big) if-statements and for-loops.

Avoid forward declarions

At the start of your program you have forward declarations for functions you use later. However, these can all be avoided if you restructure the source code to have the ones that don't depend on any other functions go first, and then the ones that depend on those, and so on, and end with main(). The main reason for doing this is that you avoid having to repeat yourself, which prevents errors.

Don't use macros

You asked whether you can use macros to make the code more compact. Don't do this. Macros are hard to get right. If you want to make things more compact, it is usually a good idea to put commonly used pieces of code into a separate function, and then use regular function calls.

Add spaces after commas

You add spaces after semicolons, braces, and most operators, but for some reason you never put a space after a comma. Please add a space after commas as well, it makes the code more readable. For example, instead of:

merge_sort_integer_sequence(sequence,0,(n - 1));

Write:

merge_sort_integer_sequence(sequence, 0, n - 1);

Don't use assert() for validating user input

The assert() macro is meant for code quality control, and is typically only used for debug builds. By defining NDEBUG in production builds, assert() will turn into a no-op.

When checking the provided arguments, such as whether atoi(argv[1]) > 0, just use a regular if-statement, print an error message and exit with a non-zero return code. Otherwise, the program will either abort with an unhelpful message if the assert is still working, or it will cause a segmentation fault later on if it's compiled out.

Use memcpy() instead of memmove() where appropriate

Use memmove() in situations where the source and destination memory regions might actually overlap, otherwise use memcpy(). The latter might be faster, and sometimes the compiler might actually warn you when it detect you actually do erroneously try to use memcpy() of overlapping memory regions.

| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

Questionable use of assert()

Code below allows user input to kill the program abnormally - but only in a debug build. assert() is better to detect problems that should not be expected to occur - but might. Here the assert rapidly fails based on user input.

    const size_t n = atoi(argv[1]);
    assert(n > 0); // Length of the array must be > 0.
    int64 sequence[n];

Instead:

    size_t n = atoi(argv[1]); // recommend strtoll
    if (n <= 0) {
      fprintf(stderr, "Invalid positive number <%s>\n", argv[1]);
      return EXIT_FAILURE;
    } 

Help

When user input is invalid as in if(argc > ..., consider say how it is invalid and then post how to get "help". Of course, also detect a request for help in argv processing.

// Review this odd error message,  Did you want `__LINE__,4`
// fprintf(stderr,"Line number: %u: More than %u arguments are passed\n", __LINE__,argc);

#define HELP "Call with -h for help\n"
fprintf(stderr,"Line number: %u: More than %u arguments are passed\n" HELP, __LINE__, 4);

Think big

The VLA int64 sequence[n]; with a large enough n will certainly cause a stack overflow. Consider memory allocation instead.

Good use of const - sometimes

const int64 * is good to show code does not modify what data points to. This self-documents, allows for more usages and optimizations. const in const data and const size_t n is less valuable. Even though I find it unnecessary verbose and distracting, code to your group's coding stand concerning this.

// Example, many similar places in code.
static void display_sequence(const int64 *const data,const size_t n) {

Maintainable

C does not specify long long to be 64-bits. It is at least 64 bits.

typedef unsigned long long int64; 
typedef long long uint64;           // Why is uint64 a signed type? 

For maximum potential use, consider (u)intmax_t.

Minor nit: trailing space

display_sequence() ends output with a " \n". From time-to-time, output with unexpected spaces leads to issues. Consider removing trailing spaces. Example:

static void display_sequence(const int64 *data, size_t n) {
    const char *delimiter = "";
    for(size_t i = 0; i < n; ++i) {
        printf("%s%lld", delimiter, data[i]);
        delimiter = " ";  //  Maybe a command option would steer ", " here for a CSV
    }
    printf("\n");
}
| improve this answer | |
\$\endgroup\$
4
\$\begingroup\$

when compiling, always enable the warnings, then fix those warnings.

for gcc, at a minimum use:

gcc -c -Wall -Wextra -Wconversion -pedantic -std=gnu11 file.c -o file

compiling the posted code results in:

gcc    -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11  -c "untitled2.c"  

untitled2.c:12:8: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
 static const bool check_sorted(const int64 *const,const size_t,const bool);
        ^~~~~

untitled2.c:14:84: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
 static void merge_integer_data(int64 *const,const size_t,const size_t,const size_t,const bool (*comparator)(const void*,const void*));
                                                                                    ^~~~~

untitled2.c:16:8: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
 static const bool integer_comparator(const void*,const void*);
        ^~~~~

untitled2.c: In function ‘main’:
untitled2.c:20:26: warning: conversion to ‘size_t {aka const long unsigned int}’ from ‘int’ may change the sign of the result [-Wsign-conversion]
         const size_t n = atoi(argv[1]);
                          ^~~~

untitled2.c:29:33: warning: conversion to ‘int64 {aka long long unsigned int}’ from ‘int’ may change the sign of the result [-Wsign-conversion]
                 sequence[j++] = atoi(parse_ptr);
                                 ^~~~

untitled2.c:34:31: warning: conversion to ‘int64 {aka long long unsigned int}’ from ‘int’ may change the sign of the result [-Wsign-conversion]
             sequence[n - 1] = atoi(parse_ptr);
                               ^~~~
untitled2.c: At top level:
untitled2.c:76:8: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
 static const bool check_sorted(const int64 *const data,const size_t n,const bool order) {
        ^~~~~

untitled2.c:105:85: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
 static void merge_integer_data(int64 *const data,size_t start,size_t mid,size_t end,const bool (*comparator)(const void *,const void *)) {
                                                                                     ^~~~~

untitled2.c:133:8: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
 static const bool integer_comparator(const void *a,const void *b) {
        ^~~~~

Compilation finished successfully.

Note: the compiler output: "Compilation finished successfully." only means that the compiler implemented some work-around for each of the problems. It does NOT mean the resulting object file is correct

instead of calling: atoi() Suggest calling strtoll() (the man page gives the details on how to use the strtoll() function)

strongly suggest eliminating:

   } else {
    if(argc > 4) {
        fprintf(stderr,"Line number: %u: More than %u arguments are passed\n", __LINE__,argc);
    } else {
        fprintf(stderr,"Line number: %u: Insufficient Number of Arguments passed\n", __LINE__);
    }
    return EXIT_FAILURE;

and modifying this:

if(argc == 3 || argc == 4) {

to something similar to:

if( argc < 3 || argc > 4 )
{
    fprintf( stderr, "USAGE: %s dataCount dataArray [-r|-reverse]\n", argv[0] );
    exit( EXIT_FAILURE );
}

// implied else, correct number of command line arguments

The above tells the user what they should have entered on the command line.

there is no need for the first command line parameter. Rather suggest enclosing the data array in double quotes so it is treated as a single argument.

for ease of readability and understanding:

  1. follow the axiom: only one statement per line and (at most) one variable declaration per statement.
  2. separate code blocks: for if else while do...while switch case default via a single blank line.
  3. separate functions by 2 or 3 blank lines (be consistent)
  4. insert reasonable space: inside parens, inside braces, inside brackets, after commas, after semicolons, around C operators
  5. honor the right margin (usually column 72 or 80) for those with narrow monitors and for printing

most of the uses of the modifier: const are harming the program rather than helping. Suggest removing all the instances of const

EDIT: removed comment about 'assert.h'

regarding these statements:

typedef unsigned long long int64;
typedef long long uint64;

Much better to include the header file: ctype.h and use: uint64_t and int64_t BTW: those typedefs seem to have the definitions backwards

regarding:

parse_ptr = parse_seq + i + 1;

this is expecting that the values in the data array are always separated via a single space. That is not a safe assumption when working with user entered data. Suggest using something like strtok() to extract each data entry

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Oh I see, I mostly compile my program with the following flags: gcc - Wall -Werror -m64 -g filename.c -o filename \$\endgroup\$ – strikersps Dec 19 '19 at 5:29
  • \$\begingroup\$ As you mentioned, when compiling the program using gcc at minimum I should use the following flags gcc -c -Wall -Wextra -Wconversion -pedantic -std=gnu11 filename.c -o filename, Can you tell me in brief what each flag is for and is this the best way to generate the object file for the program or there are other ways. I read about the O3 or O2 flags which is use for optimisation but never used it. \$\endgroup\$ – strikersps Dec 19 '19 at 5:34
  • \$\begingroup\$ Suggest not using any optimization until after the code cleanly compiles. Rather than using -g suggest using: -ggdb3 so the maximum amount of information is available to the gdb debugger. \$\endgroup\$ – user3629249 Dec 19 '19 at 5:35
  • \$\begingroup\$ information on gcc options: gcc options \$\endgroup\$ – user3629249 Dec 19 '19 at 5:38
  • \$\begingroup\$ "nothing in the header file assert.h is being used by the posted code" is unclear as code has assert(n > 0); \$\endgroup\$ – chux - Reinstate Monica Dec 19 '19 at 12:18
2
\$\begingroup\$

Use <stdint.h>

If your system has it, use it. I bet your long is probably the same as long long, but to be sure of what you're using, use intN_t.

Use strtol safely:

(This part is about OP's own answer; not the question itself, which doesn't use strtol())

Using strtol is good because it can be safer, but it is a pain in the ass to use it safely.

Here is an example of safe usage of strtol. It encapsulates strtol into a function which handles all error conditions and let's you forget about it. It also adds some useful things. It is based on this code. The code is the following:

#include <ctype.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>


int     strtol_status   (const char *restrict str,
                         const char *restrict endptr,
                         int errno_after, int errno_before)
{
        int status;

        status  = 0;

        if (str == endptr) {
                status  = -ECANCELED;
                goto out;
        }

        while (isspace((unsigned char)*endptr))
                endptr++;
        if (*endptr) {
                status  = ENOTSUP;
                goto out;
        }

        /* EINVAL or ERANGE */
        if (errno_after) {
                status  = errno_after;
                goto out;
        }
out:
        if (!errno)
                errno   = errno_before;

        return  status;
}

int     strtol_s    (long *restrict num,
                     const char *restrict str,
                     int base, ptrdiff_t *restrict read)
{
        const int   errno_before = errno;
        char        *endptr;

        errno   = 0;
        *num    = strtol(str, &endptr, base);
        if (read)
                *read   = endptr - str;

        return  strtol_status(str, endptr, errno, errno_before);
}

Usage:

if (strtol_s(&n, optarg, 10, NULL)) {
        /* something went wrong */
}
if(n < 1) {
        fprintf(stderr, "Line number: %u: Invalid sequence size. Should be a positive integer.\n", __LINE__);
        fprintf(stderr, "Refer to -h or --help for more information.\n");
        exit(EXIT_FAILURE);
}
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

First of all thanks to all the authors @user3629249 @G.Sliepen @chux-Reinstate Monica who provided valuable insights to the source-code of a command-line based merge-sort algorithm described in the Problem Description..

I have made important changes to the source code in order to make it better in terms of security, loopholes, buffer-overflows, maintainability, readability, robustness, and to be production-ready.

Changes Made:

  • Changed the format in which a user can give command-line arguments.
  • Used getopt_long() for better processing of command-line arguments with support for long and short options.
  • Added a help menu which you can access by passing -h or --help flag while running the program through command-line.
    E.g. /merge_sort -h
  • Used strtol() and strtoll() for coversion of string into long and long long.
  • Replaced memmove() to memcpy() in the merge_integer_data() function for speed-up.
  • Changed the function static void display_sequence(const int64 *const,const size_t) to static void display_sequence(const int64 *const,const size_t,const char*).
  • Used macros MEMORY_ALLOCATION_FAILED_ERROR(variable,x) and INVALID_COMMAND_LINE_OPTION_ERROR to print the error messages.
  • Removed -n --size option.

Format to supply command-line arguments:

Command-Line Based Merge-Sort-Program:
-d,--data: To specify the integer sequence to sort.
-r,--reverse: To sort the sequence in decreasing order.
-h,--help: Call with -h or --help for help.
-n,--size | -d,--data are options which take one argument which is mandatory.
-r,--reverse | -h,--help are options which take no arguments.

Source-Code:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#include<getopt.h>
#include<assert.h>

#define MEMORY_ALLOCATION_FAILED_ERROR(variable,x) fprintf(stderr,"Line number: %u: Not able to allocate <%lu> bytes of memory to "#variable".\n", __LINE__,x)
#define INVALID_COMMAND_LINE_OPTION_ERROR fprintf(stderr,"Line number: %u: Invalid Arguments Passed.Refer to -h or --help for more information.\n", __LINE__)

bool reverse_order = false;

typedef unsigned long long uint64;
typedef long long int64;

static bool check_sorted(const int64 *const,const size_t,const bool);
static void merge_sort_integer_sequence(int64 *const,const size_t,const size_t);
static void merge_integer_data(int64 *const,const size_t,const size_t,const size_t,bool (*comparator)(const void*,const void*));
static void display_sequence(const int64 *const,const size_t,const char*);
static size_t compute_length_sequence(const char*);
static int64* preprocess_input_sequence(char *const,const size_t,bool *const,bool *const);
static bool check_int64(char *const);
static bool integer_comparator(const void*,const void*);
static void print_help_msg(void);

int main(int argc,char *const argv[]) {
    size_t data_size;
    int64 *sequence = NULL;
    bool invalid_data, size_mismatch;
    invalid_data = size_mismatch = false;
    const struct option long_options[] = {
        {"data", required_argument, NULL, 'd'},
        {"reverse", no_argument, NULL, 'r'},
        {"help", no_argument, NULL, 'h'}
    };
    bool dflag, hflag;
    dflag = hflag = false;
    while(true) {
        int option_index = 0;
        int option = getopt_long(argc,argv,"d:rh",long_options,&option_index);
        if(option == -1) {
            break;
        }
        switch(option) {
            case 'd':
                dflag = true;
                data_size = compute_length_sequence(optarg);
                sequence = preprocess_input_sequence(optarg,data_size,&invalid_data,&size_mismatch);
                break;
            case 'r':
                reverse_order = true;
                break;
            case 'h':
                hflag = true;
                break;
            default:
                break;
        }
    }
    if(dflag) {
        if(!sequence) {
            if(invalid_data) {
                fprintf(stderr,"Line number: %u: Data in the sequence is invalid. Refer to -h or --help for more information.\n", __LINE__);
            } else if(size_mismatch) {
                fprintf(stderr,"Line number: %u: Size and total number of integers in sequence are not same. Refer to -h or --help for more information\n", __LINE__);
            }
            exit(0);
        }
    } else if(!dflag && hflag) {
        print_help_msg();
        exit(0);
    } else {
        INVALID_COMMAND_LINE_OPTION_ERROR;
        exit(0);
    }
    if(!check_sorted(sequence,data_size,reverse_order)) {
        merge_sort_integer_sequence(sequence, 0, data_size - 1);
    }
    display_sequence(sequence, data_size, " ");
    free(sequence);
    return EXIT_SUCCESS;
}

static size_t compute_length_sequence(const char *data) {
    size_t length = 1;
    for(unsigned int i = 0; '\0' != data[i]; ++i) {
        if(' ' == data[i]) {
            ++length;
        }
    }
    return length;
}

static int64* preprocess_input_sequence(char *const data,const size_t data_size,bool *const invalid_data,bool *const size_mismatch) {
    int64 *sequence = calloc(data_size, sizeof(int64));

    if(sequence) {
        char *buffer = data, *extracted_data;
        extracted_data = strtok_r(buffer, " ", &buffer);
        if(!check_int64(extracted_data)) {
            free(sequence);
            sequence = NULL;
            *size_mismatch = true;
        } else {
            sequence[0] = strtoll(extracted_data,NULL,10);
            size_t j = 1;
            while((extracted_data = strtok_r(NULL," ",&buffer))) {
                if(!check_int64(extracted_data)) {
                    free(sequence);
                    sequence = NULL;
                    *invalid_data = true;
                    break;
                } else {
                    sequence[j++] = strtoll(extracted_data,NULL,10);
                }
            }
        }
    } else {
        MEMORY_ALLOCATION_FAILED_ERROR(sequence, (data_size * sizeof(int64)));
    }
    return sequence;
}

static bool check_int64(char *const data) {
    bool is_int64 = true;
    if(!data) {
        is_int64 = false;
    } else {
        for(size_t i = 0; data[i] != '\0'; ++i) {
            if('-' == data[0]) {
                if('-' == data[1]) {
                    is_int64 = false;
                    break;
                }
            } else if(!('0' <= data[i] && '9' >= data[i])) {
                is_int64 = false;
                break;
            }
        }
    }
    return is_int64;
}

static void display_sequence(const int64 *const data,const size_t n,const char *delimiter) {
    printf("%lld", data[0]);
    for(size_t i = 1; i < n; ++i) {
        printf("%s%lld", delimiter,data[i]);
    }
    printf("\n");
}

static bool check_sorted(const int64 *const data,const size_t n,const bool order) {
    bool is_sorted = true;

    if(!order) {
        for(size_t i = 0; i < (n - 1); ++i) {
            if(data[i] > data[i + 1]) {
                is_sorted = false;
                break;
            }
        }
    } else {
        for(size_t i = 0; i < (n - 1); ++i) {
            if(data[i] < data[i + 1]) {
                is_sorted = false;
                break;
            }
        }
    }
    return is_sorted;
}

static void merge_sort_integer_sequence(int64 *const data,const size_t start,const size_t end) {
    if(start < end) {
        const size_t mid = ((end - start) >> 1) + start;
        merge_sort_integer_sequence(data,start,mid);
        merge_sort_integer_sequence(data,(mid + 1),end);
        merge_integer_data(data,start,mid,end,integer_comparator);
    }
}

static void merge_integer_data(int64 *const data,size_t start,size_t mid,size_t end,bool (*comparator)(const void *,const void *)) {
    size_t left_size = (mid - start) + 1;
    int64 left_data[left_size];
    memcpy(left_data,&data[start],(sizeof(int64) * left_size));
    size_t right_size = end - mid;
    int64 right_data[right_size];
    memcpy(right_data,&data[mid + 1],(sizeof(int64) * right_size));

    for(size_t k = start, i = 0, j = 0; k <= end; ++k) {
        if(i == left_size) {
            data[k] = right_data[j++];
        } else if(j == right_size) {
            data[k] = left_data[i++];
        } else if(comparator(&left_data[i],&right_data[j])) {
            if(reverse_order) {
                data[k] = right_data[j++];
            } else {
                data[k] = left_data[i++];
            }
        } else {
            if(reverse_order) {
                data[k] = left_data[i++];
            } else {
                data[k] = right_data[j++];
            }
        }
    }
}

static bool integer_comparator(const void *a,const void *b) {
    return (*(int64*)a) < (*(int64*)b);
}

static void print_help_msg(void) {
    printf("\nCommand-Line Based Merge-Sort-Program:\n"
    "-d,--data: To specify the integer sequence to sort.\n"
    "-r,--reverse: To sort the sequence in decreasing order.\n"
    "-h,--help: Call with -h or --help for help.\n"
    "-n,--size | -d,--data are options which take one argument which is mandatory.\n"
    "-r,--reverse | -h,--help are options which take no arguments.\n\n");
}

Before running the program do see the help message which will give you a fair idea about how the program works.

Output of Program:

./merge_sort --help
Command-Line Based Merge-Sort-Program:
-d,--data: To specify the integer sequence to sort.
-r,--reverse: To sort the sequence in decreasing order.
-h,--help: Call with -h or --help for help.
-n,--size | -d,--data are options which take one argument which is mandatory.
-r,--reverse | -h,--help are options which take no arguments.

./merge_sort -d "34 2 4 11 3"
2 3 4 11 34

./merge_sort -d "34 2 4 11 3" -r
34 11 4 3 2

If you think there is a possibility of significant changes that will make the code better in terms of security, loopholes, buffer-overflows, maintainability, readability, robustness, and to be production-ready, then let me know.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.