Are signed/unsigned mismatches necessarily bad?
Here is my program:
int main(int argc, char *argv[]) {
unsigned int i;
for (i = 1; i < argc; i++) { // signed/unsigned mismatch here
}
}
argc is signed, i is not. Is this a problem?
"signed/unsigned mismatches" can be bad. In your question, you are asking about comparisons. When comparing two values of the same base type, but one signed and one unsigned, the signed value is converted to unsigned. So,
int i = -1;
unsigned int j = 10;
if (i < j)
printf("1\n");
else
printf("2\n");
prints 2, not 1. This is because in i < j, i is converted to an unsigned int. (unsigned int)-1 is equal to UINT_MAX, a very large number. The condition thus evaluates to false, and you get to the else clause.
For your particular example, argc is guaranteed to be non-negative, so you don't have to worry about the "mismatch".
It is not a real problem in your particular case, but the compiler can't know that argc will always have values that will not cause any problems.
Its not bad. I'd fix compiler warnings concerning signed/unsigned mismatch because bad things can happen even if they are unlikely or impossible. When you do have to fix a bug because of signed/unsigned mismatch the compiler is basically saying "I told you so". Don't ignore the warning its there for a reason.
It is only indirectly a problem.
Bad things can happen if you use signed integers for bitwise operations such as &, |, << and >>.
Completely different bad things can happen if you use unsigned integers for arithmetic (underflow, infinite loops when testing if a number is >= 0 etc.)
Because of this, some compilers and static checking tools will issue warnings when you mix signed and unsigned integers in either type of operation (arithmetic or bit manipulation.)
Although it can be safe to mix them in simple cases like your example, if you do that it means you cannot use those static checking tools (or must disable those warnings) which may mean other bugs go undetected.
Sometimes you have no choice, e.g. when doing arithmetic on values of type size_t in memory management code.
In your example I would stick to int, just because it is simpler to have fewer types, and the int is going to be in there anyway as it is the type of the first argument to main().
Related
I used to think that in 64-bit Obj-C runtime BOOL is actually _Bool and it's a real type so it's safe to write like this:
BOOL a = YES;
BOOL b = NO;
if (a != b) {...}
It's been working seemingly fine but today I found a problem when I use bit field structs like this:
typedef struct
{
BOOL flag1 : 1;
} FlagsType;
FlagsType f;
f.flag1 = YES;
BOOL b = YES;
if (f.flag1 != b)
{
// DOES GET HERE!!!
}
It seems that BOOL returned from the bit field is equal to -1 while the regular BOOL is 1, and they are not equal!!!
Note that I am aware of the situation when an arbitrary integer number is cast to BOOL and therefore becomes a "strange" BOOL which is not safe to compare.
However in this situation, both flag1 field and b were declared as BOOL and never cast. What is the problem? Is this a compiler bug?
The bigger question is if it's really safe to compare BOOLs at all or should I write a XORing helper function? (It would be such a chore, because boolean comparisons are so ubiquitous...)
I do not repeat that using a C boolean type solves the problems one can have with BOOL. That's true – in particular here, as you can read below –, but most of the problems resulted from a wrong storage into a boolean (C) object. But in this case _Bool or unsigned (int) seem to be the only possible solution. (Except of solutions with extra code.) There is a reason for it:
I cannot find a precise documentation of the new behavior of BOOL in Objective-C, but the behavior you found is something between bad and buggy. I expected the latest behavior to be analogous to _Bool. That's not true in your case. (Thanks for finding that out!) Maybe this is for backwards compatibility. To tell the full story:
In C an object of the type int is signed int. (This is a difference to char. For this type the signedess is implementation defined.)
— int, signed, or signed int
ISO/IEC 9899:TC3, 6.7.2-2
Each of the comma-separated sets designates the same type, […]
ISO/IEC 9899:TC3, 6.7.2-5
But there is a weird exception for historical reasons:
If the int object is a bit-field, it is implementation defined, whether it is a signed int or an unsigned int. (Likely this is because some CPUs in the past could not automatically expand the sign of a partial byte integer. So having an unsigned integer is easier, because nulling the top bits is enough.)
On clang the default is signed int. So according to full-width integers int always denotes a signed integer, even it has only one bit. An int member : 1 can only store 0 and -1! (Therefore it is no solution to use int instead.)
Each of the comma-separated sets designates the same type, except that for bit-fields, it is implementation-defined whether the specifier int designates the same type as signed int or the same type as unsigned int.
ISO/IEC 9899:TC3, 6.7.2-5
The C standard says that a boolean bit-field is an integer type and therefore takes part on the weird integer signedness rule for bit-fields:
A bit-field is interpreted as a signed or unsigned integer type consisting of the specified number of bits.
ISO/IEC 9899:TC3, 6.7.2.1-9
This is the behavior you found. Because this is meaningless for 1 bit booleans types, the C standard explicitly denotes that storing a 1 into a boolean bit-field has to compare equal to 1 in every case:
If the value 0 or 1 is stored into a nonzero-width bit-field of type _Bool, the value of the bit-field shall compare equal to the value stored.
ISO/IEC 9899:TC3, 6.7.2.1-9
This leads to the strange situation, that an implementation can implement booleans of width 1 as { 0, -1 }, but has to fulfill 1 == -1. Great.
So, the short story: BOOL behaves like an integer bit-field (conforming to the standard), but does not take part on the extra requirement for _Bools.
I think this is, because of legacy code. (One could expect -1 in the past.)
I had an interesting problem today. As part of practice with my fluency with objective-c, as I sit in math class, I write programs for each problem done on the board in hopes to increase my math and programming capabilities.
However, today I encountered a problem. One of the questions was something like "Find the greatest prime number that 10564245 (<-- example number) is divisible by"
So, I went in and made the program. I got as far as doing the loop for values to check, and then began to code the part where it checks the reminder, and if it's 0, it logs it, and if it isn't, it skips it.
However, since the number is too big to be an int, it had to be a double. When I tried to plug the number in, the program gave me errors when I wanted to use the % operator with the double. Is there any way to find remainders if you have a very large number?
Thanks
ERROR: Invalid operands to binary expression
EDIT: SOLVED!
I took a little from each answer. We have the fmod capability, but I ended up using long instead of int, I don't know why I didn't think of the origionally
Use fmod().
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[]) {
double a = 77879878978942312315687897;
double b = 10;
printf("%.f",fmod(a, b));
}
Double values aren't precise for integer values and so probably shouldn't be used for this sort of thing. Instead, you can use a long into or even a long long int to do your calculations
Is it possible to terminate a while() loop with an unsigned int? For example I want to terminate a while() when the user enters a negative value. But I want it to be any negative value, not just -1.
Not that I think this is a good idea but, in C at least, you can check if your unsigned integer is greater than INT_MAX (for two's complement anyway, not so sure about the sign/magnitude and one's complement variants but they're probably rare enough that you could safely ignore them until a problem pops up).
This is, of course, assuming it was read in as an integer and converted to unsigned somewhere - if you use customised input routines expecting only unsigned numbers, they may barf at the presence of a leading - sign.
If I compile the following program with g++ and enable warnings for conversions (-Wconversion)
#include<stdint.h>
int main() {
uint16_t foo = 1;
foo += 1;
return 0;
}
I get a warning, warning: conversion to uint16_t from int may alter its value.
Fine, if the 1 in foo+=1 is interpreted as int, but what about:
foo+=static_cast<uint16_t>(1);
I get the same warning, shouldn't the operator work when the types of both sides are the same, w/o converting to an int?
I got an explanation, it comes from C but this should be equally valid in C++:
Specify a number literal as 8 bit?
For arithmetic, all operands are promoted to int if they are smaller. This explains the problem, and why it doesn't trigger on initialization, or when casting explicitely, because the cast will be undone to calculate the sum with ints.
I have this code:
unsigned int k=(len - sizeof(MSG_INFO));
NSLog(#"%d",k);
for( unsigned int ix = 0; ix < k; ix++)
{
m_pOutPacket->m_buffer[ix] = (char)(pbuf[ix + sizeof(MSG_INFO)]);
}
The problem is, when:
len = 0 and sizeof(MSG_INFO)=68;
k=-68;
This condition gets into the for loop and is continuing for infinite times.
Your code says: unsigned int k. So k isn't -68, it's unsigned. This makes k a very big number, based around a 4 byte int, it would be 4294967210. This is obviously quite a lot more than 0, so it's going to take your for loop a while to get that high, although it would terminate eventually.
The reason you think that it's -86, is that when you print it out with a function like NSLog, it has no direct knowledge about the arguments passed in, it determines how to treat the arguments, based around the format string, supplied as the first argument.
You're calling:
This:
NSLog(#"%d",k);
This tells NSLog to treat the argument as a signed int (%d). You should be doing this:
NSLog(#"%u",k);
So that NSLog treats the argument as the type that it is: unsigned (%u). See the NSLog documentation.
As it stands, I'd expect your buffer to overrun, trashing memory as the loop runs and your application to crash.
After reflecting, I believe #FreeAsInBeer is correct and you don't want to iterate through the for loop in this situation and you could probably fix this by using signed ints. However, It seems to me like you would be better off, checking len > sizeof(MSG_INFO) and if this isn't the case handling it differently. Most situations I can think of, I wouldn't want to perform any processing after the for loop, if I'd failed to read sufficient information for a message...
I'm not really sure what is going on here, as the loop should never execute. I've loaded up your code, and it seems that the unsigned part of your int declaration is causing the issues. If you remove both of your unsigned specifiers, your code will execute as it should, without ever entering the loop.