Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does an observable difference exist using `unsigned long` and `unsigned int` in C (or C++) when both are 32 bits wide?

I'm using an MPC56XX (embedded systems) with a compiler for which an int and a long are both 32 bits wide.

In a required software package we had the following definitions for 32-bit wide types:

typedef   signed int sint32;
typedef unsigned int uint32;

In a new release this was changed without much documentation to:

typedef   signed long sint32;
typedef unsigned long uint32;

I can see why this would be a good thing: Integers have a conversion rank between short and long, so theoretically extra conversions can apply when using the first set of definitions.

My question: Given the above change forced upon us by the package authors, is there a situation imaginable where such a change would change the compiled code, correctly leading to a different result?

I'm familiar with the "usual unary conversions" and the "usual binary conversions", but I have a hard time coming up with a concrete situation where this could really ruin my existing code. But is it really irrelevant?

I'm currently working in a pure C environment, using C89/C94, but I'd be interested in both C and C++ issues.

EDIT: I know that mixing int with sint32 may produce different results when it's redefined. But we're not allowed to use the original C types directly, only the typedef'ed ones.
I'm looking for a sample (expression or snippet) using constants, unary/binary operators, casts, etc. with a different but correct compilation result based on the changed type definition.

like image 925
Johan Bezem Avatar asked Dec 08 '11 08:12

Johan Bezem


3 Answers

In C++ you may run into issues with function overloading. Say you had the following:

signed int func(signed int x) {
    return x + 1;
}

signed long func(signed long x) {
    return x - 1;
}

int main(void) {
    sint32 x = 5;
    std::cout << func(x) << std::endl;
}

Prior to the typedef definition change, the value 6 would be printed. After the change the value 4 would be printed. While it's unlikely that an overload would have behavior that's this different, it is a possibility.

You could also run into issues with overload resolution. Assume you had two functions with the following definitions:

void func(int x);
void func(unsigned int x);

and were calling the functions with:

sint32 x;
func(x);

Prior to the change, the function call was unambiguous, func(int) would be an exact match. After the typedef change, there is no longer an exact match (neither function takes a long), and the compiler fails since it will not be able to determine which overload to invoke.

like image 186
DRH Avatar answered Oct 12 '22 13:10

DRH


It might lead to subtle issues because literal numbers are int by default.

Consider the following program:

#include <iostream>

typedef signed short old16;
typedef signed int old32;

void old(old16) { std::cout << "16\n"; }
void old(old32) { std::cout << "32\n"; }

typedef signed short new16;
typedef signed long new32;

void newp(new16) { std::cout << "16\n"; }
void newp(new32) { std::cout << "32\n"; }

int main() {
  old(3);
  newp(3); // expected-error{{call of overload ‘newp(int)’ is ambiguous}}
}

This leads to an error because the call to newp is now ambiguous:

prog.cpp: In function ‘int main()’:
prog.cpp:17: error: call of overloaded ‘newp(int)’ is ambiguous
prog.cpp:12: note: candidates are: void newp(new16)
prog.cpp:13: note:                 void newp(new32)

whereas it worked fine before.

So there might be some overloads surprises where literals were used. If you always use named (and thus typed) constants, you should be fine.

like image 27
Matthieu M. Avatar answered Oct 12 '22 11:10

Matthieu M.


If a pointer to sint32/uint32 is used where a pointer to int/long is expected (or vice versa) and they don't match int with int or long with long, you may get a warning or error at compile time (may in C, guaranteed in C++).

#include <limits.h>

#if UINT_MAX != ULONG_MAX
#error this is a test for systems with sizeof(int)=sizeof(long)
#endif

typedef unsigned uint32i;
typedef unsigned long uint32l;

uint32i i1;
uint32l l1;

unsigned* p1i = &i1;
unsigned long* p1l = &l1;

unsigned* p2il = &l1; // warning or error at compile time here
unsigned long* p2li = &i1; // warning or error at compile time here

int main(void)
{
  return 0;
}
like image 31
Alexey Frunze Avatar answered Oct 12 '22 13:10

Alexey Frunze