Reputation: 608
I'm puzzled by behavior I'm seeing with the Android NDK regarding negative shorts as parameters. I've reduced it to a simple example.
I have the following c function:
void shortTest(uint16_t input)
{
uint16_t tmp = -1;
LogInfo("shortTest input: 0x%X\n", input); // wrapper for logging
LogInfo("shortTest tmp: 0x%X\n", tmp);
}
The corresponding Java signature:
void shortTest(short input);
The function is called like this:
short s = -1;
MyLibrary.INSTANCE.shortTest(s);
The output:
INFO : 21:10:01:158 - shortTest:6158: shortTest input: 0xFFFFFFFF
INFO : 21:10:01:158 - shortTest:6159: shortTest tmp: 0xFFFF
This behavior can cause failures, for example with comparisons (input is not equal to tmp). As a work-around I just changed the c function to take a uint32_t and pass in an int. However, I'm concerned about this biting me in other areas as I use uint16's pretty regularly. It seems to somehow force the storage type from 2 bytes to 4. Am I missing something? Is this a bug?
Upvotes: 0
Views: 341
Reputation: 58467
A Java short
is signed, so you're sort of lying to the compiler about what shortTest
will receive.
Let's consider the consequences of this by looking at the following C code and its corresponding ARM assembly:
// Here I'm using int16_t, because that's basically what you've declared that
// you will pass to shortTest from your Java code.
void shortTest(int16_t input) {
printf("shortTest input: 0x%X\n", input);
}
int main() {
shortTest(-1);
}
shortTest:
stmfd sp!, {r4, lr}
mov r1, r0 @ Just pass on input as the second argument to printf
movw r0, #:lower16:.LC0 @ Load string address
movt r0, #:upper16:.LC0 @ ...
bl printf @ Call printf
ldmfd sp!, {r4, pc} @ Return
main:
stmfd sp!, {r4, lr}
mvn r0, #0 @ First argument = 0xFFFFFFFF
bl shortTest @ Call shortTest
mov r0, #0 @ Value to return from main
ldmfd sp!, {r4, pc} @ Return
My main
here would be your Java code, so it's passing 0xFFFFFFFF
to shortTest
, which is fine because for all it knows shortTest
wants a signed short. Then you arrive at shortTest
which (in your case) thinks it received an unsigned short, but it assumes that the caller passed the right kind of value so it won't do any zero-extention.
Then you try to print this value, and if your logging function works like printf
/sprintf
it will implicitly promote your short
to an int
, i.e. it will print a 32-bit value (excluding leading zeroes). And the 32-bit value in this case is 0xFFFFFFFF
.
Upvotes: 1