c# is a good way to learn c

D

DM McGowan II

Tom said:
Most of which exist as native .NET components... And would never have
to be written in C# because they already exist. This is what I meant by
Time-To-Market. With C#, you spend more time worrying about the problem
then writting socket libraries and service base classes...

Sockets - System.Net and System.Net.Sockets
Service base class - System.ServiceProcess.ServiceBase (along with a
whole bunch of classes to support the manipulation of services).
Unit Testing - NUnit (not part of .NET properly, but it is a freely
available product written entirely in C#)
String - System.String, System.Text, System.Text.RegularExpressions
FileName - System.IO, System.IO.Path
XML stuff - System.XML. I'm not sure what your's does exactly, but I'm
sure there is something close or would be simpler to implement.

I'm not dissing you. I'm just pointing out that there is a reason that
VB and Java were more popular for buisness programming... Less bugs, and
less time to market.




Actually, I disagree :) I have always like the non-gui parts the best.

Tom I totally agree...time to market is the key, whether your market is
the Accounting Department down the hall or Sally Customer on the other
side of the planet.

With Mono or .NET you can have your cake and eat it too. C# and the
Framework rocks!

Now if only Mr. Gates would kindly follow my suggestion and make Windows
Open Source...at least the kernel.
 
E

Ernest Borgnine

Jeff_Relf said:
int Swap_32 ( int X ) { uint_8_P P = ( uint_8_P ) & X + 4 ;
return * -- P << 24 | * -- P << 16 | * -- P << 8 | * -- P ; }
main() { printf( "%x", Swap_32( 0x84838281 ) ); }


Then John Henry he did hammah,

He did make his hammah soun',

Says now one more lick fore quittin' time,

An' I'll beat this steam drill down,

An' I'll beat this steam drill down.

http://www.ibiblio.org/john_henry/folk.html
 
J

Jeff_Relf

Hi Ernestine, Ya wrote: << I wish I knew what ya all were talkin' 'bout,
Precedence ain't got not'n to do with what beer I drink. >>

When coding, it helps to know the order of execution,
Spooky apparently has no clue about that.

Unable to recognize a simple coder, he also has no clue about who I am.
 
?

=?iso-8859-1?Q?Lin=F8nut?=

Greg Cox poked his little head through the XP firewall and said:
To be accurate, I'm an ex-softie...

As to the rest, what are you talking about? Are you actually saying it
would be better to dump a million lines of existing C++ code with 99%+
of the bugs already fixed and rewrite from scratch rather than reuse the
code and spend resources fixing the remaining bugs?

I don't know. You tell me. You're the one "in the know".

I anticipate that, upon arriving at work today, my XP box will inform me
that I have a critical patch to install for Office.
 
T

The Ghost In The Machine

In comp.os.linux.advocacy, Jeff_Relf
<[email protected]>
wrote
Hi Ernestine, Ya wrote: << I wish I knew what ya all were talkin' 'bout,
Precedence ain't got not'n to do with what beer I drink. >>

When coding, it helps to know the order of execution,
Spooky apparently has no clue about that.

I have more than a clue than you might think.

The HP PA Risc architecture, for instance (a Unix
workstation running HP/UX at one point which I had the
privilege of using for some years in my prior job --
nice machine, actually), had some very interesting ideas
of exactly when to execute an instruction, for example.
Basically, the processor had the option (and usually took
it) of executing the instruction following a conditional
branch, even though the branch was taken.

This presumably gave compiler optimizers fits.

http://h21007.www2.hp.com/dspp/tech/tech_TechDocumentDetailPage_IDX/1,1701,958!33!250,00.html

<excerpt>

pa-risc 1.1 architecture and instruction set reference manual
[...]
concept of delayed branching

All branch instructions exhibit the delayed branch
feature. This implies that the major effect of the branch
instruction, the actual transfer of control, occurs one
instruction after the execution of the branch. As a result,
the instruction following the branch (located in the delay
slot of the branch instruction) is executed before control
passes to the branch destination. The concept of delayed
branching is illustrated in Figure 42.

Execution of the delay slot instruction, however, may be
skipped ("nullified") by setting the "nullify" bit in the
branch instruction to 1.

delayed branching
program segment
Location Instruction Comment
100 STW r3, 0(r6) ; non-branch instruction
104 BLR r8, r0 ; branch to location 200
108 ADD r7, r2, r3 ; instruction in delay slot
10C OR r6, r5, r9 ; next instruction in linear code
; sequence
.. .
.. .
.. .
200 LDW 0(r3), r4 ; target of branch instruction

execution sequence
Location Instruction Comment
100 STW r3, 0(r6) ;
104 BLR r8, r0 ;
108 ADD r7, r2, r3 ; delay slot
; instruction is executed before
200 LDW 0(r3), r4 ; execution of
; target instruction

</excerpt>

Also, the compiler, when presented with your example (paraphrased):

int swap32(int v)
{
unsigned char * p = (unsigned char *) &v;
p += 4;
return (*--p << 24)|(*--p << 16)|(*--p << 8)|(*--p);
}

has the option of evaluating the expression either
left-to-right or right-to-left, at the whim of the compiler
designer(s). (Unless Kelsey tells me different -- he's more
familiar with the C/C++ specifications than I am. :))

However, some of the best optimization is done within
the compiler by reading the code *backwards* -- e.g., the
compiler might forbear a final store for a local variable
to save time, as the variable will just be discarded
later. In fact, the better compilers don't bother with
storing local variables at all, if they don't need to and
sufficient registers are available. There are a number of
other possibilities, some of which I've already alluded to.

I'm also familiar with some of the problems facing optimizers.
Briefly put, the code

int * selectAPointer(int * ap, int * bp, int * cp);

int doSomething()
{
int a = 1, b=2, c=3, *p;
int d1 = a+b+c;
p = selectAPointer(&a,&b,&c);
*p ++;
int d2 = a+b+c;
}

would have to be very carefully handled, as p might very
well point to a, b, or c after selectAPointer() returns,
or it might point to somewhere compiletely different -- but
there's no way for the compiler to tell here. Therefore,
both d1 and d2 will probably have to be explicitly coded as
something along the lines of:

MOV a(SP), r1
ADD b(SP), r1
ADD c(SP), r1
MOV r1, d1(SP)

Without the selectAPointer() call, the compiler has
the option of *merging* d1 and d2, conceptually (using
a method called "common subexpression elimination"), and
potentially making the code more efficient -- if only
by 4 instructions and a now-redundant stackframe location.

It might also remove the last instruction, and keep
d1 in a register for the length of the code.

I bring all this up to illustrate that your pattern of
linear thinking, generally desirable though it is, has
some drawbacks.

Be clear in your coding now, and your coding will be
clear to you later, when you reread it. :) It will
also be clear to the compiler, when *it* reads it.

Of course, knowing the way you code, the code may very
well be clear to you already; just don't expect it to
make sense to the rest of us, without careful analysis.
The ideal, at least to a manager's thinking, is to shuffle
people around at need between projects, both to keep them
interested and to maximize utilization of resource --
this may require reading and modifying someone else's code.

There is at least one hacker [according to Stephen Levy,
anyway] out there who writes extremely ingenious -- most
would say incomprehensible -- code with nary a comment.
I would subscribe to that notion myself, except that
most machine code is pretty much as one might see from
the foregoing: without the comments, one might as well be
looking at gobbledygook, although the machine "understands"
it perfectly well as long as it knows which registers to
modify and which memory locations to wiggle. PA Risc is
slightly unusual in its "delay slot", but is otherwise
more or less typical regarding machines, though it has
more registers (32 at the user level; I can't say I played
with the space registers all that much) than one's typical
Intel hardware, and is probably better designed from an
instruction standpoint, since it didn't have to worry about
8080 source code compatibility. And even then, the above
is *assembly*; the actual machine code would be little more
than a bunch of hex numbers or charge packets.

It was once common for system programmers to pore over
pages of printouts dumped from a console in old IBM
hardware, for example (fortunately, that was before
my time). Of course, that was also when an IBM machine
had all of maybe 8K or 16K of RAM.

I've also caught MS VC++ (version 5.0) in a bona fide
memory corruption condition. I'll admit I'm not sure who
created the bug (as it was running on NT3.5 or NT4, whose
memory allocation was slightly suspect) but the assembly
listing from a section of code I was working on at the
time was quite corrupted for the space of about 10 lines.
As Ronald Reagan once said, "trust but verify".

However, compiler mangling is not the first thing that
comes to mind when one's code does the goofy, though --
most likely, it's one's code.
Unable to recognize a simple coder, he also has no clue about who I am.

There are more things in Heaven and Earth, Horatio, than are dreamt of
in your philosophies.
-- Hamlet, Act I, Scene 5
 
K

Kelsey Bjarnason

[snips]
typedef unsigned __int8 * uint_8_P ;
__int32 Swap_32 ( __int32 X ) { uint_8_P P = ( uint_8_P ) & X + 4 ;
return * -- P << 24 | * -- P << 16 | * -- P << 8 | * -- P ;
}

Hm...where to start?

[1] __int8 isn't exactly standard.
[2] The expression

* -- P << 24 | * -- P << 16 | * -- P << 8 | * -- P

can be evaluated in any order the compiler chooses, as '|' is
commutative (a|b == b|a) and associative (a|(b|c) == (a|b)|c).
(If overloaded, of course, things get interesting. I'd
frankly have to research it.) Of course, because of the side
effects, the commutativity sort of goes out the window, but
AFAIK (Kelsey, are you still with us or have you given up on
this thread? :) ) expressions of this sort are undefined for
precisely this reason.

Sort of still here. As a simple rule of thumb, if code relies on order of
evaluation, it's broken. Further, a statement may modify the value of an
object at most once between sequence points or invoke undefined behavior;
the code above modifies P what, four times?

This sort of thing - Relf's raison d'etre if you will - is generally
referred to as "unwarranted friendliness with the compiler", or "my
compiler does it this way, so it's okay." This assumes the compiler will
never change (unwarranted assumption right there), that the code will never
move to another compiler (or another version of the same compiler, which
works differently) and that nothing else will ever change, such as building
the code with a compiler that splits operations across processors (or CPU
threads).
 
K

Kelsey Bjarnason

[snips]

Jeff_Relf said:
No... No... No... I has to follow the left to right standard.

There is no "right to left standard". MS may document that _their_ compiler
does it that way, but that's them. Nothing to do with standards.
You concluded: <<
Admittedly, you've, erm, impressed us with the readability of your code.
I think Kelsey has, too -- but I know whose code I'd prefer to maintain.
:) >>

I wouldn't want you anywhere near my code.

Not much of a worry about that; you don't actually have any code, just
gibberish.
 
K

Kelsey Bjarnason

Jeff_Relf said:
Hi Eran_Kampf, Re: How I won't use C# because it doesn't have #define,
You told me: << Funny thing is, 10 years ago there were assembly
developers
talking about C and C++ the same way you take about C#. >>

C++ is as high_level as I want it to be because I can always add a DLL.

But C#, with it's Program_Inside_a_Program, lack of #define
and UnGodly_Bloat are a straightjacket I refuse to wear.

Funny thing, that. Here the rest of the C++ world has agreed that #define
is largely pointless in C++ and even, in many cases, unnecessarily
dangerous, but you, of course, knowing better than almost the entire C++
coding world insist on doing things the wrong way.

Must be nice to live in your universe.
 
G

gerry

sorry to disagree Kelsey but the current language specifications for c , c++
, c# and java ALL agree with Relf on this point.
i tend to agree with your opinions regarding this type of coding , but they
are just opinions.



Kelsey Bjarnason said:
[snips]

Jeff_Relf said:
No... No... No... I has to follow the left to right standard.

There is no "right to left standard". MS may document that _their_ compiler
does it that way, but that's them. Nothing to do with standards.
You concluded: <<
Admittedly, you've, erm, impressed us with the readability of your code.
I think Kelsey has, too -- but I know whose code I'd prefer to maintain.
:) >>

I wouldn't want you anywhere near my code.

Not much of a worry about that; you don't actually have any code, just
gibberish.
 
T

The Ghost In The Machine

In comp.os.linux.advocacy, Jeff_Relf
<[email protected]>
wrote
Hey Spooky ! ( Mr. Coders_Are_Friends_of_Mine_and_Relf_is_No_Coder )

Hederman has me plonked and you, knowning better,
are telling him I'm unemployed.

Oops. My bad. Nice to feel needed.
You think I'm lying when I repeatedly tell you that
only coding has paid my rent since the start of 1982 ?

I delivered code to paying customers 2 hours ago...
What about you, Mr. Coders_Are_Friends_of_Mine_and_Relf_is_No_Coder ! ? ?

I'm fulltime, mister.

At least you've figured out that #including "implementation.c"
is a bad thing. (I kid you not; one contractor we hired way back
when actually did exactly that in his code.)
And, No, I'm not being rhetorical here... I want an answer... Mr. Dodger.

See above.
You concluded: <<
So we use automated translators -- compilers -- to make our
wishes known, and sometimes the translation goes very badly awry
-- Jeff's "Swap_32()" being a case in point.
In Jeff's case it's probably because of a misuse of the language.

Well, Mr. Coders_Are_Friends_of_Mine_and_Relf_is_No_Coder,
do you think you could compile the code below and tell me the result ?

typedef unsigned char * uint_8_P ;
int Swap_32 ( int X ) { uint_8_P P = ( uint_8_P ) & X + 4 ;
return * -- P << 24 | * -- P << 16 | * -- P << 8 | * -- P ; }
main() { printf( "%x", Swap_32( 0x84838281 ) ); }

Sure. On my Linux/x86 I get 84838281. Nice no-op. (Or is it
supposed to swap only on big-endian hardware? Is it supposed
to even *run* on big-endian hardware?)

gcc version 3.3.5-20050130 (Gentoo Linux 3.3.5.20050130-r1,
ssp-3.3.5.20050130-1, pie-8.7.7.1)

I don't have my SPARC up at the moment or I'd try it on that as well.

Color me unimpressed.

Just so you know: I can code this a bunch of ways. Here's some examples
that actually *work*:

int Swap_32_Shifty(int X)
{
return (X & 0xff000000) >> 24
| (X & 0x00ff0000) >> 8
| (X & 0x0000ff00) << 8
| (X & 0x000000ff) << 24;
}

This one's simple enough but the "int" should probably be unsigned,
and assumes everything is 32-bit -- an increasingly problematic
issue as more 64-bit stuff comes online. Still, it at least
puts the bytes in the right place, unlike someone else's example.

int Swap_32_Union(int X)
{
union {
int x;
unsigned char c[sizeof(int)];
} u;

u.x = X;
int i;
int j;

for(i = 0, j = sizeof(int)-1; i < j; i++,j--)
{
unsigned char t = u.c;
u.c = u.c[j];
u.c[j] = t;
}

return u.x;
}

This one's the safest of the bunch.

int Swap_32_Doubleswap(int X)
{
register char tmp;
tmp = *((unsigned char *) &X + 3);
*((unsigned char *) &X + 3) = *((unsigned char *) &X + 0);
*((unsigned char *) &X + 0) = tmp;
tmp = *((unsigned char *) &X + 2);
*((unsigned char *) &X + 2) = *((unsigned char *) &X + 1);
*((unsigned char *) &X + 1) = tmp;

return X;
}

Ugly but straightforward; I'm swapping two pairs of bytes. (For brownie
points: find the harmless bug.)

int Swap_32_Doubleswap_Local(int X)
{
register unsigned char tmp;
register unsigned char * P = (unsigned char *) &X;

tmp = P[3]; P[3] = P[0]; P[0] = tmp;
tmp = P[2]; P[2] = P[1]; P[1] = tmp;
return X;
}

A little prettier variant with a local variable holding the pointer.
If coded exactly according to spec (taking the location of a
parameter is, if I'm not mistaken, a bit of a no-no in C/C++):

int Swap_32_Doubleswap_Local_Proper(int X)
{
int Y = X;
register unsigned char tmp;
register unsigned char * P = (unsigned char *) &Y;

tmp = P[3]; P[3] = P[0]; P[0] = tmp;
tmp = P[2]; P[2] = P[1]; P[1] = tmp;
return Y;
}

It turns out Swap_32_Doubleswap and Swap_32_Doubleswap_Local generate
identical code under the optimizer (even with the harmless bug!);
Swap_32_Doubleswap_Local_Proper generates 4 extra lines.

This particularly goofy variant is nevertheless workable:

int Swap_32_Extremely_Goofy(int X)
{
int i;
int result = 0;
static int table[] = {
24,25,26,27,28,29,30,31,
16,17,18,19,20,21,22,23,
8,9,10,11,12,13,14,15,
0,1,2,3,4,5,6,7
};

for(i=0;i<32;i++)
{
if(X & 1) result |= (1<<table);
X >>= 1;
}

return result;
}

though I doubt it's the most efficient.

Or one can simply use

htonl() / ntohl()

These will swap only on little-endian hardware. Of course, that
may be exactly what's desired, as network order is big-endian.
The big advantage to these are that they are predeclared (in
<netinet/in.h>), and therefore no coding at all is required on
the user's part.

For the record, here's a code analysis of the assembly coming out.
(The comments are mine. Note that this is GAS notation, which
is the reverse of what Intel likes.)

..globl Swap_32
.type Swap_32, @function
Swap_32:
pushl %ebp ; standard frame
movl %esp, %ebp ; crap
subl $4, %esp ; make room for the P
leal 12(%ebp), %eax ; load the parameter's addy, +4 ...
movl %eax, -4(%ebp) ; ... then stuff it into P
leal -4(%ebp), %eax ; ... and take &P into ax
decl (%eax) ; --P
movl -4(%ebp), %eax ; ... and take &P into ax again
; (dumb unoptimized code)
movzbl (%eax), %edx ; *P
sall $24, %edx ; << 24
leal -4(%ebp), %eax ; ... and take &P into ax AGAIN
decl (%eax) ; --P
movl -4(%ebp), %eax ; ... I sense a theme here
movzbl (%eax), %eax ; *P
sall $16, %eax ; << 16
orl %eax, %edx ; '|'
leal -4(%ebp), %eax ; ... getting monotonous
decl (%eax) ; --P
movl -4(%ebp), %eax ; ... three guesses
movzbl (%eax), %eax ; *P
sall $8, %eax ; << 8
orl %eax, %edx ; '|'
leal -4(%ebp), %eax ; ... again?
decl (%eax) ; --P
movl -4(%ebp), %eax ; ... finally, the last repetition
movzbl (%eax), %eax ; and the last bit to or in
orl %eax, %edx ; '|'
movl %edx, %eax ; and now return the result
leave ; ... to the caller
ret

If one compiles with -O, one gets:

..globl Swap_32
.type Swap_32, @function
Swap_32:
pushl %ebp ; standard frame
movl %esp, %ebp ; crap
leal 11(%ebp), %ecx ; CX = P = &X + 3; we've
; already decremented at this point
movzbl (%ecx), %eax ; *P
sall $24, %eax ; << 24
decl %ecx ; --P
movzbl (%ecx), %edx ; *P
sall $16, %edx ; << 16
orl %edx, %eax ; '|'
decl %ecx ; --P
movzbl (%ecx), %edx ; *P
sall $8, %edx ; <<8
orl %edx, %eax ; '|'
movzbl -1(%ecx), %edx ; *P
orl %edx, %eax ; '|', leaving result in AX
popl %ebp ; for the caller
ret

It's still wrong but now it's more compact. 18 lines.

Compare that code to the unoptimized Swap_32_Doubleswap():

..globl Swap_32_Doubleswap
.type Swap_32_Doubleswap, @function
Swap_32_Doubleswap:
pushl %ebp ; standard frame
movl %esp, %ebp ; crap
subl $4, %esp ; I declared this "register",
; but oh well; make room for
; it anyway
movzbl 11(%ebp), %eax ; get the 4th byte
movb %al, -1(%ebp) ; save it
movzbl 8(%ebp), %eax ; move first byte
movb %al, 11(%ebp) ; to the fourth byte
movzbl -1(%ebp), %eax ; get the saved byte
movb %al, 8(%ebp) ; and move it to first byte
movzbl 10(%ebp), %eax ; get the 3rd byte
movb %al, -1(%ebp) ; save it
movzbl 9(%ebp), %eax ; move second byte
movb %al, 10(%ebp) ; to the 3rd byte
movzbl -1(%ebp), %eax ; get the saved byte
movb %al, 9(%ebp) ; and move it to second byte
movl 8(%ebp), %eax ; return result
leave ; ... to the caller
ret

and the optimized variant:

..globl Swap_32_Doubleswap
.type Swap_32_Doubleswap, @function
Swap_32_Doubleswap:
pushl %ebp ; standard frame
movl %esp, %ebp ; crap
leal 8(%ebp), %edx ; (unsigned char *) &X
movzbl 3(%edx), %ecx ; get fourth byte
movzbl 8(%ebp), %eax ; get first byte
movb %al, 3(%edx) ; store first in fourth
movb %cl, 8(%ebp) ; store fourth in first
movzbl 2(%edx), %ecx ; get third byte
movzbl 1(%edx), %eax ; get second byte
movb %al, 2(%edx) ; store second in third
movb %cl, 1(%edx) ; store third in second
movl 8(%ebp), %eax ; fetch the result
popl %ebp ; ... and return it
ret

This one's only 14 lines.

Which one's likely to be more efficient, at least on GCC-derived systems?

Which one's more likely to actually function properly?
 
T

The Ghost In The Machine

In comp.os.linux.advocacy, Sean Hederman
<[email protected]>
wrote
message [Snip]
For the record:
32 = 0x00000020
536870912 = 0x20000000
538976288 = 0x20202020

Interesting glitch.

Yeah, I saw that when I ran it, I just the assumed result was a mistake in
Relfies code. Couldn't be bothered to load another C++ compiler to check
this; Can you confirm it's okay on non-MS compilers? If so that's a pretty
shocking MS C++ bug, no matter how much Relf abuses the edges of the C++
standard.

Not sure if it's a bug or not. On GCC/x86 it would return 32.
[snip for brevity]
Uh...you sure you want him to be writing code in *three* languages? :)
(He already mangles C/C++ -- which for purposes of this discussion
is pretty much one language, the way Jeff uses it! -- and, presumably,
Visual Basic, which is apparently an adjunct to his X.CPP.) ...
Actually, in his case it's probably more like trying to
squish as much code in his screen as will uncomfortably
fit, readability be damned.

Probably not a bad thing for him -- but to the rest of us,
it looks like parrot droppings. ...
Not sure it's anything near like Basic. It's probably more like
Relfic -- a language that nobody else has ever invented. :)

Who woulda thunk that a major MS semi-fan like me would have so much in
common with a raving linux fundi ? ;-)

Ah, are you? Well, we can argue in another thread. :)
Does this mean I can sue Jeff for abusing my First Amendment
rights? Oh, bugger, I'm not American dammit. I'd be interested
in reading that ruling if you manage to find it though, sounds
interesting.

I'll have to find it. It's from the 9th Circuit, that much I remember.
 
J

Jeff_Relf

Hi Spooky, Re: My Swap_32(), You wrote: <<
Not sure if it's a bug or not. On GCC/x86 it would return 32. >>

Don't you guys believe in hex ? !

0x84838281 is a much Much better test,
as each byte is labeled and has it's high bit set.

By the way, on a x86, I should've reversed the order, like this:

typedef unsigned char * uint_8_P ;

int Swap_32 ( int X ) { uint_8_P P = ( uint_8_P ) & X ;
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ; }
main() {
// prints 0x84848484 ! ! MS_CPP_7_1 BUG
printf( "%x", Swap_32( 0x84838281 ) ); }

Assigning each byte to a global makes it work, like this:

typedef unsigned char * uint_8_P ; int _1, _2, _3, _4 ;

int Swap_32 ( int X ) { uint_8_P P = ( uint_8_P ) & X ;
return ( _1 = * P << 24 ) | ( _2 = * ++ P << 16 )
| ( _3 = * ++ P << 8 ) | ( _4 = * ++ P ); }

main() {
// prints 0x81828384 as it should.
printf( "%x", Swap_32( 0x84838281 ) ); }

Removing the assignments, but keeping the parens, fails:
return ( * P << 24 ) | ( * ++ P << 16 ) | ( * ++ P << 8 ) | ( * ++ P );

As I said before, MS_CPP fails with or without the optimizer,
and MicroSoft claims that the | operator is evaluated left to right:
http://msdn.microsoft.com/library/d...s/vclang/html/_pluslang_c.2b2b_.operators.asp

Operator |
Name Bitwise inclusive OR
Associativity Left to right
 
K

Kelsey Bjarnason

gerry said:
sorry to disagree Kelsey but the current language specifications for c ,
c++ , c# and java ALL agree with Relf on this point.
i tend to agree with your opinions regarding this type of coding , but
they are just opinions.

Actually, no, they don't.

He's correct about a very limited subset of the problem. However, he
completely fails to grasp the full scope of the problem, to wit, the
undefined behavior as a result of the multiple modifications involved
between sequence points, from which the associativity provides _no_
protection at all.
 
J

Jeff_Relf

Hi Kelsey_Bjarnason ( and Gerry ),

Re: This Swap_32_x86() working for gcc but not for MS_CPP_7_1:

typedef unsigned char * uint_8_P ;

int Swap_32_x86 ( int X ) { uint_8_P P = ( uint_8_P ) & X ;
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ; }
main() {
// prints 0x84848484 ! ! MS_CPP_7_1 BUG
printf( "%x", Swap_32_x86( 0x84838281 ) ); }

You claimed there's, << undefined behavior as a result of
the multiple modifications involved between sequence points,
from which the associativity provides no protection at all. >>

I don't know what you might be asserting here, if anything,
but I hope you have a lot of credible documents to support you.

MS_CPP_7_1's precedence is well defined.

MicroSoft claims that the | operator is evaluated left to right:
http://msdn.microsoft.com/library/d...s/vclang/html/_pluslang_c.2b2b_.operators.asp

Operator |
Name Bitwise inclusive OR
Associativity Left to right

Note that this works:

typedef unsigned char * uint_8_P ; int _1, _2, _3, _4 ;

int Swap_32_x86_NoBug ( int X ) { uint_8_P P = ( uint_8_P ) & X ;
return ( _1 = * P << 24 ) | ( _2 = * ++ P << 16 )
| ( _3 = * ++ P << 8 ) | ( _4 = * ++ P ); }

main() {
// prints 0x81828384 as it should.
printf( "%x", Swap_32_x86_NoBug( 0x84838281 ) ); }

Swap_32_x86() should work like this ( but doesn't ):
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ; }
1. * P -> Reg
2. Reg << 24 -> Reg
3. ++ P -> Reg2
4. * Reg2 -> Reg2
5. Reg2 << 16 -> Reg2
6. Reg | Reg2 -> Reg
7. ++ P -> Reg2
8. * Reg2 -> Reg2
9. Reg2 << 8 -> Reg2
10. Reg | Reg2 -> Reg
11. ++ P -> Reg2
12. * Reg2 -> Reg2
13. Reg | Reg2 -> Reg
14. Return Reg.
 
J

Jeff_Relf

Hi Spooky, Re: If you could try my Swap_32() in gcc,

You told me: << Sure. On my Linux/x86 I get 84838281. Nice no-op.
( Or is it supposed to swap only on big-endian hardware ?
Is it supposed to even *run* on big-endian hardware ? )
gcc version 3.3.5-20050130 (Gentoo Linux 3.3.5.20050130-r1,
ssp-3.3.5.20050130-1, pie-8.7.7.1)
I don't have my SPARC up at the moment or I'd try it on that as well. >>

I had in mind a program on a Bigendian system
that had to swap all the ints inside a .WAV file.
But I lost track of what I was doing... damned alzheimers !

I should've called it Small_is_First_32(), not Swap_32()
Going the other way, where an x86 is reading Large_First ints, it'd be:

typedef unsigned char * uint_8_P ;

__inline int Large_is_First_32 ( int X ) { uint_8_P P = ( uint_8_P ) & X ;
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ; }
main() {
// prints 0x84848484 ! ! MS_CPP_7_1 BUG
printf( "%x", Large_is_First_32( 0x84838281 ) ); }

You told me: << Color me unimpressed. >>

At least I still remember how to rollover in my bed,
get to the store/bathroom, etc. ( ha ha ).

Re: Your return ( X & 0xff000000 ) >> 24 ...

Too ugly for me.

Re: Your unsigned char t = u.c; u.c = u.c[j]; u.c[j] = t;,

That one is Waaay ugly... yuck.

You showed: <<
int Swap_32_Doubleswap( int X ) { register char tmp;
tmp = *((unsigned char *) &X + 3);
*((unsigned char *) &X + 3) = *((unsigned char *) &X + 0);
*((unsigned char *) &X + 0) = tmp;
tmp = *((unsigned char *) &X + 2);
*((unsigned char *) &X + 2) = *((unsigned char *) &X + 1);
*((unsigned char *) &X + 1) = tmp; return X; } >>

That's ugly Spooky, what's the harm in declaring:

typedef unsigned char * uint_8_P ;

By the way, a speed_optimized compile of Large_is_First_32() is merely:

20 mov ecx,dword ptr [esp+4] ; Oops ! a MS_CPP_7_1 bug !
24 shr ecx,18h
27 mov eax,ecx
29 shl eax,8
2C or eax,ecx
2E shl eax,8
31 or eax,ecx
33 shl eax,8
36 or eax,ecx

But, if it's working code you're looking for, try this:

typedef unsigned char * uint_8_P ;
__inline int Large_is_First_32 ( int X ) {
uint_8_P P = ( uint_8_P ) & X ;
return * P << 24 | P [ 1 ] << 16 | P [ 2 ] << 8 | P [ 3 ]; }
main(){
rv = Large_is_First_32( 0x84838281 );
}

But Large_is_First_32() just becomes:
mov dword ptr [rv (40451Ch)],81828384h ; Ha !

using int rv = 0x84838281 ; rv = Large_is_First_32( rv );
Large_is_First_32() becomes:

00 mov eax,dword ptr [rv (403010h)]
05 xor ecx,ecx
07 mov ch,al
09 movzx edx,ah
0C movzx eax,byte ptr [rv+2 (403012h)]
13 or ecx,edx
15 movzx edx,byte ptr [rv+3 (403013h)]
1C shl ecx,8
1F or ecx,eax
21 shl ecx,8
24 or ecx,edx
26 mov dword ptr [rv (403010h)],ecx

Re: Swap_32_Doubleswap, optimized, 14 lines: <<
pushl %ebp ; standard frame
movl %esp, %ebp ; crap
leal 8(%ebp), %edx ; (unsigned char *) &X
movzbl 3(%edx), %ecx ; get fourth byte
movzbl 8(%ebp), %eax ; get first byte
movb %al, 3(%edx) ; store first in fourth
movb %cl, 8(%ebp) ; store fourth in first
movzbl 2(%edx), %ecx ; get third byte
movzbl 1(%edx), %eax ; get second byte
movb %al, 2(%edx) ; store second in third
movb %cl, 1(%edx) ; store third in second
movl 8(%ebp), %eax ; fetch the result
popl %ebp ; ... and return it
ret >>

You asked me: <<
Which one's likely to be more efficient, at least on GCC-derived systems ?
Which one's more likely to actually function properly ? >>

My Large_is_First_32() is named better, for one thing, it works for another.
And, most important of all... it's Easier_To_Read !

Further, it's only 12 lines of assembly, including fetching and storing rv.

What's more... I found a bug in MS_CPP_7_1.
 
O

Olaf Baeyens

Hi Jeff, I actually felt sharp enough to try to reverse engineer your
original code. :)

So my big quest is..... is the code below your original? Or was there
another one? Since I cannot find the original message anymore.
int Swap_32 ( int X ) { uint_8_P P = ( uint_8_P ) & X + 4 ;
return * -- P << 24 | * -- P << 16 | * -- P << 8 | * -- P ; }

Or was it something like this:
int Swap_32 ( int X ) { uint_8_P P = ( uint_8_P ) & X ;
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ; }

I didn't notice the -- and the &X + 4 part because of my dyslexia. I thought
I saw ++. and &X

And the second question does this run on a Intel byte order or a Motorola
byte order?

So if this is you code, then be prepared.... put on your safety belts
because I have a lot of things to say. :)
I will reply when I am at home. I need some time consuming tests to do.

I do admit I cannot make it to work on the VC++ 2003 so far, but I am pretty
sure that a real VC++ 2003 programmer would not choose to follow that way of
programming. ;-)
 
J

Jeff_Relf

Hi Olaf, Are you looking for my original code, or just code that fails ?

The previous code I showed assumed that the smallest byte came in memory,
....which would only work if it were compiled/run on a bid_endian system
( converting .WAV files was the example I had in mind ).

My latest code assumes that the int's largest byte comes first in memory.
Below is my latest Working code.
rv must be a global or the optimizer barfs.

typedef unsigned char * uint_8_P ; int rv = 0x84838281 ;

__inline int Large_is_First_32 ( int X ) {
uint_8_P P = ( uint_8_P ) & X ;
return * P << 24 | P [ 1 ] << 16 | P [ 2 ] << 8 | P [ 3 ] ; }

int main(){
rv = Large_is_First_32( rv );
}

00 mov eax,dword ptr [rv (403010h)]
05 xor ecx,ecx
07 mov ch,al
09 movzx edx,ah
0C movzx eax,byte ptr [rv+2 (403012h)]
13 or ecx,edx
15 movzx edx,byte ptr [rv+3 (403013h)]
1C shl ecx,8
1F or ecx,eax
21 shl ecx,8
24 or ecx,edx
26 mov dword ptr [rv (403010h)],ecx

This code fails ( but shouldn't, ¿ a MS_CPP_7_1 bug ):

typedef unsigned char * uint_8_P ; int rv = 0x84838281 ;

__inline int Large_is_First_32 ( int X ) {
uint_8_P P = ( uint_8_P ) & X ;
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ; }

int main(){
rv = Large_is_First_32( rv );
}

Kelsey has said that, optimized or not, my multiple * ++ P's were illegal,
but I can't imagine where MicroSoft declares that,
....or when, exactly, multiple * ++ P's shouldn't work.

You concluded: << I do admit I cannot make it to work on the VC++ 2003 so far,
but I am pretty sure that a real VC++ 2003 programmer
would not choose to follow that way of programming. ;-) >>

I write code for a living and for fun... I'm real enough.

Althoug this doesn't work ( ¿ a MS_CPP_7_1 bug ):
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ;

I just Love how it looks !

This works, but doesn't look nearly as good:
return * P << 24 | P [ 1 ] << 16 | P [ 2 ] << 8 | P [ 3 ] ; }

I'm all about looks and readability... not speed.
( But it's damn fast too, you must admit )

P.S. I know you don't care, but I changed my LoopTo() again, it's now:

#define LoopTo( StopCond ) \
while ( Ch && ( Ch = ( uchar ) * ++ P ) \
&& ! ( Ch2 = ( uchar ) P [ 1 ], ( StopCond ) ) )

Note that Ch2 is now undefined when Ch becomes null.
LoopTo() is used in HTM_TXT.CPP

Pass HTM_TXT.EXE a .HTML file and it spits out a .TXT file.
http://www.Cotse.NET/users/jeffrelf/HTM_TXT.EXE
http://www.Cotse.NET/users/jeffrelf/HTM_TXT.CPP
http://www.Cotse.NET/users/jeffrelf/HTM_TXT.VCPROJ

These are valid HTML tags: <! Comment --> <Alpha> </Alpha>
But, due to the leading space, < Alpha> is not.
Things like &Unknown are sent through untranslated, for obvious reasons.
 
T

Tim Smith

[unreadable crap...I'll try to salvage what I can from his poor layout]

The code in question:
int Swap_32_x86 ( int X ) { uint_8_P P = ( uint_8_P ) & X ;
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ; }

Why it doesn't work:
Relf is not convinced:
I don't know what you might be asserting here, if anything,
but I hope you have a lot of credible documents to support you.

Read this:

<http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/
html/_clang_c_sequence_points.asp>

In C and C++, precedence and associativity basically determine the shape
of the parse tree. They do *NOT* determine the order of evaluation of
things, other than indirectly (in that an operation can not be done
until the value of both operands is known).

For example, consider a()+b()*c(). The compile can call those three
functions in any order it wants. It will have to then do the
multiplication before it does the addition.
 
H

Hurray for Peter Pumpkinhead

Tim said:
For example, consider a()+b()*c(). The compile can call those three
functions in any order it wants. It will have to then do the
multiplication before it does the addition.

What if a static global is set in a() that needs to be called in b(),
c() to get a correct calculation?
 
B

Bill

Jeff_Relf said:
return * P << 24 | * ++ P << 16 | * ++ P << 8 | * ++ P ;
}

1. &&,|| : Are short circuited
: have sequence points between operands
&,| : Are NOT short circuited
: DO NOT have sequence points between operands
2. From the ANSI/ISO C Standard 9899-1990,
section 6.3 "Expressions", paragraph 2:
"Between the previous and next sequence point an object shall
have its stored value modified at most once by the
evaluation of an expression. Furthermore, the prior value
shall be accessed only to determine the value to be stored."
3. Modifying a value more than once between sequence points is
undefined.
4. A compiler can do ANYTHING IT WANTS when behavior is undefined
5. You modify P more than once between sequence points
6. This is NOT a compiler bug
This is programmer error

Information on these topocs is easily available from GOOGLE

Hope this helps
Bill
 

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

Top