There are several things that are either unspecified or undefined in the various C standards, that can impact portability. The ones you may already know about are:

- Bit-widths of various types.
- Alignment of various types
- Endianness.

In this section we shall discuss a few more that arise in practice and can cause headaches.

Shifts by negative numbers and by numbers greater than *or equal to* the bit-width of the underlying type are undefined. Also, the left of a negative signed integer is also unspecified.

`x>>y`

is usually implemented as `x>>(y&MASK)`

, where `MASK`

can be `31`

(e.g. x86) or `63`

(e.g. PowerPC).
Another potential problem that could come up is that left shifts of signed numbers could pad with zeros instead of the sign bit. Alternatively, it could be defined so that a left- by `N`

was exactly the same as division by `1<`

```
``````
assert( -3>>1 == -2 ); /* arithmetic right */
assert( -3>>1 == 2147483646); /* 32 bit, zero padding right */
assert( -3>>1 == -1); /* right as division */
```

### bsearch() & qsort()

If we are searching an array using the library routine `bsearch()`

and two elements in the array are equal to the key, then it is unspecified which of the two elements is returned.

More problematically, if we are sorting an array using `qsort()`

and two elements in the are array are equal, then it is unspecified how the two elements are ordered. They will be adjacent to each other, but it is unspecified which will be first.

These routines can *and do* differ from implementation to implementation. Consequently, a program will exhibit different behavior on different systems, if it uses `qsort()`

to sort arrays with some equal elements.

Partly because of this, and partly because `qsort()`

ends up being inefficient (in terms of performance), I no longer use `qsort()`

.

Instead, we have written a "template-like" sorting module, along the lines described earlier. This has several benefits:

- We provide a variety of sort functions, including merge sort, so that one can pick the sort algorithm that is best suited for the input data set.
- We use the actual types of the objects to be sorted, not
`void *`

. This makes for safety.
- The compare function is one of the strings that is rewritten, so one can provide a macro instead of a function, which makes for efficiency.

### malloc & sorting on pointers

Occasionally, we need to impose a total ordering on objects. This is useful, for instance, if we want to have a set of objects that efficiently supports the union operation. If we sort all objects in sets in terms of a total ordering, when merging two sets we need to only traverse the elements of each set once.

A possible way of imposing a total ordering on objects is to use the values of the pointers; i.e. object A precedes/succeeds object B exactly if the address of A is less than/greater than the address of B.

Don't do this. The relative order of objects is now dependent on the memory allocator. If you move to a system with a different allocator, the order, and therefore, the behavior of the program will change. Worse, a change in the memory allocation/deallocation in one part of the program can change the relative order of objects, and therefore program behavior, in some other part of the program. This can lead to some non-trivial debugging.

There is one caveat to this. If all objects being are elements of the same array (i.e. they were allocated all at once), then the relative ordering will stay constant. In that case its all right to compare pointers. I would still recommend against it if there is a possibility that the implementation could be changed so that objects were later allocated independently.

Next Prev Main Top Feedback