?
Solved

mixing 16 and 32-bit integers in C

Posted on 2005-03-29
42
Medium Priority
?
709 Views
Last Modified: 2012-06-22
Hello.

As may be familiar to some of you, we are in the middle of
a port from a 16-bit DOS to 32-bit NT enviornment, a process
which has raised some concerns on my part about the fact that
the NATIVE types int, and unsigned, by def'n, have grown from 2 to 4 bytes.

Problem is  that in our application, we have always
had some types whose "size" is always going to be the same,
that is:

uint_16 is unsigned short
and
int_16 is signed short

Accordingly, I'm concerned about two effects of this process.

1. Suppose I have
int_16 my_int_16;
int my_native_int;

Under 16-bit, it's entirely OK to say
my_int_16 = my_native_int;         (A)
(or)
my_native_int = my_int_16;         (B)
(after all, the types are essentially the same).

However, under 32-bit, statement (A) assigns a 32-bit
var to a 16-bit and statement (B) does the opposite.
My gut feeling here is that I'm OK since by nature
of the application,  the data would not exceed the
range (-32768 to 32767), so I'm not that worried
about TRUNCATIONS. However, I need to
be sure that I did not overlook anything, either w/
truncations or otherwise, and brutal honesty here would be appreciated :)

2. My greater concern is passing values to functions.
For example:

void foo(int_16 i16, char *s);
void bar(char *s, int_16 i16);

To which I pass value as follows:
int i;
foo (i, "HELLO");
bar("GOODBYE", i);

My worry here is:  will passing BY VALUE, a 32-bit variable
(i) to a function expecting a 16-bit value mess up the stacK?
Again, I'm not concerned about truncations, since by
nature of the application, i will never exceed 16-bit
values but more concerned about the internal stuff like
that stack.

3. I realize, btw, that if I am passing by REFERENCE that
I will have to make the types compatible, but I'm hoping
that the compiler will point out those instances to me.

Thanks
Stevefromc
0
Comment
Question by:Stephen Kairys
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 17
  • 6
  • 4
  • +4
42 Comments
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 13654094
If you pass a value by value, a conversion will be made before passing, so, no mess
If you pass a pointer, consider this:

If original pointed data is 32 bits and function accepts a 16 bit data, then, no problem, you will read first half of data without problem, but, careful!! this will work only in little endian processors like Intel, but not in big endian like motorola.

If original pointed data is 16 bits and function expects a pointer to a 32-bits data, then function will read garbage when pointer is de-referenced.
0
 
LVL 22

Expert Comment

by:grg99
ID: 13654129
>If original pointed data is 32 bits and function accepts a 16 bit data, then, no problem, you will read first half of data without problem, but, careful!! this will work only in little endian processors like Intel, but not in big endian like motorola.

No, it really won't work well in either case.   If the 32-bit data contains 0x12345678, then the called function is going to read and/or write just one half of that.  In either case it is going to do the wrong thing.  And the result isnt going to be correct either, on either archtecture.
0
 
LVL 12

Accepted Solution

by:
stefan73 earned 156 total points
ID: 13654136
Hi stevefromc,
You can mix them, but make sure that your calling functions know the prototype of the called function, so the conversions can be done correctly. As Jaime already said, don't mix pointers. It's a mess, as you're depending on endianess.

Cheers!

Stefan
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 4

Author Comment

by:Stephen Kairys
ID: 13654162
Grg99,
Hi. Thanks for the response. Are you talking about
passing by reference here (a pointer) or by value?

If by pointer, I'm not worried because I'm letting the compiler
find those for me. I'm mostly worried about the pass-by-
value cases where the compiler just shrugs its shoulders
and says "not my problemo!" :)

0
 
LVL 46

Assisted Solution

by:Kent Olsen
Kent Olsen earned 148 total points
ID: 13654176

Back up a second....

The compiler is going to place a value on the stack based on the data type.  If the data type is a 16-bit integer, a 16-bit integer will be put on the stack.  If the data type is a 32-bit integer, a 32-bit integer will be put on the stack.

The inverse holds true from the called function.  If the passed parameter is a 16-bit integer then a 16-bit integer will be read from then stack.  Similarly, if a passed parameter is a 32-bit integer then a 32-bit integer will be read from the stack.

As long as you prototype your functions, you should have no problem.



Kent
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 13654178
Stefan,
Good call on the prototype. I had written a test program
that used prototypes but I had not realized that
of course if the callers don't know the prototypes,
I may be asking for trouble.

THANK YOU. I had overlooked that issue.
stevefromc
0
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 13654182
from grg99,
> No, it really won't work well in either case.   If the 32-bit data contains 0x12345678, then the called
> function is going to read and/or write just one half of that.

from author,
>the data would not exceed the range (-32768 to 32767), so I'm not that worried about TRUNCATIONS.
0
 
LVL 12

Expert Comment

by:stefan73
ID: 13654243
stevefromc,
> if the callers don't know the prototypes, I may be asking for trouble

Definitely. A good compiler should issue a warning in this case.

Some systems which pass their arguments in registers (such as SPARC) are less error-prone, but an Intel system definitely doesn't like wrong types.

BTW: shouldn't stack data be aligned, anyway? Otherwise IA32 memory access gets slow, and with that tiny register set, IA32 is using LOTS of stack access...

Stefan
0
 
LVL 22

Expert Comment

by:grg99
ID: 13654708
from grg99,
> No, it really won't work well in either case.   If the 32-bit data contains 0x12345678, then the called
> function is going to read and/or write just one half of that.

from author,
>the data would not exceed the range (-32768 to 32767), so I'm not that worried about TRUNCATIONS.

Doesnt help--  if the original value is -0x1234, the called function will see it as -1234 if little-endian CPU, but when it stores back a reply of say, 5678, the caller will see this as 0xFFFF5678,  which is a bit off.

You have to be mighty careful, C isnt a very good type-conflict policeman.

0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 13654743
Grg99,
Are you talking about pass-by-ref. or pass-by-value?

If pass-by-value, from what I've been hearing I'm
OK if I'm prototyping reliably although I admit there
may be some missing prototypes in our system.
Thanks
0
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 13654790
steve,
I think your better chance is to NEVER USE fundamental data types, but only typedef defined types like int_16, int_32, uint_16 or event int_8.
Will be easy to make a search/replace in all your source code...
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 13654837
Jamie,
Yeah, that's probably a good idea at some point but
we're talking hundreds of modules here and a looming
deadline...

maybe one day,  though....

Thanks
0
 
LVL 55

Expert Comment

by:Jaime Olivares
ID: 13654907
Sometimes, the harder work is the shortest, maybe you will spend 3 or 4 days to decide to come back to my suggestion.
There are hundreds of tools in the internet that can make a global replaces...
0
 
LVL 22

Assisted Solution

by:grg99
grg99 earned 148 total points
ID: 13655385
The two cases are:

pass-by-value:  The compiler pushes whatever size item the prototype says should be there, then withg luck the right size item will get to the called function

pass-by-address:  The compiler just passes the address of the item, so int types had better match width-wise.

I agree with jamie to a point-- it's often very helpful to walk through the code and change every "int" to a explicit sized type:  like int16 or int32, uint16 or uint32.   You often find lots of errors that way.  Does require quite a bit of thinking, but IMHO that's better than wildly flailing through bus errors for the next three months.

0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 13655439
Greg,
You say

>> With luck, the right size item will get to the called
function<<
in the pass-by-value case where the prototype IS there.
if the prototype is there, why is there "luck" involved?
(or were saying that "tongue in cheek?" :)
Thanks
0
 
LVL 22

Expert Comment

by:grg99
ID: 13656499
>if the prototype is there, why is there "luck" involved?

Becuz we're depending on humans to supply the prototype, which might be:

(1)  The prototype cut and pasted from the wrong file.

(2)  The right prototype, but has the loose parameter types, like "int"

(3)  The right prototype, but uses type names that are differently defined in the other file, or call in a different include file to define the types.

(4)  The right prototype, too bad a later include file also prototyped it, and differently.


There are languages like Pascal, Delphi, Modula, and ADA where this can't happen, as prototypes are more firmly nailed down and unchangeable by human hands, but we're stuck with C.

0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 13656580
>>2)  The right prototype, but has the loose parameter types, like "int"<<

So, you say "loose parameter types". But, under 32-bit,
int is int is int i.e. is always 32 bits (4 bytes).  So, assuming
I'M DEALING EXCLUSIVELY WITH MY NEW 32-BIT APP,
and that the prototype does not have any of the other
issues that I mentioned, can I safely say that if I pass
a 16-bit integer to it, that this integer will be promoted
correctly??

Ah....Pascal. Sometimes I do long for the "good old days"...
I used it in a previous application this company supported
for over 10 years so it can't be that bad ;)

Thanks


0
 
LVL 16

Assisted Solution

by:PaulCaswell
PaulCaswell earned 148 total points
ID: 13659979
Sorry to chime in late in the discussion but I'm going to anyway.

You are right to try to use the compiler to find as many errors as possible. It, after all, is the expert in syntax. You must therefore prepare your code as far as possible.

I have gone through similar processes myself and here are some points to bear in mind.

1. Look carefully for places where strict type checking is not possible.

Most Windows compilers will not check sprintf/scanf parameters against the format! I understand gcc DOES so it may be worth running your code through the GCC compiler to highlight more potential problems.

Other areas to check, obviously, will be other uses of the '...' parameter list prototype.

2. The main headache will occur in your structures. All old data files will stop working to a greater or lesser extent.

3. The worst ones will be the subtle ones. Things like 'n |= 0x8000;'.

More generally, these are the ones where an assumption has been made that no longer holds and the assumption is part of the control of the algorithm.

4. The subtlest ones will bite near the end of your cycle. Stuff like passing an address of some data in a message to another application. The new 32bit environment is probably a memory-protected environment so not only will the address be invalid but you will have to make use of some IPC process to share data between applications. This is one that bit me hard. I was just a couple of days from deadline and I thought I just had one crash bug left to track down! I actually had to design, build test and implement an IPC process in two days. Suffice it to say we went more than a week over deadline.

So, to summarise:

Use tools to help. Using several compilers should be seriously considered as an option.

Decide what MUST stay the same. Data structures for example.

Be prepared to doubt all code.

Schedule in a whole bunch of extra time at the end for 'unforseen issues'. There WILL be at least one.

To cover your actual questions:

1. Honestly, NEVER assume. Even if currently your figures never break 16bits, they almost certainly WILL.

2. and 3. Hopefully, prototyping will help here but, again, do not assume. Be prepared to rename a type or a structure to find all references and/or assumptions. Be extremely suspicious of ALL casting.

Good luck.

Paul
0
 
LVL 22

Expert Comment

by:NovaDenizen
ID: 13681585
1. (already mentioned) Make sure all functions are prototyped correctly, and use compiler switches if possible to require all functions to be prototyped.
2.  Turn on ALL WARNINGS for your compiler.  This ought to alert you to every time a wider type is implicitly cast down to a smaller type, and it also ought to bring to your attention any problems with implicit signed/unsigned conversions and comparisons.  Fix each warning individually until your code compiles without warnings.

0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 13961036
Hey everyone.

Somehow I missed these last two responses...sorry about that because they certainly contains
some thought-provoking info.

In any event, Paul, I do have some follow-ups to your points:(Nova, I'm responding to
you in a separate post).

>> The main headache will occur in your structures. All old data files will stop working to a greater or lesser extent.<<

Yep. We've seen that behavior in testing. Fixed the ones found that way, and  also went through
the H file containing our main file structures to find any other instances of fields declared
with native types.

>> The worst ones will be the subtle ones. Things like 'n |= 0x8000;'.<<
I tried this in a test program...

void main(int argc, char *argv[])
{
       int x = 0;
       unsigned ux = 0;

#ifdef NTCONSOLE // 32bit
       x |= 0x8000;
       printf("ntconsole. x = %d\n", x);
       ux |= 0x8000;
       printf("ntconsole. ux = %u\n", ux);
#else // 16bit
       x |= 0x8000;
       printf("dos. x = %d\n", x);
       ux |= 0x8000;
       printf("dos. u = %u\n", ux);
#endif
}

For NTCONSOLE, I get the following:
  ntconsole. x = 32768
  ntconsole. ux = 32768
makes sense.

For DOS:
  dos. x = -32768
  dos. ux = 32768

Is that what you mean (that x is NEGATIVE 32768 in 16-bit and POSITIVE 32768 in 32bit)?


>> Honestly, NEVER assume. Even if currently your figures never break 16bits, they almost certainly WILL<<
Please clarify...are you saying that I'm asking for trouble for FUTURE modifications to my
program that might alter the nature of my data? For example, if one of the data elements
in question is a record number that can never go above 32767, what's the problem?

Thanks!
stevefromc
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 13961073
Nova,

>>(already mentioned) Make sure all functions are prototyped correctly, and use compiler switches if possible to require all functions to be prototyped.<<


Looks like my compiler does not HAVE such a switch :(

>>Turn on ALL WARNINGS for your compiler.  This ought to alert you to every time a wider type is implicitly cast down to a smaller type, and it also ought to bring to your attention any problems with implicit signed/unsigned conversions and comparisons.  Fix each warning individually until your code compiles without warnings.<<

Unfortunately, the version of the compiler I am using does not seem to catch this type of
thing (e.g. for parmeters, having a 32-bit int in a prototype and a 16-bit int in the function def'n,
or even (gulp!) an int param in the proto vs. a CHAR param in the function def'n). :(

Thanks
stevfromc
0
 
LVL 22

Expert Comment

by:grg99
ID: 13961214
You might look at pclint.com, their protgram diagnoses many hundreds of subtle gotchas.  Not cheap but worth it for large projects.
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14130163
Jmcg,

Thanks. I guess I could use a response from Paul (and
maybe from Nova) to the responses I posed.

In any event, whether or not they have responded, I'll
review this in the next week or two (depending on
when I will be on vacation), and attempt to close it.
Thanks
stevefromc
0
 
LVL 16

Expert Comment

by:PaulCaswell
ID: 14130706
SteveFromC,

>>Is that what you mean (that x is NEGATIVE 32768 in 16-bit and POSITIVE 32768 in 32bit)?
That and many other ramifications of using absolute values in code. If your editor can do it, try searching for all absolute numbers and make some educated guesses about how it will break. My example is just one of the many possible effects of making assumptions over the size of something.


>>Please clarify...are you saying that I'm asking for trouble for FUTURE modifications to my
>>program that might alter the nature of my data? For example, if one of the data elements
>>in question is a record number that can never go above 32767, what's the problem?
Generally speaking, if you intend to use a value more than once, #define it somewhere with a meaningful name so at least you can guess what you meant when you come back to it in a few years time. This is good practice anyway and makes this kind of process much easier later on.

Paul
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14130924
Thanks, Paul.

Yeah,we had a whole bunch of -1's. We have a field
(and I'm changing the nature of the application since
its proprietary) that is UNSIGNED_16 and is used to
designate a max# of viists to a doctor. However,
if there is no limit, we store (-1) in it.

This caused the compiler to complain when I had
code like

if (max_visits == -1)
Under 16-bit the compiler didn't mind, under 32-bit
it told me that comparsion result always 0.

Long story short, we established a #define
#define NO_LIMIT_VISITS (unsigned_16)-1

and wherever I saw this compiler warning, changed
-1 to NO_LIMIT_VISITS.

That being said, however, I'm not sure of the connection
between that concept, and what you said about
program modifications that alter the nature of my data?
Thanks
0
 
LVL 16

Expert Comment

by:PaulCaswell
ID: 14131059
>>modifications to my program that might alter the nature of my data
I'm not sure where I wasnt clear. Essentially, all code gets modified sometime, its better to make thyat easy in the future while its still fresh in your mind by naming your constants well.

BTW:

>>#define NO_LIMIT_VISITS (unsigned_16)-1
Although in this case there is less of an issue, it is a good idea to add brackets around #defines unless there is a good reason not to.

E.G.

#define FileNameLength 8+1+3+1

...

struct
{
 char twoFileNames[FileNameLength*2]; // This becomes [8+1+3+1*2].
}

Paul
0
 
LVL 22

Expert Comment

by:grg99
ID: 14131561
if you have an unsigned variable, it can never be equal to -1.  But if you store 65535 in it (if it's a 16-bit integer), or 4294967295 (32-bit int) then it will compare equal to -1.     Beware.

0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14131565
Paul,

OK, now I see your point about good naming making modifications easier. Guess I wasn't thinking straight (that's what I get for trying to do any serious work before having
breakfast :) )

And don't get me started on variable names  that make
no sense like i, j, or dd, ddd, etc. etc. Not only does
it make the program harder to figure out when someone
else comes back to it (or even yourself) but such practice
can only serve to cause confusion during development :)

Thanks again. I think we're good here, re: the
issues you discussed w/ me.  Have a good day.
stevefromc


Good call about the brackets around the #defines.

0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14131599
>>if you have an unsigned variable, it can never be equal to -1.  But if you store 65535 in it (if it's a 16-bit integer), or 4294967295 (32-bit int) then it will compare equal to -1.     Beware.<<

Yeah, that's why I casted -1 to unsigned_16.

In the ideal world though, I guess this field should've
been signed, but we were not in a position to change
something at that level. Live and learn :)

Thanks
stevefromc


0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14256506
Jmcg,

Thanks for trhe reminder. If at all possible, I would
appreciate having a little more time here. I am getting
ready to go on vacation, and will not have time to
review this until I get back. I should be able to do so
by apx July 5 or 6. is that ok?

Thanks
stevefromc
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14258080
Thanks, and sorry for the delay. It' s been crazy busy
for me at work but I still need to take care of this.

stevefromc
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14332838
jmcg,

OK. I had a chance to glance through this question and I'm not quite sure how to handle it.
I want to assign points. Experts have given their time and energy to providing comments, etc,
and it would be unfair NOT to give out any points.

Problem is that while there are some answers near the top that proviide some info, the
discussion became so extensive (and I blame myself for that :) ), that I'd have a hard time
figuring out exactly how to distribute the points.

So, any hints would be greatly appreciated.

Thanks
Steve
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14333487
Actually (and maybe I'm wrong), there have been times where I have actually split points
amongst answers from ONE expert, when multiple answers from him/her proved useful.

I'll review the answers as per your guidelines over the next week or so and close this out.

Thanks
Steve
0
 
LVL 4

Author Comment

by:Stephen Kairys
ID: 14569382
I'm fine with the above. I do though, have one
quick follow-up for Stefan...

>>You can mix them, but make sure that your calling functions know the prototype of the called function, so the conversions can be done correctly. As Jaime already said, don't mix pointers. It's a mess, as you're depending on endianess.<<

Just need a little clarification on "depending on
endianess"

Thanks
Steve


0
 
LVL 12

Expert Comment

by:stefan73
ID: 14627220
> "depending on endianess"
Please ask a separate question - otherwise, your question looks like a FAQ :-)

Stefan


0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This is a short and sweet, but (hopefully) to the point article. There seems to be some fundamental misunderstanding about the function prototype for the "main" function in C and C++, more specifically what type this function should return. I see so…
Examines three attack vectors, specifically, the different types of malware used in malicious attacks, web application attacks, and finally, network based attacks.  Concludes by examining the means of securing and protecting critical systems and inf…
The goal of this video is to provide viewers with basic examples to understand and use pointers in the C programming language.
The goal of this video is to provide viewers with basic examples to understand opening and reading files in the C programming language.
Suggested Courses
Course of the Month10 days, 2 hours left to enroll

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question