Review
For code written by one who is learning, it looks pretty good.
Layout is good, and variable and function names easy to understand.
A good start!
Dynamic allocation
As previously noted in my comment, initial entry of a "task" allocates sufficient heap storage for that string. If the user then "edits" the text changing (for example) "homework"
to "do math homework"
, the longer entry will overrun the smaller buffer initially allocated. Oops... One straightforward solution would be to remove the edit()
function thereby forcing the user to delete()
the old version and add()
the new version. (Less code is better code, and this feels like a minor inconvenience to impose on the user. A "power user" would know how to employ the OS clipboard to their advantage.)
Double free()
Posit: The user enters one or more tasks on the list, then uses complete
or delete
to shrink the list. The code correctly shifts all the subsequent pointers toward the start of the array. This duplicates the pointer at the end of the active pointers. (There are now two array elements pointing to the same heap address.) When the user exits
the program, all pointers are free()
'd, resulting in a double free error.
After stripping out the "Windows" Sleep()
, to demonstrate this bug, I've posted the OP's code here (godbolt) and added one line to the function delete()
. This line (also required in complete()
) fixes the problem. The OP can comment-out this one line to see the result. (Why this was not revealed during the OP's testing remains a mystery.)
DRY (Don't Repeat Yourself)
This small program exhibits several repeats of a few lines of code (eg: getting a "serial number" from the user.) Although it seems like more work, consolidating (or avoiding) these repetitions will, in the end, prove to be more efficient. For example, the OP uses braces, even around a single statement, to highlight the body of if()
and for()
. The single, highly-visible comma after the several instances of while( ... )
is also a single statement body, but where are the braces? Does one find and fix each instance (increasing maintenance effort)? Don't repeat yourself...
Design comment (time allocation)
The only difference between delete()
and complete()
is that the latter increments and reports a counter. My opinion is that the user would prefer the coder had spent more time on code confirming user's entry before deleting a task from the list. If the user hastily & accidentally types 8 (Enter)
instead of 5 (Enter)
, task #8 (whatever it was) is now no longer on the list... What was it again? Was it important?? Will I be fired for not doing it???
FWIW: The global int completedTask = 0;
is only used within complete()
. This variable could/should be moved inside that function and declared as static
. Strive to limit the scope of variables...
Strive to design & write correct, clean code that does what's needed. Avoid writing code by copy/paste/adapt, motivated by simply "writing more lines of code."