I wish Solar was around, and would answer this because I think he was really big on the things you are asking. I really only write software for the x86, and have hardly ever dealt with portability or even cases of extreme optimization which are basically the two points I can think of that would be very important to you're question.
The only point I could think of would be to reduce bugs by using a good coding style in C (I rarely use C++).
However borrowing a bunch of compiler flag for GCC from Solar's Makefile Tutorial:
http://www.osdev.org/wiki/Tutorial:Makefile wrote:
-Wall -Wextra -pedantic -Wshadow -Wpointer-arith -Wcast-align \
-Wwrite-strings -Wmissing-prototypes -Wmissing-declarations \
-Wredundant-decls -Wnested-externs -Winline -Wno-long-long \
-Wconversion -Wstrict-prototypes
Try turning all these on and then writing something, and see what it flags as a error. Once you see a error try to determine what it is talking about and do a Google search about it. For example:
Code: Select all
int main(int argc, char *argv[]){
unsigned int a = 2;
int b = -5;
if(a > b){
printf("a>b\n");
return 2;
}
printf("b>a\n");
return 1;
}
Will say, "b>a". Do you think this is correct?
I think it is not. However it is rooted in the way the x86 does addition and subtraction.
Thus, a circuit designed for addition can handle negative operands without also including a circuit capable of subtraction (and a circuit which switches between the two based on the sign).
In this case the circuit in a processor in a generic case here does not care if a number is signed(negative and positive) or unsigned(just positive). It can add and subtract unsigned and signed numbers using the same circuit.
You should already know that if the processor has the number 0xFFFFFFFF in one of it's registers and it adds one that the number will wrap around back to 0x0 due to the way the circuit works which can be shown at this site: (also a great resource)
http://www.play-hookey.com/digital/adder.html
To do this it uses the two-compliment system. In which negative numbers count backward from zero.
0100 = 4
0011 = 3
0010 = 2
0001 = 1
0000 = 0
1111 = -1
1110 = -2
1101 = -3
1100 = -4
http://en.wikipedia.org/wiki/Two's_complement
You prolly wonder why? Of course I wondered the same thing until I tried it.
0011 + 1111 = 0010
3 + 15 = 18 = 10010
The one is dropped or on the x86 the carry flag in the EFLAGS is set. This carry flag is used in compare instruction on the x86.
Never the less addition of a positive and negative number was done using the exact same circuit and method that would also work for a positive number added to another positive number.
To do subtraction the circuit might contain thirty-three bits for each operand that is loaded into it. Then it would simply set the thirty-third bit of the second operand to subtract it from the first.
Okay. You might still be wonder so what is wrong?
Well. The problem is rooted in the fact that we lose a bit we making a integer signed. If we take that one bit and make it denote a positive or negative number then we lose the maximum value such as:
2^32 (unsigned int)
2^31 (signed int)
So if we did some calculation that pushed towards the limits of the x86's maximum register value and we did not need to represent negative numbers then we could simply use
unsigned int instead of cutting our maximum value in half by using a
signed int or also the equivalent in C being
int.
The signed portion is default unless you specify
unsigned to a primitive type in C.
So why does this fail?
Code: Select all
int main(int argc, char *argv[]){
unsigned int a = 2;
int b = -5;
if(a > b){
printf("a>b\n");
return 2;
}
printf("b>a\n");
return 1;
}
The compiler expects that you might put a value between 0 and 2^32 in
a. It also expects that you might put a value between -(2^31) and 2^31 in
b.
The problem occurs because of the compare instruction. There is no way for the operation to compare the signed and unsigned with out converting one to the other. It either has to compare them as unsigned or signed. If it treats
a as signed, and a value over 2^31 is represented in it then it will become treated as a negative number causing a bug possibly since this might not be intentional.
Of course the opposite happens in the case above, and as far as I know C always treats a smaller data type like the larger data type in the operation that is happening between them.
So what it did was treat
b as a
unsigned int instead of
signed and this caused
a to become the value 0xFFFFFFFA. If you remember negative numbers start at after the positive zero(0000b).
So it really did
if(0x2 > 0xFFFFFFFA).
--
Since C is designed to be portable too and as far as I can tell the people who came up with the C standard decided to help keep you from worrying over these things to use a signed and unsigned component to the data type.
And the
warning is a result of this. Telling you, "Hey. Something might go wrong with this. You need to take a really close look and make sure that for the target platform(s) this is going to be okay to do.".
There could exist a instruction that might compare a signed with a unsigned integer so I suppose doing things this way supports this event..
I dunno. I had to research this all up tonight just to present it in this messy fashion. I knew a good bit about it, but it still takes a little to wrap my small brain around and hopefully you can understand it quicker than I.
There are most likely a lot more in depth and complex reasons why you have certain warnings in C. You will most likely end up doing a lot of research and reading to find the answers just like I had to do.
http://en.wikipedia.org/wiki/Aliasing_(computing)
The aliasing deal has something to do with the compiler trying to optimize you're code as much as possible.
Here is something related to you're casting question.
http://209.5.165.104/search?q=cache:r3h ... -bin/info2\
www%3F(gcc.info)Warning%2520Options+C+standard+comparison+between+signed+\
and+unsigned&hl=en&ct=clnk&cd=9&gl=us
(above) wrote:
-Wcast-align'
Warn whenever a pointer is cast such that the required alignment
of the target is increased. For example, warn if a `char *' is
cast to an `int *' on machines where integers can only be accessed
at two- or four-byte boundaries.
I have never run into this, but someone has and that is why it is there.
Code: Select all
unsigned int c = 4;
unsigned int *a = &c;
unsigned int b;
b = a;
What if forge that
a was a pointer to a
unsigned int in a long and complex function. It could take a while to see the bug. So the compiler generates a warning that something looks wrong.
To tell it this is exactly what you want you might do:
b = (unsigned int)a
Since that is exactly what the compiler was going to do. It just lets the compiler know that you and it are on the same page of thinking.
Code: Select all
unsigned int *a = 0;
unsigned char *c = 0;
Will generate a warning. Since the compiler is thinking well is he really wanting to do this, or is he about to create a bug in his program. So:
c = (unsigned char*)a
Tell it this is what you want to do, and the warning will disappear.
The warnings I feel are there more to help you than tell you what you are doing is wrong. Of course if you are new, or maybe trying to make something portable they will also help you know what you might want to check out in the source code to make sure it will work where you want it to work.
There are some people who feel you should not do some things. Of course there are a lot of those situation such as the one earlier where Candy did not like the typedef hiding the fact that something was a pointer which he made a valid point about. Although using something like that would depend on the situation and how much time you have. You might want to write something quickly, then again you might want to write something portable and understandable to many people.
And the answer with casting.
really big on the things you are asking. I really only write software for the x86, and have hardly ever dealt with portability or even cases of extreme optimization which are basically the two points I can think of that would be very important to you're question.
The only point I could think of would be to reduce bugs by using a good coding style in C (I rarely use C++).
However borrowing a bunch of compiler flag for GCC from Solar's Makefile Tutorial:
http://www.osdev.org/wiki/Tutorial:Makefile wrote:
-Wall -Wextra -pedantic -Wshadow -Wpointer-arith -Wcast-align \
-Wwrite-strings -Wmissing-prototypes -Wmissing-declarations \
-Wredundant-decls -Wnested-externs -Winline -Wno-long-long \
-Wconversion -Wstrict-prototypes
Try turning all these on and then writing something, and see what it flags as a error. Once you see a error try to determine what it is talking about and do a Google search about it. For example:
Code: Select all
int main(int argc, char *argv[]){
unsigned int a = 2;
int b = -5;
if(a > b){
printf("a>b\n");
return 2;
}
printf("b>a\n");
return 1;
}
You might do instead:
Code: Select all
...
if(b > 0){
if((unsigned int)b > a){
printf("b > a\n");
}
}
printf("a > b\n");
....
The cast (above) suppresses the warning.
[edit mod="candy"]Sorry for the line splits, can't get the url tag to work[/edit]