Jump to content

  • Log In with Google      Sign In   
  • Create Account


Pointers and Booleans


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 Jethro_T   Members   -  Reputation: 168

Like
0Likes
Like

Posted 28 March 2012 - 01:36 AM

How come it's perfectly fine to do:


int* ptr = 0;

if (ptr) {}




But I get "Forcing value to bool (performance warning)" when I do:


int* ptr = 0;



bool isValid() {

	return ptr;

}



if (isValid()) {}




I don't understand how these are any different.

Sponsor:

#2 mrjones   Members   -  Reputation: 612

Like
0Likes
Like

Posted 28 March 2012 - 02:25 AM

They are different in that if(ptr) is technically compiled into an integer/pointer comparison. There should be a special instruction for comparing whether an integer or pointer is 0 in x86 instructions set.

When returning a boolean from pointer it is first converted boolean and then a boolean comparison instruction is used. Therefore it is a bit slower. Generally the difference in speed is so little that you can just safely do this to avoid warnings:
int* ptr=0;
bool isValid() {
  return (bool)ptr;
}

At least that's the general idea.

#3 NightCreature83   Crossbones+   -  Reputation: 2746

Like
0Likes
Like

Posted 28 March 2012 - 02:58 AM

They are different in that if(ptr) is technically compiled into an integer/pointer comparison. There should be a special instruction for comparing whether an integer or pointer is 0 in x86 instructions set.

When returning a boolean from pointer it is first converted boolean and then a boolean comparison instruction is used. Therefore it is a bit slower. Generally the difference in speed is so little that you can just safely do this to avoid warnings:

int* ptr=0;
bool isValid()
{
	 return (bool)ptr;
}

At least that's the general idea.


It's nicer and easier to read to write that as, this makes the intent of the code clear whereas that cast just looks nasty, also the compiler can optimize that call away in this case.
int* ptr=0;
bool isValid()
{
	 return ptr != 0;
}

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, Mad Max

#4 mrjones   Members   -  Reputation: 612

Like
0Likes
Like

Posted 28 March 2012 - 03:09 AM

Completely agreed, ptr!=0 is much better. I wasn't thinking further from avoiding the warning.

#5 SiCrane   Moderators   -  Reputation: 9567

Like
1Likes
Like

Posted 28 March 2012 - 03:16 PM

More to the point, a bool has to have the value 0 or 1. A pointer can have any value. Thus converting a pointer to a bool requires checking if it's non-null and returning a 1 if that's the case. The if doesn't need to actually convert to a 1, it can just check if it's non-null.

#6 Bacterius   Crossbones+   -  Reputation: 8585

Like
0Likes
Like

Posted 28 March 2012 - 03:25 PM

More to the point, a bool has to have the value 0 or 1.

Actually, a boolean can be represented by any word size (assumed 8 bytes since that's the smallest addressable memory size on current hardware), and by convention we define FALSE to be the value zero and TRUE to be any other value. (not that this invalidates your point, just some trivia).

The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.

 

- Pessimal Algorithms and Simplexity Analysis


#7 iMalc   Crossbones+   -  Reputation: 2301

Like
1Likes
Like

Posted 29 March 2012 - 12:42 AM

More to the point, a bool has to have the value 0 or 1.

Actually, a boolean can be represented by any word size (assumed 8 bytes since that's the smallest addressable memory size on current hardware), and by convention we define FALSE to be the value zero and TRUE to be any other value. (not that this invalidates your point, just some trivia).

You're talking about C.
In C++, which would be the language used here, there is an actual bool as a built-in type, where true is implicitly convertible to 1. Any nonzero is still treated as true of course.

Also, rather than ptr != 0, the preferabe way to do it is ptr != NULL, or if using C++0x, then ptr != nullptr
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms

#8 rip-off   Moderators   -  Reputation: 8121

Like
0Likes
Like

Posted 29 March 2012 - 02:38 AM

Actually, a boolean can be represented by any word size (assumed 8 bytes since that's the smallest addressable memory size on current hardware)...

Modern processors can still address individual bytes. The caching infrastructure might have a minimum threshold of data, but that doesn't mean that this is the minimum size of any unit of data.

Boolean values are typically 1 byte long on consumer hardware. Common exceptions include when they force structure padding or end up on the stack.

...and by convention we define FALSE to be the value zero and TRUE to be any other value.

For conditional expressions, sure. For boolean values, there are other considerations.

I believe the reason for forcing boolean expressions to true or false (rather than non-zero and 0) is to allow explicit boolean comparisons to be more reliable:
bool a = foo.isValid();
if(a == true) {
    // "Unlikely" to get here without forcing boolean to true/false
}

bool b = /* ... */;
if(a == b) {
     // Will we get here?
}
Sure, the above code could be written as:

bool a = foo.isValid();
if(a) {
    // Will surely get here
}

bool b = /* ... */;
if((a && b) || !(a || b)) {
     // We'll get here, but at what cost!!!11!1
}
I believe the first case represents an improvement over the explicit comparison, but the second case is considerably harder to comprehend (the intent is not clear).

(not that this invalidates your point, just some trivia).

I wouldn't recommend playing the C++ trivia game with SiCrane.

#9 Brother Bob   Moderators   -  Reputation: 8053

Like
0Likes
Like

Posted 29 March 2012 - 03:31 AM


...and by convention we define FALSE to be the value zero and TRUE to be any other value.

For conditional expressions, sure. For boolean values, there are other considerations.

I believe the reason for forcing boolean expressions to true or false (rather than non-zero and 0) is to allow explicit boolean comparisons to be more reliable:
bool a = foo.isValid();
if(a == true) {
	// "Unlikely" to get here without forcing boolean to true/false
}

bool b = /* ... */;
if(a == b) {
	 // Will we get here?
}
Sure, the above code could be written as:

bool a = foo.isValid();
if(a) {
	// Will surely get here
}

bool b = /* ... */;
if((a && b) || !(a || b)) {
	 // We'll get here, but at what cost!!!11!1
}
I believe the first case represents an improvement over the explicit comparison, but the second case is considerably harder to comprehend (the intent is not clear).

If true was defined on a language level or by the compiler to be any non-zero value stored in the physical bool-variable in memory, then it would also be the language's or compiler's responsibility to perform the correct comparison. Thus, if true is a non-zero value and two trues are required to compare equal, then it would be the compiler's responsibility,not the user's, to implement the == operator correctly.

Compare to how how IEEE 754 floating point values work. There are positive and negative zeros which have different bit patterns but a normal equality comparison is enough to compare the positive and negative zeros as equal. Similarly, NaN's are/can be bit-wise identical in memory, yet equality comparison has to be false. The specification says all representations of zero are equal, and all NaNs are different, so the hardware has to ensure that equality is implemented correctly

If bools were allowed varying bit patters on the hardware level to represent the same logical value values on the language level, then the same would apply to them. It would not be your responsibility to compare them correctly, and you would never have to write more than a single comparison to see if a value is true or if two trues are equal.

I'm not sure if the language specification even allows for this in the first place though, so it may just be of academic interest. But given how little details are specified sometimes and how much is open for implementation details, who knows...

#10 Bacterius   Crossbones+   -  Reputation: 8585

Like
0Likes
Like

Posted 29 March 2012 - 05:26 AM

I'm not sure if the language specification even allows for this in the first place though, so it may just be of academic interest. But given how little details are specified sometimes and how much is open for implementation details, who knows...

Well if the language doesn't support casting a byte/int/etc.. to a bool, you can always go ghetto:
1. allocate a byte (or whatever the size of a boolean in memory is on the target hardware).
2. write the integer "42" in it.
3. copy that memory into a boolean variable of your choice.
4. Posted Image

When I was still learning programming a while ago I always wondered what would happen if I put something else than a true or a false into a boolean. Now I know. It's kind of fun messing around with some aspects of a language which aren't well-documented. To me anyway.

FWIW I did a quick test in C, then in Delphi. Turns out they don't handle booleans the same. C appears to only check the least significant bit for equality, whereas Delphi performs a complete byte comparison. Thus doing that hack in C will have no effect since it only ever looks at one bit, whereas it will utterly destroy program logic in Delphi. Check it out:

#include <stdio.h>
#include <stdbool.h>

int main( int argc, const char* argv[] )
{
  bool a, b;
  char v;

  a = false;
  b = false;

  v = 43;
  memcpy(&a, &v, 1);

  v = 45;
  memcpy(&b, &v, 1);

  if (a == true)
  {
	printf("A = TRUE.\n");
  }
  else
  {
	printf("A = FALSE.\n");
  }

  if (b == true)
  {
	printf("B = TRUE.\n");
  }
  else
  {
	printf("B = FALSE.\n");
  }
}

The slowsort algorithm is a perfect illustration of the multiply and surrender paradigm, which is perhaps the single most important paradigm in the development of reluctant algorithms. The basic multiply and surrender strategy consists in replacing the problem at hand by two or more subproblems, each slightly simpler than the original, and continue multiplying subproblems and subsubproblems recursively in this fashion as long as possible. At some point the subproblems will all become so simple that their solution can no longer be postponed, and we will have to surrender. Experience shows that, in most cases, by the time this point is reached the total work will be substantially higher than what could have been wasted by a more direct approach.

 

- Pessimal Algorithms and Simplexity Analysis


#11 rip-off   Moderators   -  Reputation: 8121

Like
0Likes
Like

Posted 29 March 2012 - 05:56 AM

If true was defined on a language level or by the compiler to be any non-zero value stored in the physical bool-variable in memory, then it would also be the language's or compiler's responsibility to perform the correct comparison. Thus, if true is a non-zero value and two trues are required to compare equal, then it would be the compiler's responsibility,not the user's, to implement the == operator correctly.

I agree with the underlying intent of your post (I believe), but not quite with the detail. As I see it, the compiler has (at least) two choices, either of which will make boolean comparisons behave reasonably. Either do any extra work during any comparison involving a bool, or do any extra work when a boolean value is "created" - assigned from an expression that is not already a "safe" bool. From the programmer's perspective, unless they break the type system then these two approaches should be identical.

Obviously, messing with type punning or unions could create situations where the program loads a (potentially arbitrary) value into space that will be later treated as a boolean. Most such code is, to my knowledge, undefined behaviour (at least I cannot think of a counter example on the spot).

Doing both is possible and would be the "safest" approach, making it both easy to debug the code (you'll only see values of 1 and 0 in booleans) and would also handle people accidentally breaking the type system (is there any complex C++ program that doesn't depend on undefined behaviour). Such a compiler implementation would be inconsistent with the goals of most C++ code, which emphasises speed over safety.

Thus, if true is a non-zero value and two trues are required to compare equal...

To come back to this point, this is an interesting statement. I'm not a C++ guru that can quote standards. I'm not sure that but as a more general question in language design it is interesting what the following program should print:
i = 42
b = true
if b == i
    print "#{b} == #{i}"
else
    print "#{b} != #{i}"
end
I'm not sure I like the above program to print that 42 is equal to true, without forcing me to be more explicit about why I am comparing these values. It is almost as likely to be a mistake as it is to be what I intended.

I certainly appreciate the convenience of C++'s conversion-to-bool in conditional context, particularly for pointers. I find languages that force you to write if(x != null) more verbose than languages that allow if(x) instead.

#12 Ripiz   Members   -  Reputation: 529

Like
-2Likes
Like

Posted 29 March 2012 - 10:39 AM

Well if the language doesn't support casting a byte/int/etc.. to a bool, you can always go ghetto:
1. allocate a byte (or whatever the size of a boolean in memory is on the target hardware).
2. write the integer "42" in it.
3. copy that memory into a boolean variable of your choice.
4. Posted Image
Check it out:
<code removed>


Well I saw a little flaw in your code, you check only whether boolean is true OR something else, so I made little test of my own:
bool b1 = true, b2 = false, b3; // 3 bools, true, false and hack
char a1, a2, a3 = 2; //  3 chars, to store true, false and hack
memcpy(&a1, &b1, sizeof(bool)); // copying 'true' into char to see it's value in number
memcpy(&a2, &b2, sizeof(bool)); // copying 'false' into char to see it's value in number
memcpy(&b3, &a3, sizeof(bool)); // copying number 2 into bool
cout << (int)a1 << " (value of bool == true)" << endl; // typecasting to int to avoid weird symbols
cout << (int)a2 << " (value of bool == false)" << endl; // typecasting to int to avoid weird symbols
cout << b3 << " (value of bool == 2)" << endl; // no typecast, we need bool value
if(b3 == true)
  cout << "1 (bool == true)" << endl;
else if(b3 == false)
  cout << "2 (bool == false)" << endl;
else
  cout << "3 (bool is something else)" << endl;

Can you guess what it prints? Well here's the answer:
Spoiler


#13 TheUnbeliever   Members   -  Reputation: 961

Like
0Likes
Like

Posted 29 March 2012 - 11:10 AM

It's kind of fun messing around with some aspects of a language which aren't well-documented.


If some aspect of the language is unspecified, no behaviour of any particular implementation is part of that language.
[TheUnbeliever]




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS