C Traps and Pitfalls

Here’s a concise paper describing common C programming pitfalls by Andrew Koening (www.literateprogramming.com/ctraps.pdf) corresponding to be book with the same title.

As a reminder to myself, I’ll spend this page summarizing common mistakes and my history with it.

Here are the mistakes that I don’t make because of certain programming habits:

  • Operator precedence: I use enough parenthesis to not rely on operator precedence
  • Pointer dereferencing: I always do *(p++) instead of *p++ unless it’s idiomatic.
  • for() or if() statements executing only first line: I always surround the block with {} even if it’s just one line. Too often we need to inject an extra line and without {} it becomes a trap.
  • Undefined side effect order: I never do something like y[i++]=x[i]
  • char* p, q: very tempting since C++ style emphasize on pointer as a type over whether the variable is a pointer. I almost never declare multiple variables in one line.
  • Macro repeating side effects: use inline functions instead whenever possible. Use templates in C++.
  • Unexpected macro associations: guard expressions with (). Use typedef.

Did these once before, adjusted my programming habits to avoid it:

  • counting down in for() loop with unsigned running variable: I stick with signed running variables in general. If I’m forced to use unsigned, I’ll remind myself that I can only stop AFTER hitting 1, but not 0 (i.e. i=0 never got executed).

Haven’t got a chance to run into these, but I’ll program defensively:

  • Integer overflow: do a<b instead of (a-b)<0. Calculate mean by adding halfway length to the smaller number (i.e. (a+b)/2 == a + (b-a)/2 given a<b). Shows up in binary search.
  • Number of bits to shift is always unsigned (i.e. -1 is a big number!)

What I learned from the paper:

  • stdio buffer on stack (registered with setbuf()) freed before I/O flushed: use static buffer (or just make sure the buffer lives outside the function call).
  • char type might be signed (128 to 255 are -128 to -1) so it sign extends during upcast. Use unsigned char go guarantee zero extend for upcasting.
  • toupper()/tolower() might be implemented as a simple macro (no checks, incorrect /w side effects)
  • Can index into a string literal: "abcdefg"[3] gives 'd'

Mistakes that I usually make when I switch back from full-time MATLAB programming:

  • Logical negation using ~ operator instead of ! operator.

Common mistakes I rarely make because of certain understanding:

  • Forgetting to break at every case in switch block. It’s hard to forget once you’re aware of the Duff’s device.
  • sizeof(a[])/sizeof(a[0]) after passing into a function does not give array length: hard to get it wrong once you understand that array (declared on stack) has meta-info that cannot be accessed beyond the stack level it’s initialized.

Loading

Symantec Ghost in Windows hangs for mSATA to SATA adapter board Solution: Start Ghost with -NOTRIM

I was trying to image a mSATA SSD with Ghost in Windows (I’m using version 12.0.0.8023 Corporate Edition as I bought the Altiris license) and I ran into internal consistency error 8027 right at the very beginning of the copying process.

For some reason, it doesn’t happen if I boot to the DOS version (provided by Agilent) to do the cloning.

Luckily the status bar tells me what’s going on during the process. I notice it always hangs when ghost tells me that it’s TRIMming the SSD. I looked up the help file (ghost32.exe -help) and noticed that there’s a “-NOTRIM” option. Tried it and the clone completed successfully.

Turns out Symantec is aware of it. The title of the support article is called “Cloning Solid State Disk (SSD) drives fails when using the UEFI 12.0.0.x Ghost executables“.

The summary says it since older versions (11.5.1.x) does not have TRIM, this isn’t a problem, and

“Build 12.0.0.8003 (from GSS 3.0 early build) resolved the issue with the partition restore”

I’m not sure what it means. But the solution is the same as what I did: disable TRIM when copying SSD in Windows (done by the -notrim switch).

Loading

Create user account using email address (like your Microsoft account) in Windows 7/2008

If you live in a mixed environment of Windows 7 and 8/10 computers, you might want to set the usernames to be the same so you can share the files/printers without managing Homegroup.

Nonetheless, in Windows 7/2008, if you try to create a user account using the traditional tool (Local Users and Groups) in Computer Management, you are not allowed to use email address as user name because they banned the at-mark (@):

I searched the web for quite a while, came across stuff like UPN (User Principal Name) without luck (No active directory on Windows 7, nor I want to setup a domain controller in Windows 2008 for home network). Turns out the solution is dead simple: use the “User Account” from Control panel to create the user account. No questions asked!

Loading

Tektronix S1001 DIP switch: Open pin 3 to boot for fast self-test (TDS 744, 744A, 684A, 784A)

Normally for most TDS 500 series oscilloscopes, the DIP switch (S1001) typically all closed for normal operation. The only described use in the service manual is opening pin 6 and 7 to reveal the hidden composite test pattern screen for display adjustment.

However, I got a TDS 744 scope that boots very slow. Almost 5 mintues! To the extent that it’s unbearable. Initially I thought it’s just the non-A type booting slow. Nonetheless, I got a chance to open up another TDS 744A with boots much faster (in half a minute) with calibration seals on, and I noticed the pin 3 of S1001 is opened. Initially I thought it was out of place so I closed it again. Guess what? A slow 5 minute boot sequence!

I suspected opening pin 3 of S1001 puts the oscilloscope in the quick boot mode. I experimented with the pin 3 opened and manually initiating the full blown self-test (utility menu). Turns out I was correct! The full self-test is 5 minutes!

Since it was the settings from the calibration house, I looked up the TDS 744A/684A/784A service manual and noticed that the factory default is with pin 3 opened, while the old TDS 544A manual says pin 3 is closed. I suspect that the self-test for the new models 744/744A/684A/784A is unbearably long that the Tek decided to have it disabled for boot-up self-test.

In any case, try opening pin 3 to have the scopes boot faster!

 

Loading

MATLAB Quirks: struct with no fields are not empty

As far as struct() is concerned, I’m more inclined to using Struct of Array (SoA) over Array of Structs (AoS), unless all the use cases screams for SoA. Performance and memory overhead are the obvious reasons, but the true motivation for me to use SoA is that I’m thinking in terms of table-oriented programming (which I’ll discuss in later posts. See table() objects.): each field of a struct is a column in a table (heterogeneous array).

Since a table() is considered empty (by isempty()) if it has EITHER 0 rows INCLUSIVE OR 0 columns (no fields) and the default constructor creates a 0 \times 0 table, I thought struct() would do the same. NOT TRUE!

First of all, the default constructor of struct() gives ONE struct with NO FIELDS (so it’s supposed to correspond to a 1 \times 0 table). What’s even harder to remember is that struct2table(struct()) gives a 0 \times 0 table.

The second thing I missed is that a struct() with NO fields is NOT empty. You can have 3 structs with NO fields! So isempty(struct()) is always false!

I usually run into this problem when I want to seed the execution with an empty struct() and have the loop expand the fields if the file has contents in it, and I’ll check if the seeded struct was untouched to see if I can read data from the file. Next time I will remember to call struct([]) instead of struct(). What a trap!

At the end of the day, while struct is powerful, but I rarely find AoS necessary to do what I wanted once table() is out. AoS has pretty much the same restrictions as in table() that you cannot put different types in the same field across the AoS, but table allows you to index with variables (struct’s field) or rows (struct array index) without changing the data structure (AoS <-> SoA). So unless it’s a performance critical piece of the code, I’ll stick with tables() for most of my struct() needs.

 

Loading