Is this kosher?

M

MikeC

Folks,

I just wrote a little function to find the end of a line:

char *eol(char *pt)
{ while(*pt != '\r' && *pt != '\n' && *pt != '\0') // run along to the line
terminator character
pt++;
return pt; // and return a pointer to it
}

I use it thus:

main() // I know, I know!
{ char text_line[260], *t_pt;

/* [ get a file path into text_line] */

t_pt = eol(text_line);
*t_pt = '\0'; // this knocks off a '\n', which stops chdir() from
working

chdir(text_line);
}

This works fine, but just for the experiment, I replaced the two lines
containing t_pt with

*eol(text_line) = '\0';

.... and it worked. Is this a syntactical thing to do? Is it portable? I'd
appreciate your educated comment on this.

Thanks a heap,

MikeC.


--
Mental decryption required to bamboozle spam robots:

mike_best$ntlworld*com
$ = @
* = dot
 
R

Richard Heathfield

MikeC said:

This works fine, but just for the experiment, I replaced the two lines
containing t_pt with

*eol(text_line) = '\0';

... and it worked. Is this a syntactical thing to do? Is it portable?

Yes, it's fine, provided you are absolutely 100% certain that the pointer
that eol() returns is guaranteed to be to a writeable character. In your
case, it is, provided that you only give writeable strings to eol().

Only give genuine null-terminated strings to eol() - an array of char
doesn't /necessarily/ contain a string: char foo[3] = "foo"; being an
example of one that does not. And if you plan to do your *eol() thing,
don't give eol() a string literal to feed on:

*eol("oh deary deary me\n") = '\0'; /* asking for trouble */

char *p = "oh deary deary me\n";
*eol(p) = '\0'; /* still asking for the same trouble */

char arr[] = "but this is okay\n");
*eol(arr) = '\0'; /* no sweat */

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: normal service will be restored as soon as possible. Please do not
adjust your email clients.
 
M

Malcolm

MikeC said:
Folks,

I just wrote a little function to find the end of a line:

char *eol(char *pt)
{ while(*pt != '\r' && *pt != '\n' && *pt != '\0') // run along to the
line terminator character
pt++;
return pt; // and return a pointer to it
}

I use it thus:

main() // I know, I know!
{ char text_line[260], *t_pt;

/* [ get a file path into text_line] */

t_pt = eol(text_line);
*t_pt = '\0'; // this knocks off a '\n', which stops chdir() from
working

chdir(text_line);
}

This works fine, but just for the experiment, I replaced the two lines
containing t_pt with

*eol(text_line) = '\0';

... and it worked. Is this a syntactical thing to do? Is it portable?
I'd appreciate your educated comment on this.
I'd like to see

char *end = eol(text|_line);

*end = 0;

simply because it looks a bit funny to dereference the function return value
directly.

The function itself is unexceptional C. There is an issue about returning
pointers to input parameters, but it is problem deep and inherent in the C
language itself, and there is no point avoiding it if you choose to use C at
all.
 
R

Richard Heathfield

Malcolm said:
MikeC said:
Folks,

I just wrote a little function to find the end of a line:

char *eol(char *pt)
{ while(*pt != '\r' && *pt != '\n' && *pt != '\0') // run along to the
line terminator character
pt++;
return pt; // and return a pointer to it
}

I use it thus:

main() // I know, I know!
{ char text_line[260], *t_pt;

/* [ get a file path into text_line] */

t_pt = eol(text_line);
*t_pt = '\0'; // this knocks off a '\n', which stops chdir() from
working

chdir(text_line);
}

This works fine, but just for the experiment, I replaced the two lines
containing t_pt with

*eol(text_line) = '\0';

... and it worked. Is this a syntactical thing to do? Is it portable?
I'd appreciate your educated comment on this.
I'd like to see

char *end = eol(text|_line);

Why |?
*end = 0;

simply because it looks a bit funny to dereference the function return
value directly.

To you, maybe. It's quite common, though, in some people's code, and it's an
idiom worth being aware of, albeit a minor one.

The function itself is unexceptional C. There is an issue about returning
pointers to input parameters,

He isn't doing that, though.
but it is problem deep and inherent in the C
language itself,

No, not really. Parameters are local objects which disappear when the
function that they parameterise has completed execution. That isn't a
problem, any more than it's a problem that soup disappears from your plate
when you lift a spoonful to your mouth. It's just the way the world works,
that's all. And there's no point in returning a pointer to something that
no longer exists. But he isn't returning a pointer to an input parameter.
He's returning a pointer to a character that he tracked down using an input
parameter as a starting point. Not the same thing at all.
and there is no point avoiding it if you choose to use C at all.

No point avoiding what? He isn't doing what you claim he's doing.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: normal service will be restored as soon as possible. Please do not
adjust your email clients.
 
E

Eric Sosman

MikeC wrote On 11/10/06 17:01,:
Folks,

I just wrote a little function to find the end of a line:

char *eol(char *pt)
{ while(*pt != '\r' && *pt != '\n' && *pt != '\0') // run along to the line
terminator character
pt++;
return pt; // and return a pointer to it
}

I use it thus:

main() // I know, I know!

Interesting that the disclaimer takes nineteen or more
keystrokes, whereas inserting both `int ' and `void' would
take eight, and `int ' alone only four. Perversity for
perversity's sake?
{ char text_line[260], *t_pt;

/* [ get a file path into text_line] */

t_pt = eol(text_line);
*t_pt = '\0'; // this knocks off a '\n', which stops chdir() from
working

chdir(text_line);
}

This works fine, but just for the experiment, I replaced the two lines
containing t_pt with

*eol(text_line) = '\0';

... and it worked. Is this a syntactical thing to do? Is it portable? I'd
appreciate your educated comment on this.

As others have noted, it's fine. But in the spirit of
the Seventh Commandment, let me point out that the Standard
library already has a function that makes eol() unnecessary:

text_line[ strcspn(text_line, "\r\n") ] = '\0';
 
M

Malcolm

Richard Heathfield said:
Malcolm said:
MikeC said:
Folks,

I just wrote a little function to find the end of a line:

char *eol(char *pt)
{ while(*pt != '\r' && *pt != '\n' && *pt != '\0') // run along to the
line terminator character
pt++;
return pt; // and return a pointer to it
}

I use it thus:

main() // I know, I know!
{ char text_line[260], *t_pt;

/* [ get a file path into text_line] */

t_pt = eol(text_line);
*t_pt = '\0'; // this knocks off a '\n', which stops chdir() from
working

chdir(text_line);
}

This works fine, but just for the experiment, I replaced the two lines
containing t_pt with

*eol(text_line) = '\0';

... and it worked. Is this a syntactical thing to do? Is it portable?
I'd appreciate your educated comment on this.
I'd like to see

char *end = eol(text|_line);

Why |?
*end = 0;

simply because it looks a bit funny to dereference the function return
value directly.

To you, maybe. It's quite common, though, in some people's code, and it's
an
idiom worth being aware of, albeit a minor one.

The function itself is unexceptional C. There is an issue about returning
pointers to input parameters,

He isn't doing that, though.
but it is problem deep and inherent in the C
language itself,

No, not really. Parameters are local objects which disappear when the
function that they parameterise has completed execution. That isn't a
problem, any more than it's a problem that soup disappears from your plate
when you lift a spoonful to your mouth. It's just the way the world works,
that's all. And there's no point in returning a pointer to something that
no longer exists. But he isn't returning a pointer to an input parameter.
He's returning a pointer to a character that he tracked down using an
input
parameter as a starting point. Not the same thing at all.
OK, substitute aonther term to refer to the data accessible to the function
by reason of its pointer parameters. "Argument" won't do since that refers
to the parameters as they exist in the caller.
No point avoiding what? He isn't doing what you claim he's doing.
char *makeanalias(char *str)
{
return str;
}

Causes all sorts of subtle programming problems. There is a strong case for
designing a language which avoids it. However that language isn't C.
 
R

Richard Heathfield

Malcolm said:

char *makeanalias(char *str)
{
return str;
}

Causes all sorts of subtle programming problems.

No more so than:

char *p = q;

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: normal service will be restored as soon as possible. Please do not
adjust your email clients.
 
M

Malcolm

Richard Heathfield said:
Malcolm said:



No more so than:

char *p = q;
The difference is that
char *p = q

as it stands, simply creates a named alias. A decent compiler will optimise
it to a no-op.

However real code is unlikely to leave things at that - since q is already
in scope, why not simply replace p by q? p will be incremented, or set to a
different value on an conditional. So you've got a potentially nasty
situation.

When you further start messing about with the scope of the variables, you
soon get into a potential huge mess. In fact you could say that the code
become garbage which needs collecting.
 
R

Richard Heathfield

Malcolm said:
The difference is that
char *p = q

as it stands, simply creates a named alias. A decent compiler will
optimise it to a no-op.

char *p = makeanalias(q); is no different. It assigns the value of q to p.
If that were all there is to it, a decent compiler will, as you say,
optimise it to a no-op. But of course that isn't all there is to it.
However real code is unlikely to leave things at that - since q is already
in scope, why not simply replace p by q? p will be incremented, or set to
a different value on an conditional. So you've got a potentially nasty
situation.

No, not really. You've just got an ordinary pointer assignment. I see no
reason to struggle with this. C programmers do it all the time. Witness
some code taken at random from my source base:

unsigned char *Iterator = wnn_String->Start + wnn_Start;
unsigned char *Stop = wnn_String->Start;
if(wnn_Stop > len)
{
wnn_Stop = len;
}
Stop += wnn_Stop;
while(Iterator < Stop)
{
*Iterator = toupper(*Iterator);
++Iterator;
}

Bog-standard pointer manipulation. Really, I'm struggling to see why this
would be difficult to follow or maintain.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: normal service will be restored as soon as possible. Please do not
adjust your email clients.
 
W

William Hughes

Malcolm said:
The difference is that
char *p = q

as it stands, simply creates a named alias. A decent compiler will optimise
it to a no-op.

I don't follow. Before the assignment p is indeterminate. After the
assignment p is equal in value to q. So the operation is not a no_op.
Also, if you increment p you do not increment q.

Do you mean something like

char q[1] = 'a'
char *p;

p = q;
*q = 'b';
*p = *q;

The last is truly a no_op, and if you increment *q, you also increment
*p.

I don't see what this has to do with function calls.

- William Hughes
 
R

Richard Heathfield

William Hughes said:
I don't follow. Before the assignment p is indeterminate. After the
assignment p is equal in value to q. So the operation is not a no_op.

When I was replying to the same thing, I presumed he meant that it was
semantically equivalent to a no-op if p's value is not used thereafter.
What that has to do with the price of cheese is, however, unclear.
I don't see what this has to do with function calls.

Nor I. And I certainly don't see why it's bad for a function to return a
pointer whose value is based, however loosely, on a pointer value that was
handed to it as a parameter.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: normal service will be restored as soon as possible. Please do not
adjust your email clients.
 
C

CBFalconer

Richard said:
.... snip ...

Nor I. And I certainly don't see why it's bad for a function to
return a pointer whose value is based, however loosely, on a
pointer value that was handed to it as a parameter.

For one, consider a hypothetical revstring function:

char *revstring(char *s) {
/* code to reverse the string in place */
return s;
}

Now there is an unholy temptation to use it in a printf function:

printf("orig: %s\nrev: %s\n", str, revstring(s));

which will output two identical strings (whether or not reversed is
unknown). If the prototype is:

size_t revstring(char *s);

returning, say, the string length, there is no such temptation.
 
M

Malcolm

William Hughes said:
The difference is that
char *p = q

as it stands, simply creates a named alias. A decent compiler will
optimise
it to a no-op.

I don't follow. Before the assignment p is indeterminate. After the
assignment p is equal in value to q. So the operation is not a no_op.
Also, if you increment p you do not increment q.

Do you mean something like

char q[1] = 'a'
char *p;

p = q;
*q = 'b';
*p = *q;

The last is truly a no_op, and if you increment *q, you also increment
*p.

I don't see what this has to do with function calls.

- William Hughes

char q[1024];

gets(q); /* troll alert *

if(q[0] = '*')
{
char *p = q;

myasteriskroutine(p);
}

This is legitimate code, if p has some meaning for an asterisk-started
string. It makes the code easier to read.

However a decent compiler will say q, p - same value. Therefore we just
store them in the same location in memory.
 
F

Flash Gordon

CBFalconer said:
Richard Heathfield wrote:
... snip ...

For one, consider a hypothetical revstring function:

char *revstring(char *s) {
/* code to reverse the string in place */
return s;
}

Now there is an unholy temptation to use it in a printf function:

printf("orig: %s\nrev: %s\n", str, revstring(s));

which will output two identical strings (whether or not reversed is
unknown).

Surely it is guaranteed that if revstring does what the name suggest the
printed strings will be reversed? After all, which ever order the
parameters are evaluated in there is a sequence point between their
evaluation and printf being executed and also one on return from
revstring so there are two reasons why revstring must have completed
before the string is printed.
> If the prototype is:

size_t revstring(char *s);

returning, say, the string length, there is no such temptation.

I agree.
 
C

CBFalconer

Flash said:
Surely it is guaranteed that if revstring does what the name suggest
the printed strings will be reversed? After all, which ever order
the parameters are evaluated in there is a sequence point between
their evaluation and printf being executed and also one on return
from revstring so there are two reasons why revstring must have
completed before the string is printed.

Yes, but what are the actual parameters sent to printf? I concede
that the two outputs (in this case) are probably of the reversed
string. At any rate, the output is not that desired. The purpose
was to illustrate the trap involved in returning the input string.
 
W

William Hughes

Malcolm said:
William Hughes said:
Malcolm said:

<snip>

char *makeanalias(char *str)
{
return str;
}

Causes all sorts of subtle programming problems.

No more so than:

char *p = q;

The difference is that
char *p = q

as it stands, simply creates a named alias. A decent compiler will
optimise
it to a no-op.

I don't follow. Before the assignment p is indeterminate. After the
assignment p is equal in value to q. So the operation is not a no_op.
Also, if you increment p you do not increment q.

Do you mean something like

char q[1] = 'a'
char *p;

p = q;
*q = 'b';
*p = *q;

The last is truly a no_op, and if you increment *q, you also increment
*p.

I don't see what this has to do with function calls.

- William Hughes

char q[1024];

gets(q); /* troll alert *

if(q[0] = '*')
{
char *p = q;

At this point p has the same value as q. If at any time the
complier can determine that this is true, it is free to use
the value of q rather than the value of p. If the compiler
can determine that this will always be true then the
compiler can ignore p and use q whenever it
sees p.

However, if the value of p or q can change (e.g.
by incrementng one of them) the compiler cannot and
must not make this optimization.

So there is no trap for the programmer or maintainer.
If the code is changed so that the values of p and
q are no longer the same, the optimization will no
longer be done. In particular, if the code is changed
so that p is incremented, q will not be incremented.
myasteriskroutine(p);
}

This is legitimate code, if p has some meaning for an asterisk-started
string. It makes the code easier to read.

However a decent compiler will say q, p - same value. Therefore we just
store them in the same location in memory.

Yes, but this is just using the as if rule. The compiler can only do
this if there is no way to determine that it has done this.

- William Hughes
 
W

William Hughes

CBFalconer said:
For one, consider a hypothetical revstring function:

char *revstring(char *s) {
/* code to reverse the string in place */
return s;
}

Now there is an unholy temptation to use it in a printf function:

printf("orig: %s\nrev: %s\n", str, revstring(s));

which will output two identical strings (whether or not reversed is
unknown). If the prototype is:

size_t revstring(char *s);

returning, say, the string length, there is no such temptation.

Yes, but all you are saying is that a function of the form

do_something_to_object(object)

should not return an object (in C an object is a pointer).

Enforcing this absolutely mean that a new idiom is needed for

new_object = copy_object(object)

It is not clear that this is even "good style". Yes it avoids
the ambiguity of

print obj, do_something_to_object(object)

but it also disallows

print make_object_printer_friendly(object)


- William Hughes
 
R

Richard Heathfield

CBFalconer said:
For one, consider a hypothetical revstring function:

char *revstring(char *s) {
/* code to reverse the string in place */
return s;
}

Now there is an unholy temptation to use it in a printf function:

printf("orig: %s\nrev: %s\n", str, revstring(s));

which will output two identical strings (whether or not reversed is
unknown).

Well, it would if you'd said revstring(str)! :) Seriously, though, it's
true that making this particular mistake is a *possibility*, but it isn't
one that I would consider all that significant. After all, I would only
have to run the program once and examine its output to realise I've done
something stupid that needs fixing, and it's not as if it would take a long
time to fix, either:

printf("orig: %s\n", str);
revstring(str);
printf("rev: %s\n", str);

Just because it's possible to misuse a technique, that doesn't make the
technique bad per se.

--
Richard Heathfield
"Usenet is a strange place" - dmr 29/7/1999
http://www.cpax.org.uk
email: normal service will be restored as soon as possible. Please do not
adjust your email clients.
 
F

Flash Gordon

CBFalconer said:
Yes, but what are the actual parameters sent to printf?

For both what is passed is a pointer to a string, not the string itself.
> I concede
that the two outputs (in this case) are probably of the reversed
string.

They are guaranteed to be the reversed string for the reasons given
above. No probably about it.
> At any rate, the output is not that desired. The purpose
was to illustrate the trap involved in returning the input string.

That I agree with :)
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top