I believe that for skillsets that can properly be called "crafts" the most effective approach to learning is, generally, mentorship. Ideally, mentorship is a relationship between a more-skilled person and a less-skilled person, where the mentor assesses the skills of the protege (or "flunky") by observation and assigns tasks to the flunky that will stretch his or her skills and, at the same time, actually accomplish useful things for the mentor. In such an ideal mentorship relationship, the mentor is there to answer questions when appropriate, and to help the flunky figure out where to find the answers to particular questions independently the rest of the time. Early on, the relationship should almost certainly involve a lot more answering than help with independently finding answers, though that balance should change with reasonable alacrity.
Programming, software development, or whatever you want to call it, is one of these crafts. I am not here to debate the proper term for some specific set of skills compared to another where there is significant overlap in the meanings of these terms.
Some specific crafts are supported by excellent instructional resources that the lone autodidact can use to fill in the gap left by the lack of a mentor quite well, though obviously not perfectly. In particular, some programming languages are blessed by the existence of incredible resources like this, though finding those resources can sometimes be difficult.
Perl and Ruby are both quite well-supported in this way, minus a little bit of initial familiarity with programming concepts and early guidance on choosing a computing environment suited to learning such languages with as much facility as reasonably possible. Scheme is also well-supported, but only if you have a strong math background. Note that even the best cases tend to fail in terms of specific parts of the complete path through learning to an independent level of competence that can be provided by a proper mentorship.
There was a time when getting a programming job was often a matter of being accepted for mentorship, where it was assumed that people coming in should be evaluated for their skills and receive mentorship from old hands who can help them learn new things that will make them better programmers. These days the most common case is an assumption that the new hire basically knows everything except the local codebase and a couple of tools for which the new hire might need twenty minutes' basic familiarization, plus (if the new hire is lucky) a printout of workflow assumptions. As a result, college graduates are sought for their presumed skills then cut loose to sink or swim, and experienced daycoders are sought for their presumed useful experience that will hopefully help them not screw up too badly. Occasionally, rockstars are sought to show up and work wonders by day three.
. . . which means that the best case scenario, where someone with great potential (regardless of prior education or experience) is brought on board and helped to exceed expectations to ultimately become one of the best programmers they have ever met, a credit and asset to the team in every way, is explicitly precluded by modern standard practice. This would also be the best case scenario for the new hire, who would learn new things and get help achieving stunning success in advancing skills. Such relationships, born of mentorship, tend to be strong and loyal relationships, whereas "sink or swim" college graduate hires, daycoder acquisitions, and rockstars stopping through on their World Tour will all tend to move on the moment they decide they've found something better for their personal needs, perhaps leaving the previous employer in the lurch when the employee vanishes with only two weeks' notice (if the employer is lucky) during a critical time. It is, after all, usually the critical times when things get bad enough to make someone want to move on.
Good mentoring relationships form strong bonds, produce excellent programmers, and end up being a winning situation for everyone involved. My observations lead me to believe that as the software industry has "matured" (that is, fallen into a bureaucratic, corporate-dominated rut, in many respects), the frequency of mentoring has plummeted to the point where there's no reasonable expectation that someone can say "I want to learn how to be an effective programmer in popular language $foo!" and reasonably expect to find a mentor.
I've done a little C and C++ here and there over the years. I've never really achieved what I would call an independent level of competence, unfortunately. It suddenly occurs to me that I don't even know how to "shell out" in C, in a portable manner -- that is, execute an external program via the shell, as done with backticks or the
system() function in many high-level dynamic languages -- which would be pretty handy for figuring out how to use the Unix environment as the basis for practicing simple C programming on a regular basis. I have started looking into it, but learning how to do so is a rat's maze of web searches, manpage explorations, questions on IRC, and experimentation that will probably not yield really good "best practices" results for a while.
It's really difficult to make any real progress learning more C in general. Nobody wants to hire someone he or she will then have to teach; they all want their new hires to be cookie-cutter experts. Nobody has time to properly mentor someone in free time, either. As far as I've been able to determine, C has no set of resources that substantially fill the hole left by the lack of a proper mentor; the gaps between learning resources are too great, and the resources too oriented to uses like snippet-use or formal education, neither of which really lends itself to the independent development of a well-rounded skillset. It seems like open source project participants who already know C (probably starting in college) all assume that people new to C can just jump in the deep end and magically absorb everything needed.
My C skills at the moment are such that if I really want to get something done I should just use Ruby -- but if I don't "really want to get something done" there's not really any good way to stretch my C coding skills. As a result, I'm at loose ends right now in trying to figure out how to make much progress expanding and practicing my C programming skills. What I really need at this point, I think, is a mentor.
Are you a competent C programmer who could make good use of a protege? If so, I wanna be your flunky. I might even take an unpaid internship (locally placed, or remote by way of Internet) with a smile, but those are only offered to people currently in college.
None of this should be taken to mean that I think hiring idiots is a good idea. People with strong potential for the job you ultimately want them to fill are ideal. Mentorship is helping someone fulfill potential -- not magically transforming someone congenitally incompetent into a rockstar. Certain skillsets may be valuable as measures of potential, but should perhaps not be regarded as the only measure of potential.
Formal education is increasingly a useless measure of potential. In today's world, the "formal" in formal education has become much more important than the "education" part, resulting in pointless bureaucracy as the distinguishing factor. People who get real educational value out of formal education in many cases do so only by way of autodidactism they guide by the general thrust of what it seems like a course of study might have originally been meant to cover. Sometimes, the tendency of some people to effectively gravitate toward better teachers helps. Some subject areas may be better represented by actual educators rather than formal bureaucrats, too, though I do not know how reliable that may be from one school to the next, or from one decade to the next.