LEN()
Function-like macro to return array length
This is a very common practice in C and C++. I recommend not doing this, because, as written, LEN()
only returns the correct value when called with a statically-defined array in the same scope it was defined in. Thus, its usefulness is a bit limited, and in my opinion, overshadowed by the the fact that it will likely silently return an incorrect value if it used on a pointer. For more discussion, as well as some suggested alternative definitions for the macro, see the follow Stack Overflow questions:
Of course, your code never actually uses LEN()
, so there is no problem here. This suggestion is just to make you aware of the issue.
Locality of reference / Define loop variables in loop initialization
Since C99 was standardized, it has been legal to declare variables anywhere in a block, rather than at the top of the block. Declaring variables close to where they are used is part of the concept of "locality of reference".
One example of locality of reference is defining loop iterator variables inside the loop initialzation:
for (int i = 0; i < len - 1 - num_sorted; i++) {
...
}
This makes it clear that the variable i
exists solely for the purpose of iterating through the loop. It doesn't have life outside the scope of the loop, and any attempt to reference it outside the loop will cause the compiler to emit an error.
Whitespace around C keywords
for
, if
, while
are not functions. By "nuzzling" them up to the parentheses that follow them, it looks like a function call. Separate the words from the parenthetical expressions that follow them:
- Do:
if (argc < 2) {
- Don't:
if(argc < 2) {
Use argv[0]
instead of hard-coding program name into error/help messages
When you check if the user has passed in enough parameters on the command line,
if(argc < 2) {
printf("USAGE: bubble_sort 7 3 5 4 0 1\n");
return 1;
}
Use argv[0]
instead of hard-coding the program name when you print the error message. Also, consider printing error messages to stderr
instead of the default stdout
by using fprintf()
("formatted print to file descriptor"). fprintf()
and stderr
is defined in <stdio.h>
, which you are already including, so the idiomatic program usage error message would be:
fprintf(stderr, "USAGE: %s 7 3 5 4 0 1\n", argv[0]);
Bug: Memory allocation / buffer overflow
You dynamically allocate memory for the integers passed on the command line (where len
is the number of integers given by the user):
int *nums = malloc(len + sizeof(int));
There is a bug here. If the user gives, say, 10 numbers on the command line, then malloc will allocate 10 + 4 = 14 bytes for your array (assuming sizeof (int)
is 4). You needed 40 bytes to store the 10 numbers. When you go to convert the numbers to int
and store them in the memory pointed to by nums
, you would walk off the end of the memory — a classic buffer overflow.
Solution: change the +
operator to *
:
int *nums = malloc(len * sizeof(int));
Bug: Memory leak (apparent)
Coming from Python, memory management in C is tricky. Once you allocate memory, never lose a pointer to that allocated memory. If you lose track of a pointer returned by malloc()
, then you effectively have a memory leak — that memory can never be returned to the heap (within the scope of your program), because you can't tell free()
the address of the memory region you are freeing up.
The problem you have is in main()
:
nums = bubble_sort(nums, len);
After nums
was assigned the memory address returned by malloc()
, you then potentially let nums
point to something else. Don't do that. Now, as it turns out, you don't actually have a memory leak. But it's really hard to tell. Even looking at the definition of bubble_sort()
, it's not apparent that there is no leak.
Really, there is no need for bubble_sort()
to return a value. Or even if it does, do not assign its return value to nums
. Better to just call it as such:
bubble_sort(nums, len);
O(n)
. You did not implement the best case scenario and thus it is alwaysO(n^2)
. \$\endgroup\$