Just some Q's about C++

Started by
8 comments, last by Boder 19 years, 5 months ago
Hi ya'll I've done some thinking, and now *hic* I've got some questions Number One I've often heard switch statements are preferred to if, else constructs. Does that make this second example better than the first one?

if (x < 0) {
  // move to wall
} else {
  // apply thrusters
}

switch(x < 0){
  case 0:
    //apply thrusters
    break;
  default:
    // move to wall
}



Number Two With all these special operators, do they make the program more efficient?

x++; x+=1; // same
++x; x++; // any difference?
x++; x+=20; // any difference?
x=x+20; x+=20; // same right?



Number Three Are these special cases any different from the general cases?

if (laserOn); /*and*/ if (!laserOn);
if (laser0n!=0); /*and*/ if (laserOn==0);
// any of these faster than the others?
if (laserOn);
if (!laserOn);
if (laserOn=1);
if (laserOn==1);
if (laserOn > 1);
if (laserOn >= 1);



Number Four Understanding pointer arithmetic.

int* pointer = new int[1000];
((int*)(((short int*)(((char*)pointer)++))++))++;
// has moved the adress up 7 bytes, if sizeof(char)==1,
// sizeof(short)==2, and sizeof(int)==4, right?

//or should it be?
++((int*)(++( (short int*)(++((char*)pointer)) )) );


Number Five Small and simple.

float f = 8.40f;
int i = 2;
// either of these faster?
result = ((int)f)/i;
result = (int)(f/i);



Number Six If I want to test if two small structs are equal without testing each member...

typedef struct {
  short int x;
  short int y;
} Position;

int main(){
	Position one = {1,7};
	Position two = {1,7};
	
	if ( *(int*)&one == *(long int*)&two)
		// they are equal
        //vs.
        if ( one.x == two.x && two.y == one.y)
                // they are equal

	return 0;
}



Can I use memcmp() or another function?
Advertisement
You don't have to typedef structs in C++ (in that way). :)
I think the best solution for this would be the actual benchmarking try to run these functions about a million times and count the time it took to execute, and you got yourself a more or less correct result
I think the best solution for this would be the actual benchmarking try to run these functions about a million times and count the time it took to execute, and you got yourself a more or less correct result
Number One:
using switch/case here is pointless, and quite ugly. switch/case statements are usually used when there is a consistent number of cases to evalutate. For example:
switch(ApplicationState){	case APP_INIT:		InitStuff();		break;	case APP_RUNNING:		Frame();		break;	case APP_PAUSE:		break();}


Switch/case is probably a bit faster than if/else, but still it's something so small you really won't notice.

Number Two:
x+=20 is faster than x=x+20 because x is only evalutated once.
++x can be faster than x++ because it just increments x. x++ instead increments x and returns the previous value of x. The compiler is probably optimizing this for you anyway so don't care.

Number Three:
They are probably the same, but
if (laserOn=1);
if doing a totally different thing. it will always be true because it assigns the value 1 to laserOn! Be careful because it's a common (and hard to find) error.

Number Four:
Hmm, I don't even think you can compile this, anyway conceptually it's right.

Number Five:
The first one is a division between two integers. The second one is a division between a float and an integer, so the result is a float, and then you cast it to an int. So the result is not always going to be the same! Anyway, the first one could be faster.

Number Six:
Ok, it could work, but it's a terrible hack, plus it will just work for this struct. It could even not work because the size of a structure can be different by the sum of the sizes of its members. You better use memcmp(&One, &Two, sizeof(Position)), or even better, define operator== for the struct.
Premature optimisation is the root of all evil.

1. I've often heard switch statements are preferred to if, else constructs. Does that make this second example better than the first one?
"often heard"? From whom? In what context? Use which ever is clearer for you situation.
if (x < 0){	// something}else{	// something else}

is clearer than
switch (x < 0){	case 0:		// something		break;	default:		// something else}

but
switch (x){	case 0:		// something		break;	case 1:		// something else		break;	case 2:		// a third thing		break;	case 3:		// yet another thing}

is probably clearer than
if (x < 2){	if (x == 0)	{		// something	}	else	{		// something else	}}else{	if (x == 2)	{		// a third thing	}	else	{		// yet another thing	}}


2. With all these special operators, do they make the program more efficient?
They are shorthand for the programmer, nothing more.
Quote:x++; x+=1; // same

In that instance, yes.
Quote:++x; x++; // any difference?

The first is preincrement - increment the value and use the new value, the second is postincrement - increment the value and use the old value. So:
int x = 0;func(++x); // calls func(1)assert(x == 1);func(x++); // calls func(1) againassert(x == 2);

Quote:x++; x+=20; // any difference?

The first increments x by one, the second increments x by 20.
Quote:x=x+20; x+=20; // same right?

Yes.

3. Are these special cases any different from the general cases?
Not all of the tests you posted do the same thing, so comparing speed makes no sense. Equivalent tests will be the same speed. One thing to note is this:
Quote:if (laserOn=1);

It is performing an assigment to laserOn and then using the value of laserOn as a boolean. Any good compiler will issue a warning for this.

4. Understanding pointer arithmetic.
Quote:((int*)(((short int*)(((char*)pointer)++))++))++;

Is not valid. You cannot repeatedly apply the postincrement operator since it returns the old value, which is a temporary. I'm sure someone will explain this a lot better, probably before I've gotten around to hitting the reply button :P
Quote:++((int*)(++( (short int*)(++((char*)pointer)) )) );

I'm not even going to attempt this one. Why on Earth would you want to do something like this?

5. Small and simple.
Quote:float f = 8.40f;
int i = 2;
// either of these faster?
result = ((int)f)/i;
result = (int)(f/i);

In this case the first would probably be fractionally faster, but not worth worrying about until after you've profiled your code and identified it as a bottleneck.

6. If I want to test if two small structs are equal without testing each member...
You're using C++. Write operator==(). Make it work then make it fast.

Enigma
Quote:Original post by Arcibald Wearlot
Number Five:
...So the result is not always going to be the same!...


I was thinking this, but I couldn't actually find a case where the result would be different. I think due to truncation the results would always be the same as long as the integer is the divisor. Feel free to prove me wrong though.

Enigma
Number One:

switches are faster than if-elses because the program know exactly which case to jump to rather than having to test several conditions. However, this only becomes significant when there are more than 2 or 3 cases. If you only have an if-else or an if-else if-else, you don't need to make any special effort to use switch statements. It makes the code less clear for almost no gain in performance.

Where switch really shines is when there are a lot of cases, and you would otherwise have to write a large amount of if statements. Note that switch statements only handle integer values (maybe floating points?), so you can't use this trick with strings. The compiler does some kind of mathematical calculation in assembly to achieve this trick, so it wouldn't save any time if the data type wasn't numerical.

Number Two:

No, they're just there for the convenience of the programmer. It's just some "syntactical sugar" that makes things nicer for humans, but makes no difference to the compiler.

Number Tree:

The ! and > operators will translate to different assembly instructions, but I don't think there is any difference at all in speed. If there is, it's so incredibly miniscule that it's simply not worth worrying about. It's splitting hairs, as I believe the expression goes.

Number Four:

I don't have any desire to understand that obfuscated code, but there's a difference between a prefix operator (++x) and a postfix operator (x++). The prefix operator increments the variable *before* it's used in an expression, and the postfix operator increments a variable *after* it is used in an expression.

For example:
int x = 1;
int y = ++x

y == 2 and x == 2 because x is incremented before it is assigned.

int x = 1;
int y = x++;

y == 1 and x == 2 because x is assigned before it's incremented.

Just simple incrementing using ++x; or x++; makes no difference because incrementing is the only thing happening.

Number Five:

Generally, working with integers is faster than working with floating point numbers.

With result = ((int)f)/i, there is a conversion to an integer and an integer division.

With result = (int)(f/i), there's more than meets the eye. When you have an integer and a floating point number mixed in an expression (f/i), the integer is implicitly (invisibly) converted to a floating point number, so you would have a conversion from integer to float, a float division, and then an explicit conversion back to an int.

So the second one would be slower.

Number Six:

As far as structs go, you can't simply use the == operator between two structs. Your example actually happens to work because a struct is essentially a pointer to some data. When you cast a structure pointer with two shorts (32-bits) to an integer pointer, it will see the entire structure, since it's only 32-bits. This won't work, however, if the structure is bigger.

I believe memcmp would be an good means of comparison. Two structures with the same values should have the same thing in memory.
Number 5:

There's a good chance your compiler will optimize the last line by just doing the calculation ahead of time, and assigning 4 to result (since all the values are known statically). [google] "constant folding". In that case it wouldn't make a difference.

In general, simple things like this are known about by compiler writers and will be optimized - so that if there are two exactly equivalent ways to write something, they will compile to the same code, even if the code "suggested" by one way of writing it is faster.
Thanks everyone for the detailed replies. I was going to try to use the C++ way of creating a struct, but I didn't know if it was correct, and I originally learned in C. Habits are hard to break.

Just a note for people reading the thread... Number Four doesn't compile. Sorry. The error is "invalid lvalue in increment."

While I'm at it
// I did a test and these really are different// It blows my mindchar * name = "Johnny Slacker";char name2[] = "Johnny Slacker";


And does binary packing, i.e. storing bits for obstacles in a game, really have a noticeable effect at runtime for large tile maps? I've heard that doing (variable&BIT_OBSTACLE) and/or (MASK>>sprite_pos) at runtime can slow things down and should only be used for storing the information in files and such.

This topic is closed to new replies.

Advertisement