Skip to content

Instantly share code, notes, and snippets.

@REPOmAN2v2
Last active August 29, 2015 14:01
Show Gist options
  • Save REPOmAN2v2/4bb1df1553cad7a18126 to your computer and use it in GitHub Desktop.
Save REPOmAN2v2/4bb1df1553cad7a18126 to your computer and use it in GitHub Desktop.
C array NULL sorting program
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
const void * const t[] = { malloc(1), NULL, malloc(1), NULL, malloc(1)};
const size_t size = sizeof t / sizeof *t;
const void ** it = (const void **) t;
const void * const * const end = (const void **) t + size;
for (unsigned i = 0; i < size; ++i)
printf("%p,", t[i]);
putchar('\n');
while (it < end)
{
if (NULL == *it)
{
void const **p = it + 1;
while (p < end && NULL == *p)
++p;
if (p != end)
*it = *p, *p = NULL;
}
++it;
}
for (unsigned i = 0; i < size; ++i)
printf("%p,", t[i]);
putchar('\n');
}
/* About that bit of code:
* malloc is only used because it returns a pointer on void, so it seemed like a reasonable way to fill a void* table.
I could also have hardcoded values (0xdeadbeef, for example), but then I would have had to recast in (void *) every
single one of them. That would not have been cool. Note that it would have compiled without the casts, but I like code
that does not warn, even with -Weverything.
* This problem, and, by extension, the code, is just a simple array traversal, with some swaps. Have you ever written
a Bubble Sort? It should be similar.
* Basically, the const qualifier is useless. I just like keeping my types as strict as possible (for example, the
choice of an unsigned in my for loops), because that tends to make the program "safer".
const void * const t[] = { malloc(1), NULL, malloc(1), NULL, malloc(1)};
This builds an array of pointers, of size 7. The elements initialized by malloc(1) are non-NULL pointers, there others
obviously are. The "void *[]" is a void pointer array. The first const means that these pointers point to memory that
cannot be modifier; the second const means the pointer itself cannot be modifier: it can not be changed to point to
somewhere else.
const size_t size = sizeof t / sizeof *t;
Because this is not a (null-terminated) string, I need to keep the array's length somewhere in order not to overextend
when performing my traversal. Again, this variable will not change, so it is marked const. Its type is size_t, which is
basically an unsigned. The name is more explicite as to its nature as a type size.
I did not harcode the size to be 7, so the code is more flexible: I can just ad dor remove values from the array's
initialization, and the size will update automagically. How do I calculate it? The sizeof keyword gives the size of the
following type, in bytes. An array of 7 pointers has a size of 7 times the size of a pointer. So divide it by the size
of a pointer.
How does one iterate over an array? There are two ways:
* Use the index subscript notation, t[i], with a loop and a counter;
* Use iterators.
Depending on your algorithmic background, and your habits, one way might be more intuitive than the other. I use the
second one, here.
const void ** it = (const void **) t;
You just have to know that t[0], the first element of the array, can also be referred to as *t. Also, you should know
that t[i] is the same as *(t + i): it's the element at the addresse which is offset by i from the start of the array.
Therefore, *it is the first element of the array. Once I will have incremented this pointer, it will be pointing on the
second element of the array and so on, until the end. So, where is the end exactly? It is size positions after the start:
const void * const * const end = (const void **) t + size;
I had to cast t to (const void**), because it used to be a (const void[7]*), which isn't exactly the same (but you
shouldn't care, for now). The end iterator is a pointer, which will not change, on an other pointer (the elements of the
array *are* pointers, in this case), which also will not change, and pointing to memory that will not change; hence the
shitload of consts. Also, be noted that 'end' pointer *after* the last element, so we must pay extra attention not to
dereference it carelessly.
for (unsigned i = 0; i < size; ++i)
printf("%p,", t[i]);
putchar('\n');
This is not really a part of the program, it's just some output to demonstrate it. This time, I iterate over it with a
way which you may be more familiar with.
"%p," is the format string for pointers (hexadecimal output, or "(nil)" for null pointers), followed by a comma.
putchar() outputs a single character, in this case the line feed.
I output each pointer of the array, separated by commas, and terminating the whole with a newline.
We have seen the declarations, and the output.
Now, we get to the important stuff, the main loop.
while (it < end)
{
// stuff where *it is the n-th element of the array
++it;
}
The problem at hand is: for each element of the array, if it is NULL, replace it with the next non-NULL element.
So first, lets test the current element. If it is not NULL, nothing to do here, proceed to the next one. Hence,
everything that follows is enclosed in a conditional statement:
if (NULL == *it)
{
// more stuff
}
Note that often beginners, and more advanced people, will write comparisions the other way around. The logic behind
this order is that in case of a typo, and a = is missing, there will be a compilation error: no value can be affected
to a constant such as NULL. In the other case, it would have become *it = NULL, an assignment, which the compiler
would have accepted. Of course, since I work with -Weverything, and pay attention and address any warning I see, and
since nowadays compiler always warn on such a mistake, it would not have been a problem. But habits die hard,
especially when they can be considered good practice.
So, lets say the current element is NULL. What do we do now? We search for the next non-NULL one. When we will have
found it, we will swap them.
void const **p = it + 1;
We will find that next element by iterating in a similar fashion. p is our new iterator. We start it on the next
element (because we don't want to swap with ourselves, do we?) (Note: since the current element is null, and we
are looking for a non-null one, omitting the + 1 would not have had any impact, but it makes sense to have it.)
while (p < end && NULL == *p)
++p;
This is a loop that does nothing. It moves p towards the end, stopping either when the end is reached, or when a
non-null is reached.
if (p != end)
So which one was it? Did we stop because we found a suitable replacement? Or was it just the end of the array
(a failure)? If it was a failure, there is nothing to be done. TBH, the loop will go on to the next iterations
in my code, which is not great, because the are guaranteed to be useless. I should have done
if (p == end)
break;
else
instead, but meh.
So, we have found our replacement! Lets swap!
*it = *p, *p = NULL;
Which, of course, is the same as:
*it = *p;
*p = NULL;
*it, which was NULL, is replaced by our non-null value.
The non-null value becomes null in our element's stead. */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment