Sign in to follow this  
Stormtrooper

Understanding some code

Recommended Posts

Stormtrooper    100
Hey all, I just wanted some help understanding some code.
const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
Okay, I understand what it is doing...I just don't understand why they are doing it. The && will return true if argv[0] and *argv[0] are non-zero. If it is true, it will return argv if it is false it will return git-help....right?

Share this post


Link to post
Share on other sites
rip-off    10979
It checks if there is a non-empty string in argv[0]. Typically there is. However, there are situations where there might not be (e.g. a careless call to an exec function). In this case, the default value used is "git-help".

Why? Paranoia [smile]

Share this post


Link to post
Share on other sites
Antheus    2409
argv[0] is string containing the name of executable which was used to run this application.

Consider:
const char * s = 0;
const char * s = "";
Both of those can be considered invalid. One is not allocated, the other is empty string.

In original statement, cmd will hold either valid string (non-null, non-empty) or "git-help".

I honestly don't remember ever checking for that condition, but perhaps it is sometimes possible for argv[0] to contain invalid filename.

Share this post


Link to post
Share on other sites
Red Ant    471
I *think* argv[0] just contains the command that was used to invoke the executable. This does not necessarily have to be the actual path of the executable ... for instance, on Linux it could just as well be an alias or a soft link.

Share this post


Link to post
Share on other sites
KulSeran    3267
really though, they are using the early-out mechanism of &&.
Consider

T *ptr = 0;
if ( ptr && ptr->whatever == "blah" )

First it checks (ptr != NULL). If ptr isn't NULL, it then checks ptr->whatever == "blah".
If ptr is NULL, it early outs and just returns false for the whole statement whout executing anything on the right.

Share this post


Link to post
Share on other sites
phresnel    953
Quote:
const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";


In C/C++, the precedence of && is so that the above if the equivalent of ...

const char *cmd = (argv[0] && *argv[0]) ? argv[0] : "git-help";




... or ...

const char *cmd;
if (argv [0] && *argv [0]) {
cmd = argv [0];
} else {
cmd = "git-help";
}



Precedence may change in other languages, like PHP, where you should always underline your intention with brackets when using the ternary operator.

As KulSeran pointed out, the author is making use of the shortcircuit behaviour of the logical && operator, which means that in (A&&B) B is only queried ("executed") if A!=0, or the other way, if A is zero, than it is clear that the whole statement will be zero, and the evaluation of B is skipped.

Btw, in Bash, those short circuits also exist, so "make && make install" means, that "make install" is only executed if "make" returns 0 (where 0 in nix means success, as compared to !=0 in C/C++). This is very usefull if you distaste IDEs for your daily dev ("make && ./my-binary").

(Anecdote:)
Actually, this can yield bad performance on modern CPU, as that short circuit behaviour yields a branch, and with modern techniques like code prefetching, speculative execution + co., the use of that operator can cause prediction misses (which means: if a miss is determined by the CPU, code that already has been executed speculatively is then considered invalid, and the correct code has to be "re-executed").

If, and only, in the expression (A&&B), B is independent of the value of A (not the case in your example), and you can ensure that the relevant bits of A and B are at the same position (for boolean expressions generally the 0th bit), you can replace (A&&B) with (A&B), same for (A||B) --> (A|B). Those bitwise operators have to execute both A and B anyways, and so no branch is produced, leading to a branchfree epression.

Google for Agner Fog and the Intel Optimization Guides for more of that stuff :)


(Anecdote2:)
In Unix/GNU/Linux environments, it is often common that several links to the same binary exist in /bin, /usr/bin, and fellows. For example, the program "[" is the same binary as "test", but the behaviour slightly changes. The actual binary determines that by checking argv[0]. Cool.

Share this post


Link to post
Share on other sites
popsoftheyear    2194
Quote:
Original post by Eeyore
Hey all, I just wanted some help understanding some code.

const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";


Okay, I understand what it is doing...I just don't understand why they are doing it.

The && will return true if argv[0] and *argv[0] are non-zero. If it is true, it will return argv if it is false it will return git-help....right?


It's important to note that if "argv[0]" evaluates to false, it will never even do the "&& *argv[0]" but will still render the total conditional to be false, returning "git-help". This is good, because if argv[0] was 0, you wouldn't even be able to read from that memory location in most operating systems.

Cheers
-Scott

Share this post


Link to post
Share on other sites
KulSeran    3267
[sarcasm]
DevFred, how do you know that type T is not supposed to have a member named whatever that is a std::string that i want to compare to "blah".
[/sarcasm]
Yeah, I prolly should a have made a more complete example.

Share this post


Link to post
Share on other sites
shurcool    439
Quote:
Original post by Eeyore
Hey all, I just wanted some help understanding some code.

const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";

This is logically equivalent to:

const char *cmd;
if (argv[0] != NULL)
{
if (*argv[0] != NULL)
{
cmd = argv[0];
}
else
{
cmd = "git-help";
}
}
else
{
cmd = "git-help";
}


That is because of the early-out of the && operator. If the left condition fails, it will never check/execute what's on the right side.

Share this post


Link to post
Share on other sites
rip-off    10979
NULL is typically used with pointer values. As such, I would use the NUL character for the second condition:

const char *cmd = "git-help";
if (argv[0] != NULL)
{
if (*argv[0] != '\0')
{
cmd = argv[0];
}
}

Share this post


Link to post
Share on other sites
shurcool    439
Quote:
Original post by rip-off
NULL is typically used with pointer values. As such, I would use the NUL character for the second condition.

You're absolutely right. However, I was just demonstrating what the logic behind the original and somewhat hard-to-understand statement was, by translating it to an equivalent if/else block. And the original code does in fact check that both argv[0] && *argv[0] have non-0 values, which is equivalent to != NULL if NULL is defined as 0. :)

Share this post


Link to post
Share on other sites
rip-off    10979
NULL may be 0, but my point is about intent. When I see something compared with NULL, I'm going to assume that the other thing is a pointer. If I see it compared with '\0' then my assumption is that the other thing is a character. That is all [smile]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this