I'm not so jaded as to think that deep language understanding isn't a useful or good thing, and I'd like to think I have some myself. However, deep language understanding is not what separates the great engineers from the good ones.
In an interview, I would much rather hear a person say something about a statically declared variable with no initialization being poor code to leave behind for the next person than some arcana about the standard.
I cant even remember how many times I've had to ask co-workers,
"So, that's what you think your code will do... what does the standard say?"
or,
"Why did you use int there when the API spec defines the parameter as taking size_t?"
or,
"You think that code in your inner loop will compile to one machine instruction. Have you actually looked at the compiler's output?"
and the looks/responses that I got. There seem to be very few programmers who understand the language & their compilers deeply enough, or even care to learn. These programmers are far more productive because they're not constantly guessing or assuming how their code might work.
There's a deeper problem here, though. If you're asking, "So, that's what you think your code will do... what does the standard say?" the problem isn't really that they don't know the standard, it's that they've written the code in a way where it's ambiguous what it does.
If I see
static int i;
in code, I'm going to change it to
static int i = 0;
even if I know that static integers are initialized to 0 in the standard, because I know that eventually someone will have to maintain the code who doesn't know that. I don't particularly care what the standard says there: the code shouldn't assume detailed understanding of the standard, which isn't realistically a valid assumption.
You nailed it. Standards are nice and all, when they're perfectly unambiguous, and when everyone follows them exactly. That is not the real world. Writing code clearly and explicitly has far more reward for the effort put into it than learning all the standards pedantically.
Don't get cute and always prefer to explicitly state in code what you get for free implicitly. The maintenance guy that follows behind you will be the one to espouse how clever you actually are. That's how I mentor junior engineers as a general rule.
The field has to accept some knowledge deficits if it's going to pretend to meet business demands. I thought the original post heavily implied that the 'dumber' programmer shouldn't be hired or have a job; are we going to turn away all potential C programmers until they've mastered everything that the 'smarter' programmer knew? I'd say that would heavily discourage new C programmers if they had to commit to mastering all of that before they could think about getting a position. That would lead to shortages, of course.
>In an interview, I would much rather hear a person say something about a statically declared variable with no initialization being poor code to leave behind for the next person than some arcana about the standard.
Why are you forcing those two choices? One could be well-versed with the arcana AS WELL AS point out that bit about statically declared variable ..
The problem IMO is most programmers cargo-cult/copy-paste/stackoverflow their way into programming jobs. No, that does not mean you never ask for help. No, that does not mean you never copy-paste. (Phew!)
Its sort of like when you're learning math. Great mathematicians can understand the theory and just apply it to whatever problem they come across. Its because they have a very solid foundation underneath them. They wield their knowledge like tools and can just build anything with those tools because they understand those tools very well. Most students however just learn the patterns of the problems. And once they know enough patterns they can solve problems which fit into one of those pre-understood patterns.
The people who are deeply knowledgeable about the language are good at knowing the boundaries of the language, knowing when you're using constructs that are not valid-syntax (which still compile), etc. The social aspect of 'good comments' , 'readable code' or 'maintainable code' is also important. You can have programmers that do both. Ofcource those programmers will never work for 'you' (not you, personally..) because most programming jobs are shitty and do not require programmers of that skill. You'll find them toiling away in anonymity, working in research labs, working on compiler optimizers or operating systems or some other domain with challenging technical problems.
> Great mathematicians can understand the theory and just apply it to whatever problem they come across.
As someone who's both a mathematician and a programmer, I think that analogy glosses over an important difference, which is also relevant to why many people refuse to memorize certain things.
Good mathematical theories are internally consistent in a very strong sense. When you're learning them, you feel like you're learning something eternal that could not possibly turn out any other way. If you forget half of calculus, you can reconstruct it from the other half, and the reconstruction will be unique.
The details of programming languages, like C and especially C++, are not like that. For many mathematicians who would otherwise make excellent programmers, the details of C will feel like something not worth memorizing, because they don't make sense - they are not an inevitable, provably unique solution to any problem.
To a mathematically inclined mind, programming concepts form a hierarchy depending on how "inevitable" and worth memorizing they are. Concepts like computability, lambda calculus or big-O complexity are near the top, syntaxes of specific languages are lower down, and API details are at the bottom. That also reflects the rates of change: math doesn't change, languages change every decade, and APIs change every year.
If I want to hire a programmer for the long term, as opposed to getting quick help on my current project, I will be mostly asking them about concepts from the top of the hierarchy, not what static means in C. Of course some people will disagree with me and demand detailed knowledge about static or templates or whatnot, while dismissing mathematical questions as "puzzles". Maybe that's also a valid style of programming, I'm not sure...
A thousand times this. Mathematicians are spoilt by maths riches of eternal problems. CS ought to be too, but so many are instead fascinated by the 10 year, 2 year, 1 week problems around the fashion of a programming tool.
No wonder everyone is always talking about the rapid pace of learning in high technology.
No, I too am talking about a hierarchy of internally consistent knowledge.
Have you implemented a compiler or a interpreter for a popular language? I could be wrong but I strongly suspect you haven't. If you have ever implemented a compiler you understand the language at a fundamental level and when stumped with a bug or a highly technical puzzle or what have you - you get that "aha" moment where its like "Ofcource ! How else could it be. This language feature must be using this construct because of X or Y reason and the compiler has to work this way because this part here doesnt allow for it to work any other way, etc ,etc and the optimizer has xyz amount of scratch registers so abc condition could never happen, and on and on."
My comment was talking about a stronger kind of consistency, maybe "inevitability" would be a better word for it. You could make many different tiny changes to the rules of C and still end up with a workable programming language. (Many people have done that, there are tons of languages derived from C.) You could say that the rules of C are "random" - after learning the first n rules, you cannot use logic to predict the n+1st. So learning the rules of C is more about memorization than logic. Math is different: when you learn a piece of math, you know exactly how it follows logically from the pieces you learned before, and you know it couldn't be any other way. That's why it feels more reasonable to me to inquire about math, rather than the details of C.
On a planet far away they have universities. In those universities they have many of the same subjects we teach here, such as math, biology, philosophy, history, computer science, psychology, and literature. But although the subjects of the classes are the same, the contents are different. They teach a history class, but their history is different from ours. So are their biology and literature. I’m not interested in subjects where their content is different from ours.
In some classes, though, the content is the same. They’re teaching the same physics class (assuming they’re at the same level of understanding we’re at) and the same math classes (with a different base). In computer science most of what they teach is different, but their theory of computation is surely the same. Their biology is different, but their teaching of evolution is the same. Their philosophy is vastly different (think how different Eastern and Western philosophies are on earth), but their philosophy of science is probably very similar.
Even if this point of view doesn't make sense to some people, I subscribe to it because it's very inspiring to me. YMMV.
>You could say that the rules of C are "random" - after learning the first n rules, you cannot use logic to predict the n+1st.
You're mis-applying things here. The language is not something to be theorized by itself. Like any (edit:spell) other body of knowledge, it has its own set of initial axioms and the 'C standard' which defines the language is simply a collection of rules other meta-knowledge which are based on those. In that sense it is internally consistent. Thats what I meant.
Yes, you could say that the C standard is a formal theory. It won't be a very interesting one to a mathematician though, because it's huge and describes only one thing, C. There are no special considerations to single out that theory from a huge pool of similar ones. It's like devoting your life to the study of one particular differential equation with a thousand terms, which was written by a committee. Why not the other equation which has a minus instead of a plus over there? Why not study differential equations in general?
Compare with a formal theory like PA, which fits on a page and describes the natural numbers. Proving a theorem about PA has implications everywhere, because natural numbers are everywhere. Or if we want to talk about computation, there are many formal theories with short descriptions that have much more to say about computation in general.
I am not against your POV ! I think we largely agree on most things here. I wanted to correct what I perceived to be a mischaracterization of my 'off the cuff' analogy.
Yeah, from your reply to patrickmclaren I understood the point you were trying to make, and I agree with you. Sorry for misunderstanding you so persistently.
Can you deduce the majority of the C language from these initial axioms?
You misunderstand the usage of consistency regarding mathematics. It is not the same. These rules which compose C are design decisions, and whether they are entirely artificial. Mathematical consistency arises from imposing axioms, following which there are no decisions to be made.
>Can you deduce the majority of the C language from these initial axioms?
Well, no. You can deduce A language but it might not be C.
I sense we're going down a fruitless path here. In my first post I was only giving an analogy of someone understanding math at a fundamental level to someone understanding a programming language at a fundamental level and how they could inform similar abilities/prowess in people when it comes to dealing with complex problems/etc.
My guess you apply your experience with some bizarre language (PHP?) to C, which you never learned. C and C++ are internally consistent in the same way a math theory is. Many programming languages are not, indeed.
> You could make many different tiny changes to the rules of C and still end up with a workable programming language. (Many people have done that, there are tons of languages derived from C.)
Coincidentally, same is Math. I give you Euclid's Fifth postulate as an example.
>You could say that the rules of C are "random" - after learning the first n rules, you cannot use logic to predict the n+1st.
This depends on how do you enumerate these rules. If you learn that auto variables are allocated on the stack it's easy to predict that you cannot return their addresses and there are not going to be default initializes for them.
Saying that C++ is internally consistent at all is a huge exaggeration. Actually I'd be very careful with stating it is more consistent than PHP. Both have many sharp-edges and in both there are plenty of features that feel "tacked on" and are non-orthogonal to each other. E.g. why I can't make a generic (template) function virtual or why I cannot overload some overloadable operators to have exactly same semantics as the builtin ones.
You can't make a member function template virtual because it's just a template, the function doesn't exist until it's statically instantiated. Instantiation can happen across units of compilation, making it impossible for the compiler to generate a vtable with all the instantiations within it. It's not a "tacked on" feature/limitation at all, and I don't see how any statically typed language that allowed function overloading could really work around that issue.
I'm not sure what you mean about the operator overloading thing.
This was a rhetoric question. I perfectly know why I can't do that in C++. C++ does not allow this because of particular generics implementation (done by static compile-time macro expansion), not because those two features are inherently contradictory. In fact, they are completely orthogonal and Java/Scala/C# have no problems with that, despite being statically typed.
As nly pointed out - the virtual template function just does not make sense if you understand what it is. As for the operators I'd guess you mean && and ||? You cannot make them shortcut because the user defined operators are just functions and functions in C++ do always evaluate their arguments.
Virtual generic functions do make sense and many languages allow them. The problem is C++ implements them by compile-time macro expansion (called templates), which renders them impossible.
Template types are also not really first-class members of the type system. The compiler can't really reason about them, it only type-checks them after expansion, when all the genericity disappears. This is completely different than the fail-fast approach in Haskell or even Java, where the code is type-checked at the generic level.
Don't get me wrong - templates is an awesome, extremely powerful feature of C++, but it doesn't play well with the rest of the language, and really feels tacked-on.
Sure, generic functions are awesome when you don't care about performance. This is why C++ does not have them. If you knew nothing about C++ and somebody told you "here, we have a language with zero run-time overhead, can you guess if we managed to implement generic functions?" what would you say?
Function templates are not generic functions. Same as template classes are not types. They play really well with the rest of C++ but I agree, not so well with the rest of Java or Haskell :)
This is the major problem people nowadays have with C++ - they learn some "practical" high-level language in school (usually riddled with random stuff, judging by how much people here are pissed with my mentioning of PHP) and try to think about C++ in concepts of such a language. Then blame C++ for not being their favorite language.
Don't get me wrong. I am quite happy with this making my skills very rare and expensive, giving me both job security and decent compensation. I also want to thank all the people spreading the "C++ is complex hodgepodge of completely random things so it's impossible to learn" idea :)
Generic functions like in Java/C# can be implemented efficiently and with zero overhead. There is nothing that prevents a JIT compiler to generate several versions of the code optimised for different types at runtime, exactly like C++ does at compile-time. Some VMs already do that to some extent in order e.g. to better inline calls for the generic argument. Still, those optimisations are in their infancy, but the situation improves with every year.
"Don't get me wrong. I am quite happy with this making my skills very rare and expensive, giving me both job security and decent compensation."
Eh? C++ skills are not any more expensive than decent Java/C#/Python/(put whatever mainstream language here) skills. Who told you that? This is supply-demand. There is lower supply of C++ coders, so some may think the prices should be higher, but in fact it is compensated by lower demand. C++ is also still being taught at schools, so those skills are not that rare as, say, Haskell, Erlang or Go.
How do you figure out the types at run time with zero overhead? I guess we have different concept of "zero".
As for the price of my skills - my paycheck told me that. And sure, it could be low demand (there are vacancies in my field, that had been open for years, but I don't really know what kind of demand there is for Python, could be decades for all I care). I am just happy for my compensation and if others have more expensive skills - I am happy for them too :)
Nothing is truly zero overhead. C++ templates are neither, because they create some overhead in code size which is obviously non-free (both space-wise and time-wise). Zero-overhead for me = optimized to the point that noone can notice. There is no problem for a VM to generate 10 versions of a tight loop, every one optimised for particular type, with a switch for the type right before entering the loop, or the whole method, or even a whole call-tree.
We also mean different things by overhead. For me (and the C++ committee) it means something that is done in addition. So for me templates create zero overhead as the do not produce more code than the same, non-template code. In practice, templates produce less code as there is a higher chance to fold common code from the instantiations of the same template than from many handwritten functions.
Comparing to other generics implementations - they do add a lot of code, much more than is really needed to achieve good performance. Sure, some of this code can be reduced at linking stage, but for this to work you need to statically link everything which has its own set of problems (overhead of repeated code in every binary).
IMHO the approach is a great example of premature optimisation: generate 2^N versions then try to fold some similar ones to save space. Much better approach is: generate 1 version and specialize small parts when really needed. Technically, assuming sufficiently smart compiler, both would end up with exactly the same code, performing exactly the same, but the latter solution is preferable for other non-performance wise reasons (like better type checking, ability to distribute binary libraries, faster compile times, etc.).
BTW: I still often find Scala containers to be often much faster than C++ STL. But this is probably for much different reasons than template/generics implementation.
I have lost you. Are you still arguing that C++ is internally inconsistent because it's not doing stuff that Java does? Are you arguing that templates add a lot more code than a VM and JIT compiler and languages with generic functions are as efficient as C++?
In the first case - I fail to see how it follows.
In the second case - our levels of expertise is too far apart to discuss anything. I am not saying you are not qualified, I might be just too out of the loop and missing some dramatic advances in Java, Scala etc.
I guess it's a matter of taste. Almost every time I get puzzled by some design decision in C++, it makes sense at first but then falls apart on closer inspection. The most recent example amused me so much that I wrote a blog post about it: http://slepnev.blogspot.ch/2013/07/printing-int-in-c.html. My experience with Haskell so far has been the opposite, design decisions make no sense at first but then turn out to be really smart.
@cousin_it: I have exactly the same feeling about C++ and Haskell. Another feature of this kind is inability to do virtual calls from constructors / destructors. Sure, the explanation makes perfect sense and leaves you with a feeling that C++ got it right and the rest of the world got it wrong. C++ protects you from accessing not-fully initialized object, awesome isn't it? Unfortunately it gets in the way when using factory methods (which are perfrctly ok to be called from the constructor in other languages) and is a source of subtle bugs.
C++ library is not something that is supposed to be very useful, it's something that is supposed to be available. I, for one, never used that library in my 15 years of professional programming.
Even though the library is covered by the same standard as the language it's not a part of the language and, in practice, is not even available all the time.
@pandaman: Just a minor nitpick about the statement that C++ library is not a part of the language: How come 'new' (builtin language operator, and reserverd keyword) can throw 'std::bad_alloc' (library construct) then? ;)
New itself does not throw, the allocation function does. Nobody makes you use the library allocation functions.
A better nitpick would be dynamic_cast throwing under some conditions. But then, how does it make iostreams the part of the language? There is just a typedef in the std namespace that aliases the type for some optional, compiler specific, data.
I think the point was that mathematics relies on a small set of postulates from which you can derive everything else. Changing a postulate results in a separate system. The same is true in programming languages, but the number of postulates is very large compared to mathematics to the point that their utility is greatly reduced. And also there are so many languages each with its own postulate set that it's really inelegant when compared to mathematics.
Another way to put it is, learning the postulates for langx doesn't provide enough value for many to be worth doing.
>I think the point was that mathematics relies on a small set of postulates from which you can derive everything else.
And I agree, this is one of the assertions my opponent has made. I did not go to refute it because, if not outright false, it's disingenuous.
>Another way to put it is, learning the postulates for langx doesn't provide enough value for many to be worth doing.
This is also true, same goes for everything else. I make money using C - I learn C's postulates. If somebody paid me to use topology - I'd learned topology postulates. What would be stupid if I were paid to use topology but said: "it's hard to learn and it makes no sense for me because I don't like mathematics and don't know any other math discipline so why would I all of sudden learn topology's postulate when I can copy formulas from the net and ask questions on manifoldoverflow.com?"
Mathematicians tend to miss one important ideal; a major consumer of your code needs to be other programmers who read it. Hence why mathematicians often end up writing code that, while more terse, is a jumbled mess of single letter variables and layered abstractions.
Standard mathematical notation is not inevitable and is not unique. It is on par with most programming languages for being quite random. Both programmers and mathematicians use an imperfect tool (programming language, mathematical notation) to achieve goals that may, or may not, be eternal.
That's a fair point, and math notation is famously more ambiguous than programming languages. But it seems to me that the concepts denoted by math notation (e.g. numbers, arithmetical operations, functions...) are inevitable and unique, while the concepts denoted by strings in a programming language are much fuzzier and more arbitrary (except for languages that have a denotational semantics, which are few and far between).
People often speak of the axioms in maths when they describe how, "elegant," and, "consistent," it is. I'm not a mathematician but I've read Gödel's "proof" and know enough that there are no guarantees. It matters what system of axioms you use and requires the reader to recognize the limits of those axioms in defining the system of logic they they imply. Or some such.
Point being that there is no system that is complete. There are large bodies of mathematics these days that have specialized their notations and axioms to their domains. It is incredibly difficult to maintain a masterful knowledge of all of their idiosyncrasies and notations.
> Why are you forcing those two choices? One could be well-versed with the arcana AS WELL AS point out that bit about statically declared variable ..
You're the one forcing the choices.
He's just saying that one should consider another metric to gauge developers, that is knowledge of good software practices over deep language arcana wisdom. And he's saying that whatever the knowledge depth (or lack of) of a candidate on the C or C++ language, he'll choose the one with better software practices. (Of course, the two metrics are slightly correlated, but being an expert on C trivia doesn't necessarily makes you a good developer)
Life is too short to become an expert on C's dark corners, and I'm not even talking about C++'s.
"I would much rather [a person do X] than [a person do Y]"
I parse this kind of statement as a person doing X is more important than a person doing Y. I stated that I thought both were achievable. How have you parsed it?
Also trivia implies some memorized factoids. The entire point of the article is having an understanding of the underlying implementation of the compiler, maybe the implementation of the standard library, or the OS or the hardware ADDS to your knowledge about C making you a much more complete/well-rounded developer. Calling it C trivia is baffling to me.
>Life is too short to become an expert on C's dark corners, and I'm not even talking about C++'s.
> Also trivia implies some memorized factoids. The entire point of the article is having an understanding of the underlying implementation of the compiler, ...
That's some of the point of the article, but some of it is also about memorized factoids. There is a lot of focus on what different versions of the C standard say about different things, which is just factoids. There is a lot of focus on how different declaration syntax behaves (statics, linker visible, etc.), which is just factoids.
If you had to write code for a platform and the only compiler available was C89 compatible, you better know what syntax the C89 standard says is legal and not legal. Its simply called knowing the language. By your implied definition I see every single thing that anyone reads/learns as a 'factoid'. In my mind factoids imply some useless disconnected fact like the number of restricted keywords or the number of pages in the standard or the preferred wine of the standardization committee(its called beer I think :P). I guess we have a difference of opinion on that.
Also the difference in syntax and translation unit/linker behavior is not a random fact. Its the culmination of the thought process that goes into designing the language and also knowing a bit of the theory behind classical compilers/linkers.
It is probable that we have a disagreement about what counts as a "factoid", and maybe it's not a great word to be using ( I took it from the parent), but I do think there are real differences between some things you're conflating. Working within the framework of your example, I think understanding how compilation units and the linker work is far more fundamental than knowing when the the "static" keyword impacts linking and when it doesn't. If I know that I need some way to make my variable visible outside my compilation unit, I can look up the syntax to do it ("factoid"), but if I don't understand how linking works at all, I'm missing a more fundamental mental model. Of course, I absolutely think that a C programmer should know the syntax, it's just less important to remember such things than to understand the model.
> You'll find them toiling away in anonymity, working in research labs, working on compiler optimizers or operating systems or some other domain with challenging technical problems.
Sadly you are required to move for such jobs, which is not always an option.
And best of all is such things are not fashionable. Take a round of HN or any other place where programmers hang around. Take a note of how many articles, blogs posts, essays, tiny libraries, frameworks etc are written for languages like Javascript, or Python, or Ruby, or Java.
Compare this with C. There is a degree of serious technical focus that languages like these demand for big projects. Embedded systems, Operating systems, Databases, compilers etc. A big part of software world uses these languages on a daily basis. But there is no where the kind of hipster crowd, these languages have the web frameworks or languages have.
Even if you spend some time searching for resources on the net, there are few that teach deep C skills. There might be one or two books out there which haven't been updated in 2 decades.
By and large, these are unfashionable fields to work in. The barrier to entry is really high, Success comes only with seriousness and application of well focused effort, mistakes are expensive and they warrant serious RTFM'ing the hard way- to write to some real serious code.
I don't know why you have C up on some pedestal. Its not really a 'hard' language to learn or grasp.
Sure some languages take less time to learn than others. But every single language makes tradeoffs. For e.g performance vs productivity/usability OR static typing vs dynamic typing is a common theme when discussing C vs {Java, Python, blah}.
Also, your comments falsely compare the difficulty of the TASK with the difficulty of the IMPLEMENTATION/LANGUAGE. They are not the same! Take any advanced applied math algorithm and implement it in Python. Its not 'easy' by any means.
The domains you mentioned have a higher barrier to entry because the tasks themselves are difficult. The languages and technology used are simply variables in a much larger optmization problem that also includes monetary/human/computing/time resources as variables.
Personally I don't care about C, due to its unsafe by default design and my background in safer languages for systems programming.
I got my first computer, a Timex 2068, at the age of 10. Spent two years mainly playing games and eventually started looking into programming.
When I learned C, at the age of 16, I already knew BASIC (Spectrum, GW, Quick, Turbo), Assembly (Z80, x86, 68000), Turbo Pascal.
So C was for me a bit "meh" language, given that Turbo Pascal gave me much more, granted except for portability. So I quickly jumped to C++, which allowed me some strong typing comfort and higher abstractions in a forced C land.
Regardless of the language, when I was 18, I had already coded compilers, graphics applications, GUI prototypes, MS-DOS drivers.
Nowadays kids seem just to do JavaScript/Python/Ruby frameworks for generating web pages.
Nowadays kids seem just to do JavaScript/Python/Ruby frameworks for generating web pages.
That's progress, IMHO.
Instead of distributing application binaries carefully targeted to each type of user machine, we only have to run the application on one server machine (or a cluster of them) in a central location and can serve the application over the network. We now have code-generating frameworks that help automate away much of the tedious, repetitive parts of implementing an application.
It's not a good thing that software development is hard. It does mean that you're a very smart and hard-working person if you're able to do it effectively. But it's not a good thing for society that you need to be so smart and hard-working in order to do it.
It is, however, a good thing that it's not as hard as it used to be. The fact that a software developer has to do and know less today than they did 20-30 years ago, just to build an application and distribute it to a large number of users, is a sign of progress.
- It has reference arguments, which avoid pointer usage for records and out arguments
- Requires explicit casts for type conversions, instead of implicit type casts
- Provides region allocators, which allow to release a full block of memory in one go
- Has real enumerations that don't decay into ints
- Allows writing generic code over arrays without requiring the developer to manage a separate length variable
- Has proper memory allocation constructs instead of requiring the developer to know the size of memory to allocate
Finally, there are other languages in the Pascal family, that provide safety, while allowing for C dirty tricks done explicitly via a system/unsafe package which provides much fine control over the use of said features.
so C doesn't have proper type safe enums (though you could hack those up with typedef + cpp), but does require casts for most type conversions (except those for void*).
pascal style strings can be done with the preprocessor, as can generic iterators, allocation for idiots, and "out" function arguments.
memory pools are a library feature.
and the arrays are the same too, a pointer in pascal can be treated as an infinitely sized array, but the reverse isn't true, just like in C.
and, believe it or not, there are other languages in the C family, that provide safety in the same way there are for pascal.
tl;dr: pascal and C are more alike than different.
> so C doesn't have proper type safe enums (though you could hack those up with typedef + cpp), but does require casts for most type conversions (except those for void*).
That still does not make them safe, implicit conversion to int will still happen.
> pascal style strings can be done with the preprocessor, as can generic iterators, allocation for idiots, and "out" function arguments.
No type safety.
> memory pools are a library feature.
That aren't part of any C compiler. So you can't count them being available.
> and the arrays are the same too, a pointer in pascal can be treated as an infinitely sized array, but the reverse isn't true, just like in C.
You lost me there.
> and, believe it or not, there are other languages in the C family, that provide safety in the same way there are for pascal.
Yes, that is why I use Modern C++, C#, Java, D and will only use C at gunpoint.
> tl;dr: pascal and C are more alike than different.
True, except in Pascal when bad things happen is because the developer explicitly wanted them to happen.
In C everything goes and to have Pascal's safety back, all modern C compilers provide static analyzers. Thus proving a point about the languages design's.
they are unfashionable not because they are written in C/C++. Programming at the end of the day is just not that hard, what is hard is domain knowledge, you can write a compiler in any language.
The other day we were discussing interview questions at work. The idea came up to ask people to solve a simple programming task in an ugly, arcane, and generally incorrect way; something like "Implement X as a python one liner, using at least three methods from itertools". The good candidates would point out that it really should not be done that way.
But puzzle questions like that are silly anyways, so whatever.
"Great mathematicians can understand the theory and just apply it to whatever problem they come across."
I don't think you know how great mathematicians think. http://en.wikipedia.org/wiki/57_(number)#In_mathematics"Although 57 is not prime, it is jokingly known as the "Grothendieck prime" after a story in which Grothendieck supposedly gave it as an example of a particular prime number."
>I don't think you know how great mathematicians think.
I'm confused. What is your basis for that statement? If you you know how they think why don't you just express it? :-S
I am sorry, I have no clue what your point is, or even if you have one.
Grothendieck is/was (according to Wikipedia, he is still alive, but he retired from mathematics years ago (http://en.wikipedia.org/wiki/Alexander_Grothendieck#Politics...) a great mathematician who knows/knew a lot about prime numbers, but he could not apply that 'to every problem he came across'. In fact, when pressed to give a concrete prime number, he came up with 57.
That makes him a counterexample of the claim that all great mathematicians can apply heir knowledge.
If he were a computer scientist, he would have written several brilliant papers about the pros and cons of various properties of programming languages, but he would not be able to write a compiler for any language or even to write a program.
Arguably if people have such strong understanding of things they will be better programmers too. I would prefer the person who knew what they were doing, because they also know _why_ weird things are weird, and have a better real understanding of what to do and not do.
A person who just knows it's "bad code"--but not why--is almost certainly going to leave other bad code from lack of understanding. To pull an example from the slides, the virtual destructor: making that class virtual when it shouldn't is a waste of CPU cycles _and_ bad documentation for future developers.
However, I'd be concerned that the person with strong understanding of things will simply leave uninitialized static variables lying around in the code simply because they _knew_ it was always initialized to 0. It might work if your entire coding team has that innate understanding, but that is unlikely.
Sometimes I think that having deep and expert understanding of a language may cause you to create code that other team members cannot understand... not on purpose, but due to your assumption that these are common knowledge (whether they should be or not isn't the issue.)
Static initialisers are a horrible example of "deep" knowledge. Not knowing how they work reveals not only ignorance of syntax but a fundamental lack of understanding of the entire programming and execution environment. Failure to understand your environment inevitably leads to confusion and bad decisions.
A more common example is operator precedence, especially booleans. The precedence rules are easy and set in stone for decades, but always use parentheses to clarify, and I do not trust programmers that don't.
Again, I think one with deep understanding would know that leaving it unset would result in zero, but also know that initializing it declares intent of the original author, and is thus useful.
C and C++ can make someone loose days to track down issues, in this day and age, where teams are distributed with lots of offshoring and various skill levels across development sites.
My last C++ project was in 2006, since then I have only used C++ outside work. At work our focus has been in JVM and .NET languages.
I don't miss playing the C++ fireman expert role that has to fix a stability problem created by someone in the other side of the planet.
Yea actually even last week I have spent and entire day trying to bend a c++ library to my will due to errant assumptions and such. All part of the long and painful learning process!
Totally disagree with this. I've worked with plenty of fantastic programmers who are basically worthless without the guidance of a good lead/producer/designer.
That gui seems fine to me. It's ugly as sin, sure, but it has everything easy to find and clearly labeled. Ignore the parts you aren't using and you're good to go.
I love this GUI. Gives a clear view of what can be changed, and so much more user friendly than having to remember where each of the nested (often so many times!!) options goes. "Advanced..." buttons everywhere are the worst, and don't get me started about wizards.
A programmer is not a designer. Both are completely different fields. What you are saying is that a shark(programmer) cannot fly like an eagle(designer). But it sure can swim. And vice-a-verse.
Hmmm, no, to mimic the analogy I am saying that just because something has teeth doesn't mean it can bite like a shark. Just because something has wings doesn't mean it can fly like an eagle. A budgie with a jetpack, however, can probably fly better than an eagle. Or something.
I love this presentation. It led me to read Expert C Programming [1] by Peter Van Der Linden. My knowledge of C had vastly improved after reading that book (and subsequently C++). Even if you're not a C programmer I would recommend the book as the anecdotes alone are worth reading for the questions that it encourages you to ask.
I've since come to believe that reading the specifications and having the attention necessary to delve into these kinds of details and ask the right questions is important for mastery. It seems to me that learning 1 - 2 languages to this level of detail is worthwhile. I've been thinking of cutting back the number of languages I, "know," down to just those for which I am familiar with the specifications and how they're compiled, assembled, etc. Everything else is superficial.
Sometimes all you need is just a cursory knowledge to get something done and the ends justify those means. However if you really love your craft then mastery should be the goal, no? It seems to be the difference between, "getting something working," and, "pushing the boundaries of what is possible."
It's wonderfully frustrating that there is truly so much to learn that even the study of programming languages, which is an incredibly tiny field relative to the whole of human knowledge, it is still necessary to make depth vs. breadth trade-offs. I find your choice to focus more on depth appealing, but almost immediately feel a sense of loss for all the breadth such a focus would require giving up.
I think a better balance may be to master things that are widely relevant at depth, while giving less focus to things that are very specific. Put another way: depth for "essence", breadth for "accident". Of the items on slide 181 of the presentation, only calling conventions, memory model, and probably optimization seem like "essence" to me, ie. every program you ever write in any language will benefit from deep knowledge of those topics. The rest of the items are somewhat arbitrary and very specific to the particular history of the C language itself.
It boils down to learning a handful of languages enough that you can make some meaningful contributions or a single language deep enough that you can have a meaningful discussion. Neither approach forgoes learning fundamental theory. I equate learning to master C like learning to master a particular instrument like the violin; it requires knowledge of the instrument's construction, the physics of how it produces sound, and a host of other details that must be mastered before one can really make it sing.
I can study music theory for years and it won't help me play the violin well. It will make composition easier and assist me as I adapt myself to the instrument. However one still has to master the instrument and I see the two being complementary and distinct skills.
The metaphor holds together when you start thinking about the breadth of languages available. For example, my primary instrument is guitar. I've been playing for years and have a good grasp of it. However I recently took up the violin so that I could learn to fiddle and play some bluegrass and Irish folk tunes. I still can't play a single tune well on the violin but my knowledge of string instruments and music theory is certainly making my adoption faster than someone who is starting from the very beginning.
However one must be careful with breadth. I could try to learn to play the mandolin after I've "finished," with the violin. And perhaps a banjo after that. However mastery requires a deep, intimate knowledge: you have to spend time with an instrument, learn its quirks, and practice with it every day. Even the best guitar players practice every day. You don't have enough time in this world to master every stringed instrument.
Harder still is the transition to a completely different class of instrument. Mastering drums and guitar is not impossible but rather difficult as knowledge of one doesn't offer much in learning about the other. Learning C is one thing but learning Common Lisp is learning an entirely different model of computation. Having a grasp of the fundamental theories will help some here but nothing you know about C compilers will help you to understand CL (and vice versa). So be even more suspicious of the depth of a programmer's knowledge if they list more than a few languages on their resume if those languages span entire classes of computational models.
And I don't think there's enough time in this world for even a very competent programmer to say they are a master of more than one or two languages.
Update Added a paragraph on computational models and expanded the instrument metaphor.
"C programming is a craft that takes years to perfect. A reasonably sharp person can learn the basics of
C quite quickly. But it takes much longer to master the nuances of the language and to write enough
programs, and enough different programs, to become an expert. In natural language terms, this is the
difference between being able to order a cup of coffee in Paris, and (on the Metro) being able to tell a
native Parisienne where to get off." The behaviour of this quote from the beginning of Linden's book is, strictly speaking, undefined. I interpreted it as being able to give instructions to a native Parisian, in order to find the nearest red-light district.
"where to get off" has at least three interpretations---the one you note, one in which you insulte the Parisienne (why a Parisienne? Why not the English word, which has the advantage of being gender-neutral, "Parisian"?), and the (presumably) intended interpretation, what exit to use.
"why a Parisienne? Why not the English word, which has the advantage of being gender-neutral, "Parisian"?" I believe I did use the term Parisian....
I could have used the even more offensive (joke) term "parisite".
> It seems to me that learning 1 - 2 languages to this level of detail is worthwhile.
I think it's feasible with an "old-school" (compact) language like C or Go. But take something like C++ or Scala, for example, and the task quickly becomes impossible.
this has nothing to do with understanding but more with memory. What's drowning people in C and even more in C++ is not the logic of the language, but the sheer number of tricky concepts and the pure accumulation of information (it is reflected on the size of the specs).
There is also the fact that very often non-specified behavior, or implementation dependent or everything else that is not cool does not lead to a warning, so the learning is absolutely not reinforced by the compiler. Whereas a warning/error leads to questions that leads to google and some learning; you can be stepping far in the Pampa of undefined behavior for years when someone comes with a superior attitude in your company detects it and calls you a moron in a powerpoint.
And this also leads to very hard to write code sometimes, if you want to do some serious IEEE754 in C/C++ you will basically be pitting the spec of the language against the spec of numerical computation in a ring.
What's drowning people in C and even more in C++ is not the logic of the language, but the sheer number of tricky concepts and the pure accumulation of information (it is reflected on the size of the specs).
I agree, and I'd add that C/C++ is a bit of a leaky abstraction layer over assembly. It aims to be portable, but true portability means that the C/C++ developer really shouldn't have to know/care what assembly instructions the compiler/linker is emitting, because all of that stuff depends on which chip architecture you're targeting.
Basically, coding in C/C++ requires an intimate knowledge of how the compiler works, and sometimes even how the target chip works. C/C++ software projects of notable size oftentimes cannot be simply recompiled/linked to a different target architecture without changing things like compiler flags, the makefile, and even the application code itself. That makes it a non-portable, leaky abstraction, and dramatically increases the amount of knowledge that's required of a C/C++ developer.
I've been spending a lot of time recently looking into and thinking about alternatives to C.
I'd love to see a language as close to assembly as C is, but with a cleaner disconnect with it. As an example of what I mean:
Bitfields would be really handy when writing a device driver. A basic example of the difference they would make is "if (reg.field == VAL)" vs. either "if (reg & MASK == VAL)" or "if (GET_FIELD(reg) == MASK)". But you can't use them for that purpose because their layout is implementation defined.
There would be a noteworthy performance benefit to pay for portable bitfields, but I think it would be worth it. Right now I have to write ugly code if I want any attempt at portability (always). I'd much rather have to write ugly code where I need speed (sometimes).
I'd love to hear if anyone else has any ideas on this matter (BTW it seems like D solves many of the problems I've been thinking about, but it's memory managed).
This has nothing to do with understanding but more with memory. What's drowning people in C and even more in C++ is not the logic of the language, but the sheer number of tricky concepts and the pure accumulation of information (it is reflected on the size of the specs).
I've been coding C++ for a few years now and it still baffles me most of the time. If I take a break from it for a bit, I have to re-learn how pointers function every time.
A fun fact about sequence points. C++ has switched from "sequence points" to "sequenced before/sequenced after" relation. And it is not really "all" previous side-effects that are guaranteed to be visible after the sequence point but only those associated with the sequence point.
In 'foo(b++, a++ && (a+b));' the sequence point introduced by the '&&' only makes the 'a' see the effect 'a++' but the 'b' might not see the 'b++' (function args are not sequenced in C nor C++).
Right! In fact I believe the above code invokes undefined behavior, because b may be accessed for a reason other than to determine its new value, without an intervening sequence point.
Put another way, the compiler can arbitrarily interleave the subexpressions within separate arguments:
foo( a() && b(), c() && d() );
The compiler is free to invoke these functions in the order a(), c(), d(), b() if it pleases.
It's probably less about writing the statement while cackling maniacally and more about recognizing that pattern in less obvious examples in existing code while reviewing it or looking for a bug, and realizing that it might be worth investigating and not just a style nitpick.
The problem there is that while compilers have been getting better, they're nowhere near beating good programmers (yet ?). Languages other than C/C++ get in the programmer's way and prevent them from using "maximum" cleverness, and that means the best java/c# programs are simply always going to be slower than the best c/c++ programs.
That's true on balance, which is why C/C++ is still typically faster than managed languages. In many particular cases it's not true, however. Consider "register" or "inline" directives, which have been advisory for a long time.
Hermione (I'm sure that's her) rates her C++ knowledge at 4-5, and Stroustrup himself at 7!?
Bullshit. Either they are poorly calibrated, or they are displaying false modesty. Sure, they probably still have plenty to learn about C++, but come on, Hermione is already at the top 97% in terms of language lawyering.
Wanting to be stronger is good. Not realizing you're already quite strong is not so good.
It's real, it's called the Dunning-Kruger Effect. I've seen it plenty of times with real superstar-experts rate themselves as "slightly above average"...
I think it is more than that. I had a boss that basically would not hire somebody if they said they were at the top of any measure. In one sense, she was correct. No one can know everything about C++. OTOH, if you are in the 9x percentile, why should you not rate yourself at 9-10 out of ten? At some point it gets silly when you are arguing about how you should properly distribute yourself in bins so long as you understand the scope of the problem.
It's all relative. I would consider a 10 somebody who could write the C++ compiler without guidance for example. And probably give myself a 4 as a result.
It's really quite interesting that they build the queries from the AST. To contrast C# generates the Sql for Linq to Sql by building up an expression tree using the IQueryable interface.
This simplifies step one of the Pony ORM author's answer.
My point is the scale is absolute. Somebody who rates themselves a 9 on C++ knowledge probably doesn't realize how much there truly is to know. Sort of a corollary to the dunning-krueger effect.
What I still wonder about is where that 7/10 rating for Stroustrup comes from. I've seen it mentioned in some other posts on hackernews, but just with the remark "Stroustrup rated himself as 7/10 in an interview". I'd like to see the entire interview (or what other source it comes from), just to be sure it's not an out of context quote.
For the niches that JS fills, a clear language with few pitfalls would be best.
C++ has a different niche. On top of that, everyone acknowledges the warts of C++, but they're largely necessary for either backward compatibility or performance reasons.
At least C++(11) is a (better) statically typed language :)
Although, This isn't even half of it! thanks god the girl didn't know anything about templates.
Templates are a horrid syntax for an okay functional, compile-time language. They can be useful - in quite a few ways an improvement on C macros. I say this as someone who's done quite a bit of C and C++, and am currently working mostly happily in C at work and Haskell on my side project.
A college student in CS here. I'm always impressed by people with deep understanding of programming language internals and try to pick up as much about programming language internal workings and compilers as I can. How does one get really good at this? Is it by spending a lot of time programming and building stuff? Is it by reading books/blogs/articles about programming languages? Any recommendations for such resources?
You'll learn a lot of this via debugging or reverse engineering. Do you REALLY know how your compiler, linker, and loader work? If you do, you'll understand everything they wrote about static variables, stack frames, etc.
The best exercise to start with is this one on creating the smallest ELF executable possible:
And after that I'd do the same thing on Windows with a PE file.
Then try to break things. Try to write a C program that has a buffer overflow bug in it and exploit that to change the flow of program execution. Then exploit the same bug to open an xterm or calc.exe.
Getting really familiar with a good debugger will help you a lot with these things.
After you have a decent understanding of program loading and execution, you can dive into the more language lawyery things they're talking about so you can answer the questions about the nineteen different meanings of "static" like the hacker girl. With C, that's a worthwhile goal. With C++, good fucking luck.
The presentation's "Deep C" pun is a reference to this book, and if you make it all the way to slide 444 you'll see it mentioned as further reading. It's a wonderful book for understanding C (not C++) and what's really going on.
The points made in this (excellent I thought) presentation are very subtle and I'm not sure I would have come across them before organically. Because the behavior outlined in the presentation tends toward the exception, it's more efficient to learn it by reading. Effective C++ is a great place to start if you are trying to hone your C++ skills (practically every C++ programmer in the industry considers it mandatory).
Keep in mind though, that knowing about evaluation order, and stack frames, and sequencing, and linker optimizations is all great and all, but I definitely consider it icing on the cake for a working software engineer for most positions. If you're a senior guy, sure, you should know this stuff. But the first thing to do is learn to actually write programs well. Authoring your own projects and contributing to open source is a great way to do it.
Get hold of an embedded microprocessor and write some silly app for it. E.g. ZigBee or Digi's rabbit microprocessors.
I learned all my "deep C" on the rabbit. The architecture is based on Z80 and the compiler is a really shitty non-standard C compiler with some custom syntax.
You will very quickly learn to throw away many assumptions you might have about how things work. Learn how to write mutithreaded code without an operating system or a thread library (hint: co-routines). Learn how memory layout, very quickly get a feel for the time/space tradeoffs of different algorithms, etc.
One way is as you said is by spending a lot of time programming and building stuff.
The slide deck says, the only way is the experience. You need to read and learn endlessly over the years.
Like you I hope there was one book, that does this. Makes a nice idea for a community project. Compiling such massive wisdom into a book can't be done by a person alone.
Currently its a bit like alchemy, looks very little chemistry and much more magic. And to learn there are hardly any resources beyond your regular C books, which more or less keep talking of the same things.
1) learn to program in assembly
2) inspect the assembly generated from your compiler
3) trace a call all the way from user land down into the kernel, and back up
4) don't forget networks. Whether that means a single wire instrumented with a 'scope, or a network switch instrumented with wireshark, knowing how data gets around is important.
Until you can do that on your platform, you don't really understand it (which is fine for many jobs out there, I'm just addressing the question of how to learn it).
I suspect the shortest and best path is to write your own (toy/emulator) assembler and compiler, and to do some embedded work with no OS. That'll at least expose you to all the various issues; you might not implement register allocation well (or at all), but you'll have had to think about it and the implications (example: passing function parameters on the stack vs in registers). That's one of the advantages of a University education to my way of thinking. You will be put through all these paces if it isn't just a Java accreditation program.
There are good books documenting, say, the Windows or Linux internals, so you can gain a good understanding of things like virtual memory, device drivers and so on.
It is not as hard as it might seem. This is all basic information, one fact built on top of another. I have no idea how the ARM pipeline works, but that doesn't worry me. I know what pipelines are, some of the tradeoffs they have, and if I need to get down and dirty with a cute little ARM processor I'll know what I need to learn. You can feel sort of overwhelmed if you try to think how pressing on these keys get turned into truetype fonts on a graphical screen, and then sent across the internet to anyone bored enough to read my ramblings, while at the same time my computer updates the clock, checks my mail, and does a dozen other things, but piece by piece it is all discrete, well contained, and completely understandable.
Reading Stroustrup is pretty much mandatory to understand C++. I personally wouldn't bother with the standards (other than a brief go-over); so much of the code in those slides are just things you should never, ever program. Which is to say I disagree with the slides in large measure; it's almost suspicious to have some of that knowledge! Either your coworkers are writing truly atrocious code, or you are spending time learning corners of the language which is time that almost certainly could be better spent elsewhere, such as learning about memory mapped files or something. As others have said, knowing where the boundaries of where undefined behavior lies is important, but knowing how to avoid straying even close to that territory is far more important (always initialize variables. Even static ones! You are going to cost some sorry bastard half a day of debugging when they erroneously think the problem in the code is an uninitiated variable and keep chasing that until they finally figure it out)
The top option for learning is to work off a spec to implement your own compiler. A good second place would be exhaustively studying and maintaining an existing compiler.
Regular use of a language can build certain kinds of knowledge about the internals, but it won't be as well-rounded a study as actually working with them directly.
I also found this very awkward. Especially because the author created the character he subsequently demolished.
While I can follow most of the C problems, C++ has never interested me. As as a Common Lisp programmer, all of this seems quite demented.
By reading the slides I have learned about some interesting optimization concepts, and wondered how CL compilers do it. But honestly for 99% of my Job I couldn't imagine using something like C.
How far into the presentation do I need to go for someone to mention that the quotes in the printf are not US ASCII and therefore the program would not compile?
I've been a C developer (among other things) for about 20 years or so, but I've always described my C++ skills as being "in the C with objects" range, I might have to change that to being "nearly in the C with objects" range.
I didn't have any trouble with the C examples, but with C++ it just puzzles me why people would want to use a language like that in the first place. What does it buy you except bigger head aches?
It buys you the speed of C (or close), with nice abstractions. And when you use destructors correctly, it can provide pretty much automatic memory management too, much safer than C.
And it's pretty simple to avoid all these warts, just... you know don't write them. On the other hand I don't have any experience with old C++ codebases, which is where you're likely to find this hellish stuff.
RAII is used for idiomatic cleanup from a function. In C the way to handle that would be to have a cleanup section in every function and then doing a goto to that which adds clutter to the function.
That is the same with any language. An expert should not only know the language as defined by the standard or canonical implementation, but also the majority of implementations for the said language and consequent deviations from the official language semantics.
Ergo, no one is actually a real expert in a specific language.
If B() is noexcept then A() is actually exception safe. The only thing which could happen would be bad_alloc but then v does not have to be deleted any more.
What bizarre nonsense. I hope nobody thinks this level of (mis)understanding is replacement for understanding, you know, how to program.
The majority of this is undefined behavior or implementation details of your compiler. If you rely on that, I don't want your code anywhere near my machine.
I think you missed the point. It is more about coder's attitude than about using (or not using) the presented examples in real code. I wouldn't like to work with a programmer than writes shitty code accidentally relying on UB, and denies every criticism by saying "it works, so what's up?".
I felt this way as well. On an interview why would you give a really specific answer to a really general question? I don't think it's that most people can't answer like this, rather that it's not what was asked.
I am trying to find a way to reconcile two realities. On one hand, whenever I encounter books or articles on language legalese (particularly in C and C++), it is always immediately apparent that I'm somewhat out of my depth, despite programming in C and C++ for 20 years. I can follow the reasoning, but I almost never spot the finer problems myself.
On the other hand, I can and have written large, complex games that run well and appear to be just fine from the user's point of view. I can seemingly write reams of code without ever thinking of such issues, except when they trip up the compiler or cause detectable bugs. I find I very, very seldom need to think of the lawyerly issues at work, or in personal projects, and to my knowledge, nobody has suffered materially for it. (Granted, I am writing games and not NASA software.)
Whenever these types of issues come up, I always think: "Wow, I know less than I thought. This seems serious?" And yet I never seem to see real-world consequences. This seems strange to me.
Most of the C stuff here is pretty good, but some of the C++ information is a little questionable. Even the "good programmer" exhibits a few common misconceptions about inheritance and virtual destructors. Here's the text from slide 348:
"What is the point of having a virtual destructor on a class like this? There are no virtual functions so it does not make sense to inherit from it. I know that there are programmers who do inherit from non-virtual classes, but I suspect they have misunderstood a key concept of object orientation. I suggest you remove the virtual specifier from the destructor, it indicates that the class is designed to be used as a base class - while it obviously is not."
She's right that the class described on the slide probably shouldn't have a virtual destructor.
A base class should have a virtual destructor if and only if objects of its derived class are to be deleted through base class pointers.
The following four statements are wrong:
1. A class with other virtual functions should have a virtual destructor.
2. A class without other virtual functions should not have a virtual destructor.
3. A class designed to be a base class should have a virtual destructor.
4. You shouldn't inherit from classes that don't have virtual functions.
It's a narrow-minded to say that programmers who inherit from "non-virtual" classes have "misunderstood a key concept of object orientation." Which key concept is that, by the way? Object orientation isn't the be all and end all of C++. There are reasons to use inheritance that have nothing to do with run-time polymorphism. Maybe you just want to reduce redundancy and organize your data types in terms of each other.
Another gripe is that on slide 369, she says:
"When I see bald pointers in C++ it is usually a bad sign."
Naked pointers should usually be avoided for memory management. That's true. But they make great iterators, and they're useful, along with references, for passing objects to functions.
On the other hand, seeing the keywords "new" and "delete" in code is usually a bad sign. Resources (not just memory) should be managed by resource management classes. If you try to do it manually, especially in the presence of exceptions and concurrency, it's very easy to cause an inadvertent resource leak.
"On the other hand, seeing the keywords "new" and "delete" in code is usually a bad sign." I thought those two keywords were intended to replace the basic memory allocation functions found in stdlib.h. What are the alternatives?
As other folks have mentioned, unique_ptr, make_unique, and all their happy friends are there if you need them. But your variables/objects should go on the stack unless you have a reason not to put them there. As the slide suggests, if you need a lot of stuff on the heap, a standard std::vector will often do.
In C, it can be tricky to ensure that every malloc has a matching free and every fopen has a matching fclose etc. This style of resource management is error-prone, but it comes with the C territory.
Java throws its hands up in the air in disgust and basically accepts that you will generate a lot of garbage. Then it slaps a mandatory garbage collector on top to clean everything up, which I guess is an okay way to deal with memory, but it sucks for other resources that aren't managed by the garbage collector.
C++, on the other hand, makes it easy to write garbage-free code. But it's only easy if you avoid using new or similar functions that allocate resources such as file descriptors, locks, network sockets etc.
If you see people using "new", there's a good chance they're misguided C or Java programmers.
C programmers often think they should use new/delete as often as they used malloc/free in C. Java programmers sometimes try to use new every time they instantiate an object. Java did steal the new keyword from C++, and it's perfectly normal for Java code to be riddled with new's because there's no other way to do it in Java. But these styles lead to disaster in C++.
It has a constructor (to initialize the resource), a destructor (to release the resource), copy/move constructors, and lets you get access to the underlying resource. See http://en.cppreference.com/w/cpp/memory/shared_ptr for an example.
> Why do professional programmers write code like this?
Because we 1) for some reason or another we _must_ use C or C++, 2) we're coding for a single CPU platform, and 3) we need to get the friggin' job done in this century.
I haven't seen these tricks come into play when solving real problems at ALL. Not saying you should not know the language, but better to have problem-solving skills than rot the standard. The bit about leaving a new line after main tells me she really has memorized the standard.
It's not about tricks, it's about precisely understanding the mechanics of the language. This can be exploited to craft tricks, which is occasionally important. It can also be exploited to avoid gotchas, which is usually important. It can also be exploited to better and more quickly understand what some behavior means when things aren't working as expected, which is always important.
I guess it depends on what you consider "real problems".
One can always write good and efficient code by following what he/she knows is the defined behavior. Just found weird that some things related to standards were given more importance whereas problem solving is the key while writing any program.
Well, I'm currently being paid to write C day to day, so it's solving someone's real-enough problem that they're willing to part with money. I described roughly how I see my comprehension of the language reflected in my work.
So am I and I've mostly used a subset of C features to solve many problems. I've also seen some C 'experts' fail to come to a proper solution even if they know all the features.
"I've mostly used a subset of C features to solve many problems."
Sure. What about code written by others? What about when you're debugging and something just isn't working the way you expect because you've unknowingly stepped outside that subset? (Most of the important concepts here aren't syntactic constructions you can avoid trivially by typing Y instead of X, and may or may not be detectable statically). Also, maybe you're missing opportunities to better insure your code is correct at compile time - I'm not sure whether it falls within your subset, but for instance I recently put together a macro that asserts statically that two expressions have the same type (and without any runtime cost).
"I've also seen some C 'experts' fail to come to a proper solution even if they know all the features."
Obviously. Knowledge of the spec and your compiler aren't the only thing that matter, by a long shot. I'm just saying they most emphatically are helpful.
I guess you summed it up. Indeed having more depth in a language is very important and useful, I was just trying to make a point that these aren't the only things.
Here is how I perceive a language,
It is made up of syntax and behaves in a certain way. If you read the documented features you'll know all of it. There may be some undocumented things which may not be of utmost importance. However, this is just the base of programming. And when you start dealing with 5-6 languages you can forget things if you don't have a good memory but the good thing is it's all documented and known features. Whereas while programming, one can often be involved in solving unknown problems, which can't be figured out just by googling.
It is interesting looking at the 'deep' issues of programming languages. This kind of ties in with the "Don't ask me maths questions..." post a few days ago.
There really is a lot more to programming than just algorithms, UI design or syntax.
Especially today some people think "great programmers" are the ones who knows all the "fashionable" tools and frameworks, wants to abstract everything (god help him if he install vim in his test environment without a fab file that configures chef) and maybe worships Uncle Bob
Knowing how to use Redis is cool, do you know what's even cooler? Being able to write it (or at least knowing how it works more or less)
Even better is getting a programmer that knows enough about making X to not attempt to implement it.
Anyway the programming is such a multi-dimensional evaluation problem that you're just as wrong as you are right.
Think about it - there are so many things programmer SHOULD KNOW it isn't possible to know them all. Let's name them some of them - good architecture style, good API writing skills, good code writing style, good documentation style, company's internal workflow with all its nuances, compiler standards, compiler optimization, non-standard compiler behavior, version control system API, version control innards, database system, file system, low level operating system implementation, kernel innards, non-standard OS behavior, hardware knowledge, driver knowledge, non-standard hardware behavior, hardware compatibility, software-hardware compatibility, etc. (This doesn't cover assembler knowledge, binary represntation of important formats).
You simply have to choose to not learn everything about certain system to be good at other things.
I'll rather pay an artist to paint me a nice piece using store bought paint than making his/her own paint.
That's fine if you just need a nice picture to hang on your wall, but probably won't work if you want a genre defining piece that that redefines how artists think about the color blue.
Ooh. I remember "Dynamic-C" and its built-in support for coroutines. TI and Microchip had their own quasi-proprietary extensions to C, but I don't think I ever saw anyone go as far as Rabbit did.
Sorry I deleted my original comment as I thought it might be too off-topic.
Yes, Dynamic C had built-in coroutines :) I think it's hard for me to use threads now because of how convenient a single-threaded main loop with coroutines is. A lot of concurrency "problems" vanish when you realize you as the programmer are controlling the context-switching and that race conditions are implicitly not possible then.
I went so far as to write my code so that it compiled in GCC and Dynamic-C to speed up development time (not needing to wait for the 2 minutes it takes to compile and reflash), which meant abandoning all those things and re-implementing them in a cross-platform way buried in a mountain of #ifdefs
Although I can't remember all the details now (5 years since I touched that compiler) but I still have a lot of deep seated hatred towards it for reasons I can't remember clearly :)
I only know python well and am looking for another language to learn. C was on the shortlist, but after this, crikey. At least I now know it's not going to be an easy ride.
I found the original ANSI C to be more straightforward, and many times after I was asking myself "why was this done in this way" I only realized that I would have ended up with the same way of doing things - e.g. that the stack arrays should just skip the "pointing" memory, and things like that. What you saw in the presentation are mostly just later complications that popped up from the attempt to improve the manner of approach while keeping the entire thing speedy and backward-compatible. Do you think this is too much? Take a look at C++11 and marvel at the quantity of language hacks there!
I wonder what a linux kernel developer would have to say about these slides. How many of these features/quirks are they finding useful in their day to day job.
Heh, and they didn't even get into the aliasing rules. In embedded software, life would be a lot easier if I could hit every engineer who wants to type-pun without a union in the head with the ISO standard.
For this reason, a lot of compilers have options to not strictly enforce the aliasing rules
[edit]
Also C and C++ are both permitted to reorder structs, it's just that they don't because that's the easiest way to follow the standard.
No, in C struct members must be allocated in the order in which they are declared. C99 §6.2.5 says:
A structure type describes a sequentially allocated
nonempty set of member objects (and, in certain
circumstances, an incomplete array), each of which
has an optionally specified name and possibly
distinct type.
and §6.5.8 says:
If the objects pointed to are members of the same
aggregate object, pointers to structure members
declared later compare greater than pointers to
members declared earlier in the structure,…
What the language standard does not specify (and is very explicit about not doing) is padding between struct members, if any. However, the ABI spec for the compilation target details exactly how a struct must be laid out. If this were not the case, libraries could not work.
As mentioned by others, C does not allow reordering of members in structs. However, C++ does!
§9.2.12 says:
Nonstatic data members of a (non-union) class declared without an
intervening access-specifier are allocated so that later members
have higher addresses within a class object. The order of allocation
of nonstatic data members separated by an access-specifier is unspecified (11.1)
This means, that under a strict reading, even the following struct can have its members reordered:
Did this change in C++11 or does this wording mean the same thing? It's not clear to me that the implicitly public a is of a different access control than the explicitly public b according to the following wording:
> Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11).
Embedded C developers can produce very, ehum, 'interesting' code. They usually come from electronics engineering and think in terms of registers and memory instead of an abstract machine. I've seen code that actually relies on two arrays being defined in order and thus can be indexed from only the first array but with a calculated offset. This is outside of a struct...
Just type casting a struct to a char-pointer and dumping it straight out on the network or to persistent storage is code you see every day.
C is not allowed to reorder structs. There's a section in the C standard, n1516 page 113 (section 6.7.2.1 paragraph 15)
> Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order which they are declared.
"In embedded software, life would be a lot easier if I could hit every engineer who wants to type-pun without a union in the head with the ISO standard."
Is that the same standard that says it is illegal to write to one entry and read from another in a union* ?
* Note : special situation of identical fields in a structure
The C99 standard explicitly says type punning through a union is allowed. This is clarified in TC3 (2007) by the addition of this note in section 6.5.2.3:
If the member used to access the contents of a union object
is not the same as the member last used to store a value in
the object, the appropriate part of the object representation
of the value is reinterpreted as an object representation in
the new type as described in 6.2.6 (a process sometimes called
"type punning"). This might be a trap representation.
The girl spewing some bullshit may lead to an interview ended early. Clearly bright, but not as bright as she think she actually is... Come back in a few years with a bit more humility... Hard to decide if the other guy is bad or inexperienced without a resume...
"If you compile in debug mode the runtime might try to be helpful and memset your stack memory to 0"
This is a retarded explanation (pages are set to 0 when they are recycled by the OS so you don't end up having data from dead processes mapped in your memory, with all the security implications). Also, actually randomizing memory in a debug context would actually be more helpful to trigger those initialization bugs...
People that think they know everything are a lot more dangerous than people actually aware of their limitation and safely working within them.
Okay, you are assuming an Intel architecture? Plenty of embedded environments don't have OSes, let alone pages or virtual memory.
IOW, I've worked in environments where, yes, the value in memory is based on what the compiler does in debug mode, not what the OS might be doing. I'd say the girl's answer is, while not exhaustive, certainly correct. Without more context, we don't know why the value was 0.
You have printf, and the interviewer says "on my machine, I actually get 1, then 2, then 3". Those two hint very strongly at a general purpose computer, thus paging etc.
What really annoyed me is the suggestion that zeroing is more helpful than randomization in a debug context. Randomizing everything not defined by the standard is a good way to trigger lots of bugs based on false assumptions...
There are two legitimate concerns in a debug context. The first is predictability / repeatability; zeroing is substantially more helpful than randomization in this context.
The second is revealing incorrect assumptions / fuzzing / stress testing; zeroing is substantially less helpful than randomization in this context.
Ideally, both options would be available. When someone says "give me a debug build", I think it's probably correct that they are caring more about the former, but really it should be clear and controllable.
No, that addresses reproducibility, which is worthwhile but is not the same as predictability.
Predictability in this context means simplifying the problem. If uninitialized memory is nulled, then I know what I'm looking for/at more quickly. Likewise if it's set to any other particular, known value. I know it will (or won't) be failing null checks, and I know that it will (or won't) be segfaulting if dereferenced. This helps with characterizing and fixing bugs, (related to but distinct from detecting bugs in the first place). Relying on this to make the program work is bad, but that's not the same thing.
Also 0xCC is the breakpoint opcode (INT3) on x86 architecture, so if the program were to start executing at that point, the debugger would be notified (or probably an immediate application crash if no debugger attached).
I signed up with HN to get articles related to C, yet they're about as common as hens teeth.
I've just executed a word search on this comments page: 18 instances of "static", but no mention of "volatile" or "extern", which is the same situation in several textbooks on C.
The article mentioned looking at the assembler output of a program, but didn't give any hints on where to learn some assembly.
"You need to learn the history of HN and realize that Lisp is the original hacker language." That's fairly common knowledge, I've been coding for eighteen months and I learned that "fact" months ago.
"As for assembly, there's been several lists of "free" computer books post that have included assembly tutorials." I haven't mentioned what assembly I wish to learn. A lot of the assembly tutorials I have found on the web talk about x86 assembly, yet my processor is 64-bit. I'm quite sure that the principles extend easily from one register size to another, but I have no experience at all in assembly.
It uses MIPS assembly, which you can run on a simulator. I don't think many machine architecture classes teach x86 because it's more complex. The MIPS knowledge from that course has translated easily to x86 in my tiny experience of peering into my compiler's assembly output.
seen this before. whilst its great there are degrees of respect to have for C and C++.
the most productive code i have ever written generally involves me working around the constraints of the language to implement a paradigm which is missing at compile-time, or juggling macros and templates so that i can reduce boilerplate code down to a template with a macro to fill the gaps the template is too featureless to give me (vice versa, the template is there because macros aren't complete enough either).
its good to understand this deep language stuff though because you can understand why C/C++ are limited. for instance the C sequence points limit the compiler in its ability to perform optimisation, as do struct layout rules and many of the other weird and wonderful specifics...
what saddens me most though is that nobody has offered anything to improve C and C++ in these areas which matter most to me... its not even hard. just let the compiler order structs because most programmers don't understand struct layout rules.
its not a good thing that these things are so explicitly specified for the language - its gimping the compilers, which is limiting me. also it results in pointless interview questions about sequence points.. :P
Regarding padding, with GCC at least it's not (precisely) word size that it optimizes for, but alignment constraints of the particular members. A short will be bumped to a multiple of 2, an __int128 will be bumped to a multiple of 16. The alignment restriction of an entire struct is the largest alignment of any member. This certainly has the intent and consequence of more aligned loads.
Deep understanding of the language used will make you do a better job.
This reminds me of the story that I've run into once. The junior developer wanted to raise PHP's memory_limit parameter because his code crashed almost every time while writing big file content to the output. He didn't know what output buffering is and that he can turn it off and print the file directly to the output. :D
Knowledge of the language is a big factor, but it's far from being the only one. How fast one is able to create things is an example of another important factor. There are many more.
Informative and thought-provoking presentation, but I think the mock interview format is a disservice, as with it comes individual ideas of what a good or useful interview is like.
I don't agree. Just a few slides in, what good is "functions don't have to return a value explicitly, even if declared as returning non-void" (a priori, I mean—not counting backwards compatibility as a reason)?
I think this presentation is interesting from a programming language design point of view; if you were designing a new language, this presentation highlights various 'gotchas' in C and C++ that might be the sort of thing you would try to avoid creating in your new language.
Many of these fall under the heading of opportunities to apply the Pythonic design criterion "refuse the temptation to guess":
1) in C, according to one of the comments, it was claimed (i didn't check) that if you declare your own printf with the wrong signature, it will still be linked to the printf in the std library, but will crash at runtime, e.g. "void printf( int x, int y); main() {int a=42, b=99; printf( a, b);}" will apparently crash.
-- A new programming language might want to throw a compile-time error in such a case (as C++ apparently does, according to the slides).
2) In C, depending on compiler options, you can read from an uninitialized variable without a warning
-- A new programming language might want to not auto-initialize any variables, and to throw a compile-time error if they are used before initialization.
3) In C, code like "int a = 41; a = a++" apparently compiles but leaves 'a' in an undefined state because "you can only update a variable once between sequence points" or it becomes undefined, but on many compilers works anyway. A sequence point is "a point in the program's execution sequence where all previous side effects SHALL have taken place and all subsequent side-effects SHALL NOT have taken place".
-- A new programming language might want to throw a compile-time error in such a case
4) In C, the evaluation order of expressions is unspecified. so code like "a = b() + c()" can call b() and c() in any order. If they have side effects then this might matter, yet no compiler error is given. However, the evaluation order of a() && b() IS specified.
-- A new programming language might want to throw a compile-time error when side-effectful code is called in context in which the order of evaluation is unspecified.
Other miscellaneous gotchas:
5) In C, static vars (but not other vars) are initialized to 0 by default.
-- A new programming language might want to either auto-initialize all variables, or to not auto-initialize any variables,
6) The presentation says "C has very few sequence points. This helps to maximize optimization opportunities for the compiler.". This is a tradeoff between optimization vs. principal of least surprise.
-- A new programming language which wanted to make things as simple as possible would maximize 'sequence points', putting them in between practically every computation step. But some new programming languages would choose to minimize sequence points in order to allow the compiler to optimize as much as possible.
7) The presentation says that the standard says that source code must end with a newline.
-- Imo that's a bit pedantic and the ideal programming language would not care if code ended in a newline.
8) In one context (inside a function), the 'static' keyword is used to make a variable persist across calls to that function. But in another context (outside of any function), the same 'static' keyword is used as an access modifier to define visibility to other compilation units!
-- Using the same keyword for two different (albeit related) purposes is confusing. A new programming language might either drop one of those features entirely, or have a distinct keyword for it.
You know if someone's asking me these kinds of questions in an interview it's the sign that something's deeply dysfunctional on the team and there's been some kind of past trauma that makes them feel the need to ask.
In an interview, I would much rather hear a person say something about a statically declared variable with no initialization being poor code to leave behind for the next person than some arcana about the standard.