logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

How-I-learned-to-stop-worrying-and-love-C.md (6457B)


  1. ---
  2. date: 2017-03-15
  3. # vim: tw=80
  4. title: Principles for C programming
  5. layout: post
  6. tags: [C, philosophy]
  7. ---
  8. In the words of Doug Gwyn, "Unix was not designed to stop you from doing stupid
  9. things, because that would also stop you from doing clever things". C is a very
  10. powerful tool, but it is to be used with care and discipline. Learning this
  11. discipline is well worth the effort, because C is one of the best programming
  12. languages ever made. A disciplined C programmer will...
  13. **Prefer maintainability**. Do not be clever where cleverness is not required.
  14. Instead, seek out the simplest and most understandable solution that meets the
  15. requirements. Most concerns, including performance, are secondary to
  16. maintainability. You should have a performance budget for your code, and you
  17. should be comfortable spending it.
  18. As you become more proficient with the language and learn about more features
  19. you can take advantage of, you should also be learning when not to use them.
  20. It's more important that a novice could understand your code than it is to use
  21. some interesting way of solving the problem. Ideally, a novice will understand
  22. your code *and* learn something from it. Write code as if the person maintaining
  23. it was you, circa last year.
  24. **Avoid magic**. Do not use macros[^1]. Do not use a typedef to hide a pointer or
  25. avoid writing "struct". Avoid writing complex abstractions. Keep your build
  26. system simple and transparent. Don't use stupid hacky crap just because it's a
  27. cool way of solving the problem. The underlying behavior of your code should be
  28. apparent even without context.
  29. One of C's greatest advantages is its transparency and simplicity. This should
  30. be embraced, not subverted. But in the fine C tradition of giving yourself
  31. enough rope to hang yourself with, you can use it for magical purposes. You
  32. must not do this. Be a muggle.
  33. **Recognize and avoid dangerous patterns**. Do not use fixed size buffers with
  34. variable sized data - always calculate how much space you'll need and allocate
  35. it. Read the man pages for functions you use and handle their failure modes.
  36. Immediately convert unsafe user input into sanitized C structures. If you later
  37. have to present this data to the user, keep it in C structures until the last
  38. possible moment. Learn of and use extra care around sensitive functions like
  39. strcat.
  40. Writing C is sometimes like handling a gun. Guns are important tools, but
  41. accidents with them can be very bad. You treat guns with care: you don't point
  42. them at anything you love, you exercise good trigger discipline, and you treat
  43. it like it's always loaded. And like guns are useful for making holes in things,
  44. C is useful for writing kernels with.
  45. **Take care organizing the code**. Never put code into a header. Never use the
  46. `inline` keyword. Put separate concerns in separate files. Use static functions
  47. liberally to organize your logic. Use a coding style that gives everything
  48. enough breathing room to be easy on the eyes. Use single letter variable names
  49. when their purpose is self-evident and descriptive names when it's not, and
  50. avoid neither.
  51. I like to organize my code into directories that implement some group of
  52. functions, and give each function its own file. This file will often contain
  53. lots of static functions, but they all serve to organize the behavior this file
  54. is responsible for implementing. Write up a header to give others access to
  55. this module. And use the Linux kernel coding style, god dammit.
  56. **Use only standard features**. Do not assume the platform is Linux. Do not
  57. assume the compiler is gcc. Do not assume the libc is glibc. Do not assume the
  58. architecture is x86. Do not assume the coreutils are GNU. Do not define
  59. \_GNU_SOURCE.
  60. If you must use platform-specific features, describe an interface for it,
  61. then write platform-specific support code separately. Under no circumstances
  62. should you ever use gcc extensions or glibc extensions. GNU is a blight on this
  63. Earth, do not let it infect your code.
  64. **Use a disciplined workflow**. Have a disciplined approach to version control,
  65. too. Write thoughtful commit messages - briefly explain the change in the first
  66. line, and add justification for it in the extended commit message. Work in
  67. feature branches with clearly defined goals, and do not include changes that
  68. don't serve that goal. Do not be afraid to rebase and edit your branch's history
  69. so that it presents your changes clearly.
  70. When you have to return to your code later, you will be thankful for the
  71. detailed commit message you wrote. Others who interact with your code will be
  72. thankful for this as well. When you see some stupid code, it's nice to know what
  73. the bastard was thinking at the time, especially when the bastard in question
  74. was you.
  75. **Do strict testing and reviews**. Identify the different possible code paths
  76. that your changes may take. Test each of them for the correct behavior. Give it
  77. incorrect input. Give it inputs that could "never happen". Pay special attention
  78. to error-prone patterns. Look for places to simplify the code and make the
  79. processes clearer.
  80. Next, give your changes to another human to review. This human should apply the
  81. same process and sign off on your changes. Review with discipline as well,
  82. taking all of the same steps. Review like it'll be your ass on the line if
  83. there's a problem with this code.
  84. **Learn from mistakes**. First, fix the bug. Then, fix the real bug: your
  85. process allowed this mistake to happen. Bring your code reviewer into the
  86. discussion - this is their fault, too. Critically examine the process of
  87. writing, reviewing, and deploying this code, and seek out the root cause.
  88. The solution might be simple, like adding strcat to the list of functions that
  89. should trigger your "review this code carefully" reflex. It might be employing
  90. static analysis so a computer can detect this problem for you. Perhaps the code
  91. needs to be refactored so it's simpler and easier to spot errors in. Failing to
  92. reflect on how to avoid future fuck-ups would be the real fuck-up here.
  93. - - -
  94. It's important to remember that rules are made to be broken. There may be cases
  95. where things that are discouraged should be used, and things that are encouraged
  96. disregarded. You should strive to make such cases the exception, not the norm,
  97. and carefully justify them when they happen.
  98. C is the shit. I love it, and I hope more people can learn to see it the way I
  99. do. Good luck!
  100. [^1]: Defining constants with them is fine, though