#### Review
For code written by one who is learning, it looks pretty good.<br>
Layout is good, and variable and function names easy to understand.<br>
A good start!
<hr>

#### 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.)
<hr>

#### 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)](https://godbolt.org/z/c1oYo5aGj) 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.)
<hr>

#### 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...
<hr>

#### 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."