Hacker News new | past | comments | ask | show | jobs | submit login

The 4-byte (SMTP-command-style) lookup:

  if(*(u_int32_t)cmdword == *(u_int32_t *)"EHLO") {
    handle_ehlo();
  }
The "extern inline" idiom for forcing inlining, which I picked up from Mike Stolarchuk.

Passing and assigning trivial structures by value instead of fiddley pointers.

Arena allocators.

Not so much a trick, but: you can safely free() NULL, which saves a conditional. In the same vein: not only is there no point to casting malloc()'s return value, but there are (admittedly rare) circumstances where doing so can be harmful. So save yourself the typing.

assert(!"message") instead of assert(0).




In your first example you must take care not to run afoul of alignment or aliasing rules. This is not always easy.

Using "extern inline" is a bad idea since no two compilers implement it the same way and none according to spec (that I know of). There is no standard way to force inlining of a function.


You're right, if you do the 4-byte lookup trick on a SPARC you'll get a SIGBUS if you do it at arbitrary offsets. It's not always easy to ensure your comparison target is e.g. at the start of your read buffer, or in its own array on the stack, or in the return value from strdup(). It's definitely a "trick", not a proper form.

I also wouldn't do "extern inline" on a random C compiler; it works on clang, gcc, and Sun's compiler, though.


You probably know this already, but if you pass -std=c99 to gcc, you have to have a separate definition for your inlined functions. Here's how I do it; I welcome suggestions for improvement:

module_a.h: #ifndef INLINE_ # define INLINE_ inline #endif /* INLINE_ */

  INLINE_ void function_a()
  {
  }
inline_defs.c #define INLINE_ #include "module_a.h"


Regarding STMP-command-style lookup: A more portable way to do that is to define a macro to assemble the 4 bytes into unsigned int:

  #define CHAR4_TO_UINT(a,b,c,d)       \
    (                                  \
        ((unsigned int)(a))      |     \
      ( ((unsigned int)(b))<<8  )|     \
      ( ((unsigned int)(c))<<16 )|     \
      ( ((unsigned int)(d))<<24 )      \
    )
then

  unsigned int  ui_cmdword = CHAR4_TO_UINT(
      cmdword[0], cmdword[1], cmdword[2], cmdword[3]
  );
  if( ui_cmdword == CHAR4_TO_UINT('H','E','L','O') )
      handle_helo();
  else if( ui_cmdword == CHAR4_TO_UINT('E','H','L','O') )
      handle_ehlo();
А compiler will generate memory-fetching code once, optimize right side of comparisons into constants and make everything flow fast and safe. You can even use switch statement if you like.


The first trick is handy but unfortunately it breaks aliasing rules.


No, don't do this. It works on x86, but on architectures with load alignment restrictions it can crash on valid data.


It works fine on SPARC too; you just have to know where the data is coming from.

You're right to point out that people should be cautious about this code. I'm just listing my "favorite tricks". I'm not recommending that people use integer casts as their go-to string comparison.


The string literal is (all but -- honestly I'm not sure what the standard says here) guaranteed to be aligned naturally for the platform. The thing on the left hand side is not. If it's a pointer to a heap block, you're fine. If it's a pointer to a token parsed out of string input, it can be anything.

Even architectures that support misaligned accesses can be configured to trap on them and generate unexpected fatal signals.


Sorry, I wasn't talking about the string literal; I was talking about the buffer you're comparing it to. I'm saying: yes, you're right, you have to be careful that it's aligned.


Natural alignment for string literals is generally 1 byte. Natural alignment for uint32_t is generally 4 bytes.


With possibly-inlined, SSE-optimized strcmp variants that compare 8 or 16 bytes of a string at a time (at least according to Valgrind), how much speed is gained these days by casting and using the standard integer comparison instructions?


Although if you want your C to compile with a C++ compiler, you will need to cast the result of malloc(). C++ is more strongly-typed than C, and the compiler will complain.


You're right, that was an oversight. This is one of the more frustrating decisions in C++; it defies the semantics of a "void pointer", breaks existing code, and provides no real safety (the thing we're enforcing "type safety" over being a simple register-width integer used to express any type in the whole system).

But you do sometimes want to cut-paste code from .c files into .cpp files, and this idiom will make your compiler yell.


There are much more subtle differences that will wreak total havoc without the compiler emitting a single squeak. For example, compare this compiled as C or C++:

  #include <stdio.h>
  
  int foo;
  
  int main(void)
  {
      struct foo {
          int a, b;
      } x;
      printf("%zd\n", sizeof(foo));
      return 0;
  }
One should always write code in the best possible way for the language actually in use, even if this is invalid in some other language with superficially similar syntax. In converting code from C to C++, adding a few pointer casts will be the least of your worries.


One way implicit casts for void * are pretty big safety harness actually. You can still interpret any random pointer as "a register width thingamajig". Interpreting any random void * as a valid pointer to a particular type is obviously dangerous.


I like putting the constants on the left side of the comparison... cuts down on the 'missing one equals sign' errors. This can be a readability issue though.


GCC will warn about using an assignment as a truth value in if(), while(), etc., so it's probably safe enough to stick with whatever order you're familiar with and let GCC alert you if you miss an equals sign.


This is frequently called "Yoda conditionals."


That 4 byte lookup's useful. I needed to do it recently and had assumed it wasn't possible.

If memory serves, CodeWarrior for Mac (and possibly other classic Mac compilers) had syntactic sugar mapping single-quoted four-character strings to the ubiquitous OSType. Something like:

    OSType creatorCode = 'TTXT';
where OSType was typedef'd to uint32.


In addition to breaking strict aliasing and alignment as other have pointed out, your first example also assumes little endian and won't work on big endian architectures.

Just don't do it unless it's a quick hack that you absolutely know you'll rewrite correctly within the day, before committing. And even then, write it correctly the first time around.


This construct does not depend on byte order since both sides are using the same (unsafe) type-punning.


I brought up a thread on this on SO a while back. I wanted to find places where it didn't work:

http://stackoverflow.com/questions/328215/does-anyone-know-o...


> you can safely free() NULL, which saves a conditional

This makes for a clearer code, but the conditional is still there, tucked in free's code. Obviously.


I think he means not having to test the pointer before handing it off to free().




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: