Comments please

  • Thread starter Christopher Benson-Manica
  • Start date
M

Martin Dickopp

Sean Kenwrick said:
Sean Kenwrick said:
Martin Dickopp said:
Christopher Benson-Manica wrote:
This function is intended to print a string guaranteed to be
in the form MM/DD/YYYY (assume <stdio.h> and <string.h> are
included) as YYYY-MM-DD:

void printdate( const char *date )
{
char tdate[10], *cp;
int mod=10, i;

cp=tdate;
for( i=6 ; i%mod != 5 ; ++i, ++cp ) {
if( !(i%mod) )
*(cp++)='-';
*cp=date[i%mod];
}
*cp=0;
*strchr( tdate, '/' )='-';
printf( "New date is %s\n", tdate );
}

/*
This function is intended to print a string guaranteed to be in the
form MM/DD/YYYY (assume <stdio.h> and <string.h> are included) as
YYYY-MM-DD:
*/
void printdate(const char *date)
{
printf( "New date is %s-%.2s-%.2s\n", strrchr(date, '/') + 1, date,
strchr(date, '/') + 1);
}

Jeremy.

I like this - very clever - but it relies on 'C' calling convention of
passing arguments to functions in reverse order (but so what).

There is no such convention in C, and the code doesn't make the assumption
that there is.

Doh! Of course your right, I missed the fact that the first call was to
strrchr() rather than strchr() and so I couldn;t quite figure out the logic
until my brain suggested that the calls must be going in the reverse order
(which still wouldn't work (damn this stupid brain!)).

The order in which the `strrchr' and `strchr' calls happen is unspecified,
but that doesn't matter, since neither function has any side effects.
Actually, I just thought about this again and realised I was right the first
time about the C calling convention. Check out this code:


#include <stdio.h>

void myfunc(int, int);

int main(){
int i=0;
myfunc(++i,i+=8);
}

void myfunc(int a,int b){
printf("a=%d b=%d\n",a,b);
}

Do you think it will print out a=1, b=9 or a=9, b=8 ???

I think it will print nothing, but it will make daemons fly out of my
nose. ;)
Its the latter,

No, it's not. `++i' and `i+=8' both modify the same object `i', and since
there is no intervening sequence point, the program invokes undefined
behavior. Virtually /anything/ can happen.
(although I agree his solution didn't rely on this calling convention)

The only "calling convention" the C language has is that the arguments to
a function call are evaluated in unspecified order, and that there is
sequence point immediately before the function is called.

Martin
 
B

Barry Schwarz

This function is intended to print a string guaranteed to be in the
form MM/DD/YYYY (assume <stdio.h> and <string.h> are included) as
YYYY-MM-DD:

void printdate( const char *date )
{
char tdate[10], *cp;

If tdate is to contain a string (as indicated by the printf below) and
if that string is to have a length of 10 (as indicated by the function
specification in the first paragraph above), the tdate needs to be an
array of at least 11 characters.
int mod=10, i;

cp=tdate;
for( i=6 ; i%mod != 5 ; ++i, ++cp ) {

Will i%mod != 5 ever be different than i != 15? Why spend the time on
a division?
if( !(i%mod) )

Will (!(i%mod)) ever be different than (i==10)? Why another division?
*(cp++)='-';
*cp=date[i%mod];
}
*cp=0;

Undefined behavior as cp now points beyond the end of tdate.
*strchr( tdate, '/' )='-';
printf( "New date is %s\n", tdate );

Undefined behavior as tdate does not contain a properly terminated
string.



<<Remove the del for email>>
 
C

Christopher Benson-Manica

Sean Kenwrick said:
void printdate(char *date)
{
printf( "New date is %s-%s-%s\n",
strtok(NULL,"/"),strtok(NULL,"/")-3,strtok(date,"/")+3);
}
try it, it works!

Okay...

$ cat t.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void printdate(char *date)
{
printf( "New date is %s-%s-%s\n",
strtok(NULL,"/"),strtok(NULL,"/")-3,strtok(date,"/")+3);
}

int main(void)
{
char str[256];
strcpy( str, "09/12/2003" ); /* strtok modifies its argument! */
printdate( str );
return 0;
}

$ gcc -O2 -Wall -ansi -pedantic t.c
$ ./a.out
Segmentation fault (core dumped)

Uh oh. Looks like undefined behavior to me...
 
C

Christopher Benson-Manica

Barry Schwarz said:
If tdate is to contain a string (as indicated by the printf below) and
if that string is to have a length of 10 (as indicated by the function
specification in the first paragraph above), the tdate needs to be an
array of at least 11 characters.

Yes, I am now aware of that blunder. I seek forgiveness.
Will i%mod != 5 ever be different than i != 15? Why spend the time on
a division?
Will (!(i%mod)) ever be different than (i==10)? Why another division?

Because division is fun? *sigh*
Undefined behavior as cp now points beyond the end of tdate.
Undefined behavior as tdate does not contain a properly terminated
string.

Because undefined behavior is fun? *sigh*
 
S

Sean Kenwrick

Christopher Benson-Manica said:
Sean Kenwrick said:
void printdate(char *date)
{
printf( "New date is %s-%s-%s\n",
strtok(NULL,"/"),strtok(NULL,"/")-3,strtok(date,"/")+3);
}
try it, it works!

Okay...

$ cat t.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void printdate(char *date)
{
printf( "New date is %s-%s-%s\n",
strtok(NULL,"/"),strtok(NULL,"/")-3,strtok(date,"/")+3);
}

int main(void)
{
char str[256];
strcpy( str, "09/12/2003" ); /* strtok modifies its argument! */
printdate( str );
return 0;
}

$ gcc -O2 -Wall -ansi -pedantic t.c
$ ./a.out
Segmentation fault (core dumped)

Uh oh. Looks like undefined behavior to me...

Just out of curiosity - what happens if you turn off optimisation? I
suspect that this is causing the parameters to be passed through registers,
not on the stack.

I know that the standard does not define the order of parameter passing,
but I would expect the default behaviour of compilers to usually pass
parameters in reverse oder onto the stack so that variable length parameter
lists can be more easily acheived. Obviously you shouldn't ever rely on
this behavior - its just a curious side effect of compiler architecture
which I attemted to exploit in the code above....

Let me know if gcc still fails without the optimisation....

Sean
 
D

Dan Pop

In said:
void printdate(char *date)
{
printf( "New date is %s-%s-%s\n",
strtok(NULL,"/"),strtok(NULL,"/")-3,strtok(date,"/")+3);
}

Broken code: the order in which the three strtok calls are executed is
unspecified.

It's also pointless: you don't need null-terminated strings in order to
use %s, as Jeremy's example shows: you can control how many characters
are output in the absence of a null terminator.

Dan
 
C

Christopher Benson-Manica

Broken code: the order in which the three strtok calls are executed is
unspecified.

*I* know it's broken - if you look closely, I was responding to a
post where someone claimed that it worked.
 
C

Christopher Benson-Manica

Sean Kenwrick said:
Just out of curiosity - what happens if you turn off optimisation? I
suspect that this is causing the parameters to be passed through registers,
not on the stack.

$ /usr/bin/gcc -Wall -ansi -pedantic t.c
$ ./a.out
Segmentation fault (core dumped)

It's caused by code relying on specific unspecified behavior. The
implementation need not even *have* a stack :)

The default behavior here is whatever was convenient to implement on
an Alpha NetBSD system.
 
S

Sean Kenwrick

Christopher Benson-Manica said:
$ /usr/bin/gcc -Wall -ansi -pedantic t.c
$ ./a.out
Segmentation fault (core dumped)

It's caused by code relying on specific unspecified behavior. The
implementation need not even *have* a stack :)

The default behavior here is whatever was convenient to implement on
an Alpha NetBSD system.
That's very interesting. I wonder what mechanism it is using to pass a
variable numbers of parameters to functions like printf(). I realised
before your post that the program would still fail even with optimisation
switched off. This is because printf() has already been compiled and the
decision about how it will get it's parameters is already built into to it's
binary image.. I don't suppose its worth the effort to use the debugger to
check how it is doing it....... unless your curious too...

Sean
 
M

Michael Wojcik

That's very interesting. I wonder what mechanism it is using to pass a
variable numbers of parameters to functions like printf().

There are many possibilities. An implementation could use "frames"
allocated from a heap, for example, where each frame contains parameter
values and metadata such as the number of parameters and the size of
each; the address of the current invocation frame might be passed to
the called function in a register (the previous contents of which would
also be saved in the frame). That's similar to a calling convention
often used on IBM mainframes.

Or an implementation might pass the number of parameters as another
hidden parameter.

And so on.
I realised
before your post that the program would still fail even with optimisation
switched off. This is because printf() has already been compiled and the
decision about how it will get it's parameters is already built into to it's
binary image.

That's not necessarily true either. The implementation might be able
to deal with different calling conventions.

In any case, the manner in which parameters are passed doesn't
determine the order in which they're evaluated. It's entirely
possible that in some implementation optimization switches change the
order of parameter evaluation.

In short, as a C programmer *you know nothing about how parameters are
passed*. As a user of a particular C implementation you may have some
special knowledge, but within C itself you do not.

Back in the early 1990s I used three different C implementations on
the AS/400: EPM C, System C, and ILE C. They were all at least close
to conforming to C90 (the first two might not have been fully
conforming; I don't recall). None of them passed parameters in
anything like the way you'd expect. And calling local functions (ie
those defined in the same translation unit) was a *very* different
process - for the implementation - than calling external functions
(though the syntax was the same for both, of course).

And pointers were 16 bytes long, and calling a function that had a
return type that was any size other than sizeof(int) without a
correct prototype in scope aborted the program.

Many C programmers could stand some experience with that sort of
implementation.
 
P

Peter Shaggy Haywood

Groovy hepcat Arthur J. O'Dwyer was jivin' on Mon, 19 Jan 2004
17:45:51 -0500 (EST) in comp.lang.c.
Re: Comments please's a cool scene! Dig it!
This function is intended to print a string guaranteed to be in the
form MM/DD/YYYY (assume <stdio.h> and <string.h> are included) as
YYYY-MM-DD:

Jeremy Yallop has hit the nail on the head w.r.t. the unnecessary
complexity of this code. However, I'd just like to point out...
void printdate( const char *date )
{
char tdate[10], *cp;
int mod=10, i;

cp=tdate;
for( i=6 ; i%mod != 5 ; ++i, ++cp ) {

...that you've given a weird name to the obviously-correct magic
number (10, as in base-10), but kept in the "arbitrary" magic numbers

I don't think the 10 has anithing to do with base 10. I think it has
something to do with the length of the input string. (Oh, you said
that below. Didn't see that at first.) But, of course, that's what he
gets for writing such weird code. :)
6 and 5, of which I can't even tell what they're supposed to be.

The positions of the data to be extracted from the string, maybe.
[And in fact, you're *not* using the 10 to mean base-10, as I
assumed upon seeing you mod'ing things by it. It's a hard-coded
string length, I think. That's just silly.]

Agreed.

--

Dig the even newer still, yet more improved, sig!

http://alphalink.com.au/~phaywood/
"Ain't I'm a dog?" - Ronny Self, Ain't I'm a Dog, written by G. Sherry & W. Walker.
I know it's not "technically correct" English; but since when was rock & roll "technically correct"?
 
P

Peter Shaggy Haywood

Groovy hepcat Christopher Benson-Manica was jivin' on Mon, 19 Jan 2004
22:19:31 +0000 (UTC) in comp.lang.c.
Comments please's a cool scene! Dig it!
This function is intended to print a string guaranteed to be in the
form MM/DD/YYYY (assume <stdio.h> and <string.h> are included) as
YYYY-MM-DD:

void printdate( const char *date )
{
char tdate[10], *cp;
int mod=10, i;

cp=tdate;
for( i=6 ; i%mod != 5 ; ++i, ++cp ) {
if( !(i%mod) )
*(cp++)='-';
*cp=date[i%mod];
}
*cp=0;
*strchr( tdate, '/' )='-';
printf( "New date is %s\n", tdate );
}

Ye gads! Thinking of entering the IOCCC are we? :)

--

Dig the even newer still, yet more improved, sig!

http://alphalink.com.au/~phaywood/
"Ain't I'm a dog?" - Ronny Self, Ain't I'm a Dog, written by G. Sherry & W. Walker.
I know it's not "technically correct" English; but since when was rock & roll "technically correct"?
 
S

Sean Kenwrick

Peter "Shaggy" Haywood said:
Groovy hepcat Arthur J. O'Dwyer was jivin' on Mon, 19 Jan 2004
17:45:51 -0500 (EST) in comp.lang.c.
Re: Comments please's a cool scene! Dig it!
This function is intended to print a string guaranteed to be in the
form MM/DD/YYYY (assume <stdio.h> and <string.h> are included) as
YYYY-MM-DD:

Jeremy Yallop has hit the nail on the head w.r.t. the unnecessary
complexity of this code. However, I'd just like to point out...
void printdate( const char *date )
{
char tdate[10], *cp;
int mod=10, i;

cp=tdate;
for( i=6 ; i%mod != 5 ; ++i, ++cp ) {

...that you've given a weird name to the obviously-correct magic
number (10, as in base-10), but kept in the "arbitrary" magic numbers

I don't think the 10 has anithing to do with base 10. I think it has
something to do with the length of the input string. (Oh, you said
that below. Didn't see that at first.) But, of course, that's what he
gets for writing such weird code. :)
6 and 5, of which I can't even tell what they're supposed to be.

The positions of the data to be extracted from the string, maybe.
[And in fact, you're *not* using the 10 to mean base-10, as I
assumed upon seeing you mod'ing things by it. It's a hard-coded
string length, I think. That's just silly.]

Agreed.
The code relies on the fact that if you start at the first Y of YYYY and
wrap around tpo the beginning you will end up with the string in the correct
order (hence the modulo arithmetic), the only job then is to convert the
'/'s into '-'s as you go along...

Sean
 
P

Peter Pichler

Martin Dickopp said:
The only "calling convention" the C language has is that the arguments to
a function call are evaluated in unspecified order, and that there is
sequence point immediately before the function is called.

Isn't there a sequence point after evaluating each argument?

Annex C (informative)
Sequence points
1 The following are the sequence points described in 5.1.2.3:
- The call to a function, after the arguments have been evaluated (6.5.2.2).
....
- The end of a full expression: an initializer (6.7.8); the expression in an
expression statement (6.8.3); the controlling expression of a selection
statement (if or switch) (6.8.4); the controlling expression of a while or
do statement (6.8.5); each of the expressions of a for statement (6.8.5.3);
the expression in a return statement (6.8.6.4).
....

In my naive interpretation, each argument is a full expression (an
initializer?).
Hence,

#include <stdio.h>

int main (void)
{
int foo = 42;
printf("%d %d\n", foo++, foo++); /* 4 sequence points here? */
return 0;
}

produces indeteminate results, but not an udefined behaviour.
Where am I going wrong?

Peter
 
P

Peter Nilsson

Peter Pichler said:
Isn't there a sequence point after evaluating each argument?

Annex C (informative)
Sequence points
1 The following are the sequence points described in 5.1.2.3:
- The call to a function, after the arguments have been evaluated
(6.5.2.2).

It means, after _all_ the arguments have been evaluated.
...
- The end of a full expression: an initializer (6.7.8); the expression in an
expression statement (6.8.3); the controlling expression of a selection
statement (if or switch) (6.8.4); the controlling expression of a while or
do statement (6.8.5); each of the expressions of a for statement (6.8.5.3);
the expression in a return statement (6.8.6.4).
...

In my naive interpretation, each argument is a full expression (an
initializer?).
Hence,

#include <stdio.h>

int main (void)
{
int foo = 42;
printf("%d %d\n", foo++, foo++); /* 4 sequence points here? */
return 0;
}

produces indeteminate results, but not an udefined behaviour.
Where am I going wrong?

Function calls are postfix expressions and parameter passing is done via
assigment (6.5.16), not via declarations (6.7).

6.5.2.2p3 and p4:

A postfix expression followed by parentheses () containing a
possibly empty, commaseparated list of expressions is a function
call. The postfix expression denotes the called function. The list
of expressions specifies the arguments to the function.

An argument may be an expression of any object type. In preparing
for the call to a function, the arguments are evaluated, and each
parameter is assigned the value of the corresponding argument.

6.5.2.2p10:

The order of evaluation of the function designator, the actual
arguments, and subexpressions within the actual arguments is
unspecified, but there is a sequence point before the actual call.
 
P

Peter Pichler

Peter Nilsson said:
(6.5.2.2).

It means, after _all_ the arguments have been evaluated.

I know, I only left that part in to give some context. I didn't want to snip
*everything*.
Function calls are postfix expressions and parameter passing is done via
assigment (6.5.16), not via declarations (6.7).

Thanks, that clarifies it.
6.5.2.2p3 and p4:

A postfix expression followed by parentheses () containing a
possibly empty, commaseparated list of expressions is a function
call. The postfix expression denotes the called function. The list
of expressions specifies the arguments to the function.

An argument may be an expression of any object type. In preparing
for the call to a function, the arguments are evaluated, and each
parameter is assigned the value of the corresponding argument.

6.5.2.2p10:

The order of evaluation of the function designator, the actual
arguments, and subexpressions within the actual arguments is
unspecified, but there is a sequence point before the actual call.

Yes, I have read the two paragraphs above, but only comprehended them
*after* I posted. As Dan would say, I should have engaged my brain first ;-)

Peter
 

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,777
Messages
2,569,604
Members
45,229
Latest member
GloryAngul

Latest Threads

Top