-2

I am writing a lot of C code for embedded systems lately and I always wonder if I should use signed or unsigned values for my return type. Is there a "right" way to do so?

I personally prefer the signed types, because they give me a broader range for expressing myself. But I work with a lot of legacy code which uses solely unsigned types.

Example 1 (my code):

int16_t my_initializer(void)
{
  return -1; //-> error (critical)
 ...
  return 0;  //-> everything is OK
 ...
  return 1;  //-> warning (non critical)
 ...
  return 2;  //-> another warning
}

Example 2 (legacy code):

uint16_t my_initializer(void)
{
  return 0;  //-> everything is OK
 ...
  return 1;  //-> warning (non critical)
 ...
  return 2;  //-> another warning or an error
 ...
}

I took a look in the linux kernel coding style and the Google C++ style guide but I found no clue whether to use the signed or unsigned types. Is there a "best practice" out there?

old_timer
  • 69,149
  • 8
  • 89
  • 168
0xFAB
  • 1
  • 1
  • 2
  • 1
    Given the issues with mixing signed and unsigned in the same expression, I would choose signed. This question is very close to Opinion Based. – Richard Critten Feb 17 '18 at 11:12
  • *"I personally prefer the signed types, because they give me a broader range for expressing myself."* - this is a weird statement, integer type should be selected based on the range of numbers that it is supposed to hold. – user7860670 Feb 17 '18 at 11:14
  • 3
    How did you rule out using enums? – Yunnosch Feb 17 '18 at 11:16
  • https://stackoverflow.com/questions/385975/error-handling-in-c-code – RedArrow Feb 17 '18 at 11:21
  • You could decide that negative numbers indicate errors and positive numbers are warnings, or vice versa (with 0 as the OK status). As long as you're consistent, it really won't matter very much. (There are code bases that use 0 to indicate an error — extra information available elsewhere, and non-zero indicates some sort of success.) – Jonathan Leffler Feb 17 '18 at 11:29
  • Signed numbers might have a tiny performance benefit, as it is typically very efficient to check for positive/negative/zero (via the processor's status register). I'd still wrap these in an enum, though. – Erlkoenig Feb 17 '18 at 11:32
  • The performance gain is negligible. If you use 0 as successful, then the compiler can emit instructions that test for non-zero. However, you still need extra comparisons if you want more detailed information than pass or fail. So, use an `enum` and `switch`. In general, when code is easier to read, there are fewer injected defects and it is easier to maintain (by you and others). – Thomas Matthews Feb 17 '18 at 18:29

3 Answers3

2

If the return value is ALWAYS used to return a status, I would prefer to issue a descriptive value, such as

 enum errorcode
 {
     SUCCESS,
     OUT_OF_MEMORY,     
     INVALID_ARGUMENT,   // Like -1 to sqrt
     OUT_OF_RANGE,       // Indexing into an array or similar.
 };

 errorcode status = my_function();

This also has the advantage of debugging with symbols, and in your debugger you get something like:

gdb> p status
SUCCESS
...
gdb> p status
OUT_OF_MEMORY

rather than trying to remember if 1 is out-of memory or something else.

Whether something is a "warning" or an "error" is quite hard to determine. Is it an error to overwrite an existing file, or just a warning? What does the recipient code do with the "warning" if you have already overwritten the existing file? return WARN_FILE_EXISTED; doesn't exactly help here, as there is nothing the caller can meaningfully achieve there - either have a flag that says "don't overwrite existing files" or "do overwrite existing flag" (or two separate functions, etc)

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
0

The C standard says that overflows during calculations of singned types are undefined, on some targets overflow exceptions are triggered. For unsigned types overflow is defined. That is the reason why unsigned is preferred by some developers. Also bit shift operations are well defined on unsigned and implementation dependent for signed. For returning error codes there is no reason for preferring signed or unsigned. Some use the convention to teturn positive values on sucess and negative on error. I prefer int because it is shorter to read than unsigned. I use unsigned only for calculations that overflow and bit manipulations.

notan
  • 359
  • 1
  • 10
0

It depends on the code format you are dessigning . With signed a better and understanding code agn be produce where negative value can repesents the faults and errors . Where as with signed u can have a long positive range of digits so it depends on the theme for what you are using that variable .