My own functions for working with files I wrote the functions for opening a file, getting a file size and getting a file contents. I would welcome any suggestions to improve my code.

You might think I'm over-commenting the code. But I do it mostly just for the visual separation of the code parts. I hope to know your opinion about it.

P. S. English isn't my native language, but I try to write all code comments in English. So I would welcome any corrections also!

/*
===============
open_file
===============
*/
FILE *open_file(char const * restrict name, char const * restrict mode) {
    /* Debug: checking the input arguments. */
    assert(name != NULL);
    assert(mode != NULL);

    /* Opening the file and checking if an error occurs. */
    FILE *file = fopen(name, mode);
    if (file == NULL) {
        LOG("Error: the file \"%s\" couldn't be opened (in the \"%s\" mode). "
            "The reason: \"%s\".\n", name, mode, strerror(errno));
    }

    return file;
}

/*
===============
get_file_size
===============
*/
long get_file_size(FILE *file) {
    /* Debug: checking the input arguments. */
    assert(file != NULL);

    /* Setting the file position to the ending of the file. */
    fseek(file, 0, SEEK_END);

    /* Getting the size of the file. */ 
    const long size = ftell(file);
    if (size == -1) {
        LOG("Error: can't get the current value of the file position. The "
            "reason: \"%s\".\n", strerror(errno));
    }

    /* Setting the file position to the beginning of the file. */
    rewind(file);
    return size;
}

/*
===============
get_file_contents
===============
*/
char *get_file_contents(FILE *file) {
    /* Debug: checking the input arguments. */
    assert(file != NULL);

    /* Getting the size of the file. */
    long const size = get_file_size(file);
    if (size == -1)
        return NULL;

    /* Allocating memory for the contents of the file. */
    char * const contents = calloc(size + 1, 1);
    if (contents == NULL) {
        LOG("Error: can't allocate %ld bytes for a contents of a file. "
            "The reason: \"%s\".\n", size, strerror(errno));
    }

    /* Reading the contents of the file and checking if an error occurs. */
    size_t const read_size = fread(contents, 1, size, file);
    if (read_size &lt; (size_t) size) {
        LOG("Warning: the size of elements successfully read (%zu bytes) is "
            "less than the size of the file (%ld bytes). It also may occurs "
            "if the file uses the CR+LF line endings (it is the Microsoft "
            "Windows system usually).\n", read_size, size);
    }

    return contents;
}
I must admit that I didn't went through all of your code and have not even compiled it. Some ideas for improvement:

open_file:

I would change your function from

FILE *open_file(char const * restrict name, char const * restrict mode)


to something like this

int open_file(const char *name, const char *modes, FILE **file)


I prefer using a return value that indicates a successful function execution for functions that can fail. By using this you don't have to remember that the function is not successful if the return values is NULL. You will always get a FAILURE return for each function. Inside the function you could use something like this:

int open_file(const char *name, const char *modes, FILE **file) {
    if (name == NULL) { goto fail; }
    if (modes == NULL) { goto fail; }
    *file = fopen(name, modes);
    if (*file == NULL) { goto fail; }
    return SUCCESS;
fail:
    return FAILURE;
}


Even if someone tells you using goto would be bad. I absolutely love it for this situation. The main reason against goto is, that it can create code that is hard to understand. But in this case to me there is no clearer version due to the usage of goto.

If that is still not readable enough for you, you can create macros for error-checking. In these macros you could also integrate printing or logging error messages. For example for developement you could switch on printing error messages and for production code compile it without any error messages if the user shall not see them.

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define SUCCESS EXIT_SUCCESS
#define FAILURE EXIT_FAILURE

#define ERRTEST_ARG_NULL(arg, tag) \
    do { \
        if (!(arg)) { \
            printf("%s:%d: In function '%s': error: %s: ERROR_ARG_NULL\n", __FILE__, __LINE__, __func__, tag); \
            goto fail; \
        } \
    } while (0) 

int open_file(const char *name, const char *modes, FILE **file) {
    ERRTEST_ARG_NULL(name, "name");
    ERRTEST_ARG_NULL(modes, "modes");
    *file = fopen(name, modes);
    ERRTEST_ARG_NULL(*file, "*file");
    return SUCCESS;
fail:
    return FAILURE;
}

int main(void) {
    FILE *file;
    printf("%d\n", open_file("C:/pathToFile/file.txt", "rb", &amp;file));
    getchar();
    return 0;
}


assert

I would go with the above solution for error-checking because you are able to continue with you program even if an error occured. Think of an user that tries to open a file but uses a wrong file name. Using 'assert' your program would stop working from the first problem.

fread

'fread' will fail for loading bigger files than 1GB (at least on my machine). You can still use it for bigger files by reading them sequentially in chunks with sizes smaller than 1GB and then adding them at the right spot in your allocated array.

+1 for introducing restrict to me. That was new to me.