The Devil is in the Details
You're not incompetent. You just lack a bit of attention to details and aren't demanding enough.
Let the Compiler be your Guide
You say you're a C programmer. Let the compiler be demanding of you, and compile (for gcc) with at least:
-Wall -Werror -ansi -pendantic -Wstrict-prototypes.
... and Invite its Helpers to the Party!
Make sure to run some code quality tools on your code on top of that. Use the plethora of linters, static code analyzers and others that you have at hand to try to detect possible bugs. Use Valgrind.
Add Constraints to the Mix
Force yourself to write code that targets different operating systems and architectures. Some are way less forgiving than others when it comes to screwing up your memory allocations, and you'll see various results in the way they handle their threading models. As you'll work through these differences, you'll learn to iron out problematic points.
Develop a Keen Eye for Testing
That's not everything though, and they cannot guess what you are trying to do. So get into the habit of thinking of edge cases and good tests for you code. Ultimately, these things will come naturally to you, and writing more robust code becomes a habit. You just don't have the habit yet.
Fail Early, and Fail Hard
Write code that really fails. Meaning that when someone's wrong, it should really fail hard and crash, so the issue is notable and needs to be addressed, instead of weaseling your way out by trying to handle errors and staying alive for as long as you can.
There are different schools of thought, and both have advantages. Defensive Programming is great to write software that is somewhat fault-tolerant and doesn't crash by compromising security of the platform. However sometimes you want to use a design-by-contract approach and enforce these contracts (and the developers know when things would go wrong early on in the dev cycle).
Practice, Practice, Practice
Of course, all of the above is already about that. But maybe what you lack is an incentive to do it and do something great. You mention having issues as soon as things get complex. Then get out of your comfort zone and build something complex. What do you miss in your virtual life, on a regular basis? If you can think of something, can you build it? Start simple, and build something bigger and bigger as you go. See it through, and improve its overall design as you go. Then maybe throw it out and do a full rewrite: now that you have a better insight, you should be able to rebuild this complex system faster, and with a better design not suffering from its initial flaws.