Programming Puzzle

D

Dan Pop

In said:
That is not much universal in my C++ world, since for non-POD types such
a generic function would invoke undefined behaviour.

Your C++ world is irrelevant once you cross-post to comp.lang.c.
For POD types, I would use a char/unsigned char array of fixed size (or
VLA in your world) or a char/unsigned char in the extreme, if malloc()
family failed.

You must be a first class idiot if you *rely* on being able to use
a VLA of the same size as a failed malloc request. And you have
no recovery strategy after the attempt to allocate it fails: your program
has already invoked undefined behaviour and has, probably, crashed and
burned. So, forget about VLAs. Using a *small*, statically allocated
buffer or a single unsigned char object are, however, valid alternatives.

Dan
 
H

Howard

Dan Pop said:
In <[email protected]> Ioannis Vranos

Your C++ world is irrelevant once you cross-post to comp.lang.c.

Well, why the heck are you cross-posting to C++ if C++ is irrelevant?
You must be a first class idiot if you *rely* on being able to use

This is the second time in a row you're resorted to insults. Care to keep
the discussion llimited to the subject?
 
I

Ioannis Vranos

Dan said:
You must be a first class idiot if you *rely* on being able to use
a VLA of the same size as a failed malloc request.


For accuracy, I was talking about VLA with a small fixed size (instead
of a built in array), however VLAs are a mess so I would not use it
anyway (and also C99 is not one of my favourite standards).


And you have
no recovery strategy after the attempt to allocate it fails: your program
has already invoked undefined behaviour and has, probably, crashed and
burned. So, forget about VLAs. Using a *small*, statically allocated
buffer or a single unsigned char object are, however, valid alternatives.



Yes or single char ones.






Regards,

Ioannis Vranos
 
J

Julie

Risto said:
There is a doubly-linked-list algorithm that uses the same
memory location for both forward and backward pointers.
They are stored as XORred, and given the address of an
anchor node, the chain of nodes can be traversed at either
direction.

- Risto -

Could you please post the relevant code that sets up two variables that share
the same memory location?

Thanks
 
D

Dan Pop

Well, why the heck are you cross-posting to C++ if C++ is irrelevant?

For the simple reason that C and C++ have a common subset and it is
perfectly possible to talk about swapping in the context of this common
subset.

If the OP wanted a discussion including the C++ features outside this
common subset s/he wouldn't have cross-posted to c.l.c in the first place.

Or am I missing something?

Dan
 
D

Dan Pop

In said:
For accuracy, I was talking about VLA with a small fixed size (instead

What is a *variable* length array with a small *fixed* size? Looks like
an oxymoron to me...
of a built in array), however VLAs are a mess so I would not use it
anyway (and also C99 is not one of my favourite standards).

VLAs are far from being a mess. It's the availability of conforming C99
implementations that renders them useless for portable programming.
Yes or single char ones.

Nope, plain char is NOT exempt from trap representations. Furthermore,
even without trap representations, plain char can have padding bits and
a -0 may become a +0 when copied via a char on one's complement and
sign-magnitude implementations:

3 If the implementation supports negative zeros, they shall be
generated only by:

- the &, |, ^, ~, <<, and >> operators with arguments that
produce such a value;

- the +, -, *, /, and % operators where one argument is a negative
zero and the result is zero;

- compound assignment operators based on the above cases.

It is unspecified whether these cases actually generate a
negative zero or a normal zero, and whether a negative zero
^^^^^^^^^^^^^^^^^^^^^^^^^^^
becomes a normal zero when stored in an object.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So, when thinking in terms of accessing value representations on a
byte by byte basis, the *one and only* type suitable for the job is
unsigned char: no padding bits, no trap representations, all values
from 0 to 2 ** CHAR_BIT - 1 represented in pure binary.

Dan
 
D

Dingo

Julie said:
Nope -- a union is still a single variable, with just different ways to access
it.
Could you please provide your preferred definition of "variable".
I wish to understand why you think a union member is not one.
 
I

Ioannis Vranos

Dan said:
What is a *variable* length array with a small *fixed* size? Looks like
an oxymoron to me...


C++'s std::vector for example is of variable length, however usage in
the style:


vector<int> somearray(5);

is too common.


Nope, plain char is NOT exempt from trap representations. Furthermore,
even without trap representations, plain char can have padding bits and
a -0 may become a +0 when copied via a char on one's complement and
sign-magnitude implementations:



I do not know/have understood what "trap representations" actually are,
however in C++98 it is guaranteed to be safe to read POD types as
sequences of chars and unsigned chars:


"For any complete POD object type T, whether or not the object holds a
valid value of type T, the underlying bytes (1.7) making up the object
can be copied into an array of char or unsigned char.36) If the content
of the array of char or unsigned char is copied back into the object,
the object shall subsequently hold its original value.

[Example:

#define N sizeof(T)
char buf[N];
T obj; // obj initialized to its original value
memcpy(buf, &obj, N); // between these two calls to memcpy,
// obj might be modified
memcpy(&obj, buf, N); // at this point, each subobject of obj of
// scalar type
// holds its original value
—end example]



For any POD type T, if two pointers to T point to distinct T objects
obj1 and obj2, if the value of obj1 is copied into obj2, using the
memcpy library function, obj2 shall subsequently hold the same value as
obj1.

[Example:
T* t1p;
T* t2p;
// provided that t2p points to an initialized object ...
memcpy(t1p, t2p, sizeof(T)); // at this point, every subobject of POD
//type in *t1p contains
// the same value as the corresponding subobject in *t2p
—end example]"






Regards,

Ioannis Vranos
 
P

Prateek R Karandikar

Joona said:
C99 answer:

4 Some operators (the unary operator ~, and the binary operators
<<, >>, &, ^, and |, collectively described as bitwise
operators) are required to have operands that have integer
type. These operators return values that depend on the internal
representations of integers, and have implementation-defined
and undefined aspects for signed types.

Consider, for example, 0 ^ 0, whose result (an int with all bits set) is
a trap representation, as explicitly allowed by C99 for one's complement.

It's hard to find an example for two's complement using the ^ operator,
but it can be done with ~INT_MAX, if the representation with the sign
bit set and all the value bits reset is a trap representation (again,
explicitly allowed by C99 for two's complement and sign-magnitude).

Whether real world implementations have such trap representations is a
completely different story...

Dan

Off-topic. The topic of this group is Standard C++, not C99.
 
P

Prateek R Karandikar

Joona said:
C99 answer:

4 Some operators (the unary operator ~, and the binary operators
<<, >>, &, ^, and |, collectively described as bitwise
operators) are required to have operands that have integer
type. These operators return values that depend on the internal
representations of integers, and have implementation-defined
and undefined aspects for signed types.

Consider, for example, 0 ^ 0, whose result (an int with all bits set) is
a trap representation, as explicitly allowed by C99 for one's complement.

It's hard to find an example for two's complement using the ^ operator,
but it can be done with ~INT_MAX, if the representation with the sign
bit set and all the value bits reset is a trap representation (again,
explicitly allowed by C99 for two's complement and sign-magnitude).

Whether real world implementations have such trap representations is a
completely different story...

Dan

Off-topic. The topic of this group is Standard C++, not C99.
 
P

Prateek R Karandikar

Joona said:
C99 answer:

4 Some operators (the unary operator ~, and the binary operators
<<, >>, &, ^, and |, collectively described as bitwise
operators) are required to have operands that have integer
type. These operators return values that depend on the internal
representations of integers, and have implementation-defined
and undefined aspects for signed types.

Consider, for example, 0 ^ 0, whose result (an int with all bits set) is
a trap representation, as explicitly allowed by C99 for one's complement.

It's hard to find an example for two's complement using the ^ operator,
but it can be done with ~INT_MAX, if the representation with the sign
bit set and all the value bits reset is a trap representation (again,
explicitly allowed by C99 for two's complement and sign-magnitude).

Whether real world implementations have such trap representations is a
completely different story...

Dan

Off-topic. The topic of this group is Standard C++, not C99.
 
J

Julie

Prateek said:
Off-topic. The topic of this group is Standard C++, not C99.

1) You may want to learn how to post just a *single* copy of your message.

2) This thread (with the exception of your responses) has been to both C++
*AND* C groups.
 
J

Julie

Dingo said:
Could you please provide your preferred definition of "variable".
I wish to understand why you think a union member is not one.

I'll retract my statement -- I'll agree that a union does allow for two
variables to share the same memory address.
 
F

Foobarius Frobinium

Chapter and verse, please.

Well, you should, depending on compiler flags. gcc -Wall gives you:

foo.c: In function `main':
foo.c:10: warning: control reaches end of non-void function

Which is besides the point as I was making a joke about how shitty MS
programs are. Obviously, this will segfault.
 
D

Dave Vandervies

Siemel Naran <[email protected]> scribbled the following



Tail recursion is first doing the computation, then recursing. Head
recursion is the other way around.

There's a generally-accepted definition that pins it down more precisely
than this.

A "tail call" is a call to a function where the return value of that
function is returned directly from the calling function without any
further processing:
--------
int foo(int i)
{
int j;
j=do_nontail_call(i);
return do_tail_call(j);
}
--------
(Or even:
--------
int foo2(int i)
{
return do_tail_call(do_nontail_call(i));
}
--------
which when untangled does the exact same thing, but doing the untangling
may lead to a clearer idea of where exactly the boundary is.)
It's possible, in theory at least (though some parameter passing
mechanisms make it too difficult to be worth the effort), to remove
the invocation record for the function making the tail call from the
invocation-record stack and have the tail-called function return directly
to the function that made the last non-tail function call.

"Tail recursion" is recursion accomplished using tail calls. Note that
this is a property of the calls made and not simply of the function
itself, and it's possible to make a tail-recursive and non-tail-recursive
call to the same function:
--------
void untested_and_probably_buggy_quicksort(int *data,size_t n)
{
int middle_index;

/*Make sure the recursion terminates*/
if(n<2)
return;

/*Hand-waving to avoid having to do too much thinking*/
middle_index=untested_and_probably_buggy_partition(data,n);

/*Non-tail-recursive call*/
untested_and_probably_buggy_quicksort(data,middle_index-1);

/*Tail-recursive call*/
untested_and_probably_buggy_quicksort(data+middle_index+1,
n-middle_index-1);
/*Note that even though we're not actually returning anything,
we're not doing anything between when the tail call returns
and when we return.
*/
}
--------
The aforementioned invocation-record-stack optimization for tail calls
has the effect of converting tail recursion into iteration, so the above
is exactly equivalent (assuming this optimization does indeed occur) to:
--------
void untested_and_probably_buggy_quicksort(int *data,size_t n)
{
int middle_index;

/*A compiler may not be clever enough to convert the exit
condition from the if(...)return form to while(...) directly,
but a good optimizer should generate the same code for
both forms.
*/
while(n>1)
{
/*These are the same as the tail-recursive version*/
middle_index=untested_and_probably_buggy_partition(data,n);
untested_and_probably_buggy_quicksort(data,middle_index-1);

/*Here we're replacing the old parameters with the values
they'd have in the next (tail-recursive) invocation,
then going back to the top of the loop
*/
data=data+middle_index+1;
n=n-middle_index-1;
}
}
 
G

Gordon Burditt

#include "card_t.h" /* This is part of the program, NOT the
#implementation*/ define DECKSIZE 52
...
int i,j;
card_t deck[DECKSIZE];

/* bogo-shuffle the cards */
for (i = 0; i < DECKSIZE; i++) {
for (j = 0; j < DECKSIZE; j++) {
/*
* DO NOT CHANGE THE LINE OF CODE BELOW.
* Note: rand() % 2 is used here because it is
* a poor random number generator in many
* implementations, and if it isn't possible to
* cheat, *THEY* will break your legs repeatedly
* and cut off your supply of semicolons.
*/
if (rand() % 2) {
swap(&deck, &deck[j]);
}
}
}



Hi,

I apologize, for this might not be the topic of this thread, however I feel
compelled to comment on this bogo-shuffle: is that taken from a text on how
*not* to generate a random permutation?


No, it was not taken from a text. It is original code written for
the post. I needed an excuse for why I might want to swap two
elements of an array, possibly even if they happen to be the same
element.
a) The use of rand() % 2 is bad. In a different thread I came across a
random number generator that would produce a strictly alternating sequence
of bits when used this way. The funny comment indicates that the author was
acutally aware of this shortcoming.

An implementation of rand() can SUCK(tm) without producing a strictly
alternating sequence of bits, although it's hard to make it suck
strictly worse than that (I suppose always returning 1 or always
returning zero qualifies as sucking worse, though). Why do you
think the comment was in there?
b) The method has quadratic runtime in DECKSIZE. Clearly a random-shuffle
can and should be done in linear time.

Clearly a performance criteria for a method does not have to specify
a MAXIMUM runtime; sometimes it is desired for performance to be
at least so BAD and worse is possible (for example, making the
algorithm HARD to implement efficiently was one concern in the
original design of UNIX password encryption). Another example is
the use of 16 nested infinite loops where a single infinite loop
would do.
c) Finally, even with a perfect random number generator, this method is
guaranteed *not* to give equal probabilities to the different permutations.
For small DECKSIZE, the difference is even noticable.

A bogosort sorts by generating all possible permutations of the
input, then (bubble) sorting the permutations by the number of
elements out of order, and returning the one with the least number
of elements out of order. In other words, it replaces the job of
sorting N elements to one of sorting N! elements. A recursive
bogosort uses bogosort instead of bubble sort to sort the permutations,
thus replacing the job of sorting N elements with one of sorting
N!!!!!!!!!!!!!!!!!!!!!!!!!!!! ... It is also likely to cause the
heat death of at least 2**(N!) universes before it finishes (by
crashing due to trying to use more memory than there are particles
in the universe).

A bogo-shuffle, named after bogosort, is a poorly-implemented, SLOW
shuffle but it doesn't come close to the inefficiency of the recursive
bogosort. You probably won't ever get to use it enough to discover
that the probabilities aren't equal. Nobody lives that long.

Gordon L. Burditt
 
J

Jim

Agreed.

For me, saying that 'swap operates on two variables' would be sufficient, but
for the sake of clarity, it could be documented that references to the same
variable leads to undefined behavior.

Isn't this exectly what C99's 'restrict' type qualifier is for?

Jim
 
T

Tim Northover

Jatinder wrote:

Ok I couldn't resist so I 'll give my answers to these questions. As a
poster has done previously, where "C" is mentioned I consider C++98 (I
view these from clc++).

Me neither.
Note: There is a restriction for if statements, but if statements do not
constitute a loop.
<snip recursive version>

I suspect what they meant was something of this sort:

#include <iostream>

template <int i> class Count
{
Count<i-1> c;
public:
Count() : c() { std::cout << i << std::endl; }
};

template <> class Count<1>
{
public:
Count() { std::cout << "1" << std::endl; }
};

int main(void)
{
Count<100> c;
return 0;
}

With a modification to count back down.

Tim.
 

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,780
Messages
2,569,611
Members
45,281
Latest member
Pedroaciny

Latest Threads

Top