Programming Puzzle

C

Chris Torek

Dan said:
void swap(int *p, int *q) { ... } [snippage]
swap(&i, &i);

Just because you have two pointers that _point_ to the same address, this
doesn't mean that they (the variables here which are still the **pointers**)
share the same memory location.

As I see it, there are *four* variables in the swap() function: p,
q, *p, and *q. It also happens to be the case that two of these
variables -- *p and *q -- are potentially the same single variable,
the "i" in the call above.

The C99 standard, at least, does not seem to define the term
"variable", so one is free to use it to mean whatever one likes.
If you prefer to reserve the word only for "p" and "q" themselves,
and not refer to *p and *q as "variables", that is your right --
but there will be confusion when communicating with those who *do*
call *p and *q "variables".

(It is possible that the current C++ standard defines the term
"variable" in such a way that swap() has only two. I have not been
keeping up with C++.)
 
I

Ioannis Vranos

Dan said:
Since you seem to be unable to understand plain English:

#include <stdio.h>

void swap(int *p, int *q) { ... }

int main()
{
int i = 10;
swap(&i, &i);
printf("%d\n", i);
return 0;
}

Try this code for different implementations of the swap function, using
a temp var and using in-place swapping. Compare the results.

This example is trivial, but the situation can realistically arise in more
complex algorithms.



You are right about that, however in reality a decent swap
implementation would check if the passed arguments have the same value
so as to avoid unneeded operations.






Regards,

Ioannis Vranos
 
I

Ioannis Vranos

Dan said:
Since you seem to be unable to understand plain English:

#include <stdio.h>

void swap(int *p, int *q) { ... }

int main()
{
int i = 10;
swap(&i, &i);
printf("%d\n", i);
return 0;
}

Try this code for different implementations of the swap function, using
a temp var and using in-place swapping. Compare the results.

This example is trivial, but the situation can realistically arise in more
complex algorithms.



You are right about that, however in reality a decent swap
implementation would check if the passed arguments have the same value
so as to avoid unneeded operations.






Regards,

Ioannis Vranos
 
H

Howard

You are right about that, however in reality a decent swap
implementation would check if the passed arguments have the same value
so as to avoid unneeded operations.

And the need for that check is exactly one of the arguments for why this
choice of implementation was no better than using a temporary variable in
the first place. If you can afford the space for the code to check this
condition, then you can surely afford the space for the temporary integer
variable instead.

-Howard
 
I

Ioannis Vranos

Howard said:
And the need for that check is exactly one of the arguments for why this
choice of implementation was no better than using a temporary variable in
the first place. If you can afford the space for the code to check this
condition, then you can surely afford the space for the temporary integer
variable instead.


I think the non-temporary requirement is not for space concerns but to
find out how c00l we are. :) Even under severe space concerns there is
always space for a *temporary* variable. If there are space concerns to
the extreme, then we should write numbers in its memory directly. :)






Regards,

Ioannis Vranos
 
A

Arthur J. O'Dwyer

#include <cstdio>
using namespace std;int main(){const char* s="#include <cstdio>%cusing
namespace std;int main(){const char*
s=%c%s%c;printf(s,10,34,s,34,10);}%c";printf(s,10,34,s,34,10);}

% cat > test.cpp
#include <cstdio>
using namespace std;int main(){const char* s="#include <cstdio>%cusing
namespace std;int main(){const char*
s=%c%s%c;printf(s,10,34,s,34,10);}%c";printf(s,10,34,s,34,10);}
% g++ test.cpp
test.cpp:2:46: warning: multi-line string literals are deprecated
% ./a.out
#include <cstdio>
using
namespace std;int main(){const char*
s="#include <cstdio>%cusing
namespace std;int main(){const char*
s=%c%s%c;printf(s,10,34,s,34,10);}%c";printf(s,10,34,s,34,10);}


Certainly doesn't look like an "exact copy" to me. Besides
which, it doesn't work on my DeathStation 9000.
A trivial application of my 'quine' program produces the
C program reproduced below my signature line. (Now I just have
to figure out how to make 'usenetify2' interface with 'quine',
and I've got a winner! ;-))

-Arthur
http://www.contrib.andrew.cmu.edu/~ajo/free-software/quine.c



#include <stdio.h>


void quine(void)
{
static char q[] = {
'0',
'}', ';', '\n', ' ', ' ', ' ', ' ', 'i', 'n', 't',
' ', 'i', ';', '\n', ' ', ' ', ' ', ' ', 'p', 'u',
't', 's', '(', '"', '\\', 'n', '#', 'i', 'n', 'c',
'l', 'u', 'd', 'e', ' ', '<', 's', 't', 'd', 'i',
'o', '.', 'h', '>', '\\', 'n', '\\', 'n', '"', ')',
';', '\n', ' ', ' ', ' ', ' ', 'p', 'u', 't', 's',
'(', '"', 'v', 'o', 'i', 'd', ' ', 'q', 'u', 'i',
'n', 'e', '(', 'v', 'o', 'i', 'd', ')', '\\', 'n',
'{', '\\', 'n', ' ', ' ', ' ', ' ', 's', 't', 'a',
't', 'i', 'c', ' ', 'c', 'h', 'a', 'r', ' ', 'q',
'[', ']', ' ', '=', ' ', '{', '"', ')', ';', '\n',
' ', ' ', ' ', ' ', 'f', 'o', 'r', ' ', '(', 'i',
'=', '0', ';', ' ', 'q', '[', 'i', ']', ';', ' ',
'+', '+', 'i', ')', ' ', '{', '\n', ' ', ' ', ' ',
' ', ' ', ' ', ' ', ' ', 'i', 'n', 't', ' ', 'n',
'l', ' ', '=', ' ', '(', 'i', '%', '1', '0', ')',
'?', ' ', '\'', ' ', '\'', ':', ' ', '\'', '\\', 'n',
'\'', ';', '\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', 'i', 'f', ' ', '(', 'q', '[', 'i', ']', ' ',
'=', '=', ' ', '\'', '\\', 'n', '\'', ')', '\n', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'p',
'r', 'i', 'n', 't', 'f', '(', '"', '\'', '\\', '\\',
'n', '\'', ',', '%', 'c', '"', ',', ' ', 'n', 'l',
')', ';', '\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', 'e', 'l', 's', 'e', ' ', 'i', 'f', ' ', '(',
'q', '[', 'i', ']', ' ', '=', '=', ' ', '\'', '\\',
't', '\'', ')', '\n', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ', 'p', 'r', 'i', 'n', 't', 'f',
'(', '"', '\'', '\\', '\\', 't', '\'', ',', '%', 'c',
'"', ',', ' ', 'n', 'l', ')', ';', '\n', ' ', ' ',
' ', ' ', ' ', ' ', ' ', ' ', 'e', 'l', 's', 'e',
' ', 'i', 'f', ' ', '(', 'q', '[', 'i', ']', ' ',
'=', '=', ' ', '\'', '\\', '\'', '\'', ')', '\n', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'p',
'r', 'i', 'n', 't', 'f', '(', '"', '\'', '\\', '\\',
'\'', '\'', ',', '%', 'c', '"', ',', ' ', 'n', 'l',
')', ';', '\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', 'e', 'l', 's', 'e', ' ', 'i', 'f', ' ', '(',
'q', '[', 'i', ']', ' ', '=', '=', ' ', '\'', '\\',
'\\', '\'', ')', '\n', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ', 'p', 'r', 'i', 'n', 't', 'f',
'(', '"', '\'', '\\', '\\', '\\', '\\', '\'', ',', '%',
'c', '"', ',', ' ', 'n', 'l', ')', ';', '\n', ' ',
' ', ' ', ' ', ' ', ' ', ' ', ' ', 'e', 'l', 's',
'e', '\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
' ', ' ', 'p', 'r', 'i', 'n', 't', 'f', '(', '"',
'\'', '%', 'c', '\'', ',', '%', 'c', '"', ',', ' ',
'q', '[', 'i', ']', ',', ' ', 'n', 'l', ')', ';',
'\n', ' ', ' ', ' ', ' ', '}', '\n', ' ', ' ', ' ',
' ', 'f', 'p', 'u', 't', 's', '(', 'q', ',', ' ',
's', 't', 'd', 'o', 'u', 't', ')', ';', '\n', '}',
'\n', '\n', 'i', 'n', 't', ' ', 'm', 'a', 'i', 'n',
'(', ')', '\n', '{', '\n', ' ', ' ', ' ', ' ', 'q',
'u', 'i', 'n', 'e', '(', ')', ';', '\n', ' ', ' ',
' ', ' ', 'r', 'e', 't', 'u', 'r', 'n', ' ', '0',
';', '\n', '}', '\n', '\n', 0};
int i;
puts("\n#include <stdio.h>\n\n");
puts("void quine(void)\n{\n static char q[] = {");
for (i=0; q; ++i) {
int nl = (i%10)? ' ': '\n';
if (q == '\n')
printf("'\\n',%c", nl);
else if (q == '\t')
printf("'\\t',%c", nl);
else if (q == '\'')
printf("'\\'',%c", nl);
else if (q == '\\')
printf("'\\\\',%c", nl);
else
printf("'%c',%c", q, nl);
}
fputs(q, stdout);
}

int main()
{
quine();
return 0;
}
 
H

Howard

Ioannis Vranos said:
I think the non-temporary requirement is not for space concerns but to
find out how c00l we are. :) Even under severe space concerns there is
always space for a *temporary* variable. If there are space concerns to
the extreme, then we should write numbers in its memory directly. :)

Oh, I see I'm carrying my argument over from another thread with that point.
We had another recent thread regarding this issue, and I got mixed up as to
where this discussion originated (esp. since at some point the subject line
changed...for *some* reason! :)).

-Howard
 
I

Ioannis Vranos

Howard said:
Oh, I see I'm carrying my argument over from another thread with that point.
We had another recent thread regarding this issue, and I got mixed up as to
where this discussion originated (esp. since at some point the subject line
changed...for *some* reason! :)).


Well, at some point I considered the "thing" to be more accurate than
the original term used. :)






Regards,

Ioannis Vranos
 
J

Julie

Howard said:
Geez, give me a break! What I've shown exhibits *exactly* the kind of
problem that can happen when trying to swap two integers using the xor
technique. Just because someone used terminology that suggested the two
variables themselves had the same memory location, surely you knew what was
meant! The problem is when both memory locations are the same, which can
happen if using pointers or references. That's all that was meant, not that
there were two *different* variables occupying the *same* memory.
-Howard

Break given.

Your example doesn't swap two integers, it swaps one.

I know exactly what is meant -- using the xor technique on two variables will
never fail; using the xor technique on the same variable (same literal
variable, reference, or pointer -- still all *ONE* variable) will fail for most
cases.

You have given example(s) of the latter, *not* the former. There is no
disagreement on the latter.
 
J

Julie

Ioannis said:
Since you use the malloc() family and this is cross posted to clc, I
assume you use C.

You assume wrong.

malloc is part of C **AND** C++.
In C this casting is not needed.

however it is valid, and required in C++.
array[0] = 42;
array[1] = 9000;
array = (int *)realloc(array, sizeof(int) * (--count));

In C this casting is not needed.

however it is valid, and required in C++.
 
J

Julie

Howard said:
So who ever sid the procondition was that you were swapping two non-pointer,
non-reference variables? If you're writing the swap as a function, you have
to use references or pointers, or else you'll only be swapping local copies.
That's what makes this an important consideration, because those pointer or
references parameters could be referring to the same memory location. The
swap function need to contain a check to handle that specific case.

-Howard

All that was said was that there were two variables (originally numbers) that
were to be swapped. Your examples use 1 variable, aliased 1 or more times.

Write your code with 2 variables where the xor trick fails, excluding placement
new.
 
B

Buster

Owner@buster ~/projects/cxx/quine
$ g++ -o quine-1.5.exe quine-1.5.cpp

Owner@buster ~/projects/cxx/quine
$ ./quine-1.5.exe | cmp - quine-1.5.cpp

Owner@buster ~/projects/cxx/quine
$ ./quine-1.5.exe
#include <iostream>
#include <ostream>
#include <string>
#include <iterator>
#include <algorithm>

using namespace std;

template <typename F>
struct format_type
{
format_type (const string & s, ostream & stream, F fun)
: b (s.begin ()), e (s.end ()), out (stream), f (fun)
{
while (condition ()) b = f (i, out);
}

bool condition ()
{
copy (b, i = find (b, e, '%'),
ostream_iterator <char> (out, ""));
return i != e;
}

private:
string::const_iterator b, e, i;
ostream & out;
F f;
};

template <typename F>
void format (const string & s, ostream & stream, F f)
{ format_type <F> (s, stream, f); }

struct p
{
template <typename iter>
iter operator () (iter i, ostream & out)
{
out << '%' << * ++ i;
if (* i == 'n') out << "\"\n \"";
return ++ i;
}
};

struct q
{
template <typename iter>
iter operator () (iter i, ostream & out)
{
switch (* ++ i)
{
case '%': out << '%'; break;
case 'b': out << '\\'; break;
case 'n': out << '\n'; break;
case 'q': out << '\"'; break;
case 's': format (s, out, p ()); break;
}
return ++ i;
}
q (const string & s) : s (s) { }
const string & s;
};

int main ()
{
string s (
"#include <iostream>%n"
"#include <ostream>%n"
"#include <string>%n"
"#include <iterator>%n"
"#include <algorithm>%n"
"%n"
"using namespace std;%n"
"%n"
"template <typename F>%n"
"struct format_type%n"
"{%n"
" format_type (const string & s, ostream & stream, F fun)%n"
" : b (s.begin ()), e (s.end ()), out (stream), f (fun)%n"
" {%n"
" while (condition ()) b = f (i, out);%n"
" }%n"
"%n"
" bool condition ()%n"
" {%n"
" copy (b, i = find (b, e, '%%'),%n"
" ostream_iterator <char> (out, %q%q));%n"
" return i != e;%n"
" }%n"
"%n"
"private:%n"
" string::const_iterator b, e, i;%n"
" ostream & out;%n"
" F f;%n"
"};%n"
"%n"
"template <typename F>%n"
"void format (const string & s, ostream & stream, F f)%n"
"{ format_type <F> (s, stream, f); }%n"
"%n"
"struct p%n"
"{%n"
" template <typename iter>%n"
" iter operator () (iter i, ostream & out)%n"
" {%n"
" out << '%%' << * ++ i;%n"
" if (* i == 'n') out << %q%b%q%bn %b%q%q;%n"
" return ++ i;%n"
" }%n"
"};%n"
"%n"
"struct q%n"
"{%n"
" template <typename iter>%n"
" iter operator () (iter i, ostream & out)%n"
" {%n"
" switch (* ++ i)%n"
" {%n"
" case '%%': out << '%%'; break;%n"
" case 'b': out << '%b%b'; break;%n"
" case 'n': out << '%bn'; break;%n"
" case 'q': out << '%b%q'; break;%n"
" case 's': format (s, out, p ()); break;%n"
" }%n"
" return ++ i;%n"
" }%n"
" q (const string & s) : s (s) { }%n"
" const string & s;%n"
"};%n"
"%n"
"int main ()%n"
"{%n"
" string s (%n"
" %q%s%q);%n"
" format (s, cout, q (s));%n"
"}%n"
"");
format (s, cout, q (s));
}

Owner@buster ~/projects/cxx/quine
$
 
J

Julie

Ioannis said:
You are right about that, however in reality a decent swap
implementation would check if the passed arguments have the same value
so as to avoid unneeded operations.

Regards,

Ioannis Vranos

or simply state the precondition that the swap operates on 2 variables, no
check is necessary or warranted.
 
I

Ioannis Vranos

Julie said:
You assume wrong.

malloc is part of C **AND** C++.




however it is valid, and required in C++.

In C this casting is not needed.


however it is valid, and required in C++.


Ok, Ok. :)






Regards,

Ioannis Vranos
 
J

Julie

Howard said:
And the need for that check is exactly one of the arguments for why this
choice of implementation was no better than using a temporary variable in
the first place. If you can afford the space for the code to check this
condition, then you can surely afford the space for the temporary integer
variable instead.

-Howard

You don't need the 'check' -- it is a precondition of the function: swap two
variables (numbers) w/o using a temporary.

Not everything has to have checks:

free((void*)12345);

is going to die, as well it should as the spec clearly defines what is a valid
parameter, anything else is undefined.
 
J

Julie

josh said:
union
{
int x;
int y;
} a;

&a.x == &a.y

-josh

Yes, my good friend, unions, forgot all about them.

Still, you really only have 1 variable, just two different ways of looking at
it.
 
I

Ioannis Vranos

Julie said:
or simply state the precondition that the swap operates on 2 variables, no
check is necessary or warranted.



A decent swap function should perform a value equality check both for
safety and run-time efficiency, especially when this does not impose any
space/time concerns, perhaps in the style:


template<class T>
void swap(T &a, T &b)
{
if(a!=b)
{
// ...
}
}



In any case, we should stick with std::swap and provide specialisations
when necessary.






Regards,

Ioannis Vranos
 
H

Howard

Julie said:
Howard wrote:
Break given.

Thanks! :)
Your example doesn't swap two integers, it swaps one.

I know exactly what is meant -- using the xor technique on two variables will
never fail; using the xor technique on the same variable (same literal
variable, reference, or pointer -- still all *ONE* variable) will fail for most
cases.

You have given example(s) of the latter, *not* the former. There is no
disagreement on the latter.

There is no disagreement on the former, either. Just a difference in the
wording of the problem. To me, two pointers or references that refer to the
same memory location are still two differrent variables. But you are
correct...the memory locations being swapped in such a case are identical,
thus you could rightfuly call them "the same".

The importance, though, as it relates to actually *writing* a swap function,
is that the swap function itself cannot guarantee in advance that the
reference or pointer parameters passed into it will not at some point refer
to the same location in memory. Therefore, it is vital that such a swap
function include a guard against swapping the "same" variable. (Unless, of
course, you clearly document that the function does *not* guard against such
behavior, and declare such usage of the function as a violation of its
contract, generating undefined behavior.)

-Howard
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top