A function returning string or pointer

S

svata

Hello to all,

as a result from my previous post I'm busy with splitting code into
functions.
The one problem ( out of many ) I encounter is how to properly
use/code a function which returns either array of characters(string) or
a pointer to this array.

I read some articles, some other posts and come to this solution:


char *read_name(void){
static char item_name[11];
char *p_item_name;

printf("Enter the description: ");
if (fgets(item_name, sizeof(item_name), stdin) != NULL){
/* if the input contains a new line */
if (( p_item_name = strchr(item_name, '\n')) != NULL ){
*p_item_name = '\0'; /* get rid of new line */
}
else {
while(getchar() != '\n'){ /* get rid of the rest in the buffer */
;
}
}
}
return item_name; /* return string in the form of character array */
}

Are there other solutions? What are the pros and cons using
array/pointer?

svata
 
L

loic-dev

Hello Svata,
as a result from my previous post I'm busy with splitting code into
functions.
The one problem ( out of many ) I encounter is how to properly
use/code a function which returns either array of characters(string) or
a pointer to this array.

I read some articles, some other posts and come to this solution:

Are there other solutions? What are the pros and cons using
array/pointer?

Your code is fine, especially if the item_name read should be truncated
to 10 characters (this limit could be defined as constant, BTW, to make
code change easier).

Cheers,
Loic.
 
C

Chris Dollin

svata said:
Hello to all,

as a result from my previous post I'm busy with splitting code into
functions.
The one problem ( out of many ) I encounter is how to properly
use/code a function which returns either array of characters(string) or
a pointer to this array.

You /must not/ return a pointer to an array which is a local
non-static variable of the function.
I read some articles, some other posts and come to this solution:

char *read_name(void){
static char item_name[11];
(fx:snip)

return item_name; /* return string in the form of character array */
}

So you /must not/ do this. The variable `item_name` evaporates when
the function returns, so the pointer to it isn't pointing anywhere
and any use of it gets you undefined behaviour -- which is a Very
Bad Thing.

You must return a pointer to store which will outlive the function
call: for example:

* a static array, usually a bad idea because different uses of the
function will share that array.

* mallocated store (which the using code will have to free)

* store passed in as an argument (so it's the caller's problem)

* mix as desired (carefully)
 
C

Chris Dollin

Chris said:
svata said:
Hello to all,

as a result from my previous post I'm busy with splitting code into
functions.
The one problem ( out of many ) I encounter is how to properly
use/code a function which returns either array of characters(string) or
a pointer to this array.

You /must not/ return a pointer to an array which is a local
non-static variable of the function.
I read some articles, some other posts and come to this solution:

char *read_name(void){
static char item_name[11];
(fx:snip)

return item_name; /* return string in the form of character array */
}

So you /must not/ do this.

OK, OK, I'm blind. I read it three times and didn't see `static`.
As soon as I posted, I saw it.

Sorry sorry sorry. Egg egg egg. Time to go hone.
You must return a pointer to store which will outlive the function
call: for example:

* a static array, usually a bad idea because different uses of the
function will share that array.

So you were OK to do what you did, except there's a gotcha you
need to beware of.
 
S

Stephen Sprunk

svata said:
as a result from my previous post I'm busy with splitting code into
functions.
The one problem ( out of many ) I encounter is how to properly
use/code a function which returns either array of characters(string)
or
a pointer to this array.

I read some articles, some other posts and come to this solution: ....
Are there other solutions? What are the pros and cons using
array/pointer?

Returning a pointer to a static buffer is a common technique. However,
that has the problem that your callers need to be aware of that, and
they'll likely need to strdup() the results if they want to call the
function frequently. It also means the function is not reentrant, which
can kill you if you try to call that function from multiple threads,
signal handlers, etc.

malloc()ing a new buffer for each call is also done at times. That
solves the above problems, but it means that the caller needs to know to
free() the strings you return when they're done with them, or they'll
end up with a memory leak.

The last common option is to require the caller to provide a buffer (and
its size!) for you; you just put the string into their buffer. By
requiring the caller to do the malloc(), it makes it easier for them to
see they need to do a free(). Or they might use a local array, if they
know how big to make it.

The question in all cases is what to do if the buffer isn't big enough
for the input. If you _know_ that a certain size will never be
exceeded, you can go with that, but such "knowledge" usually turns out
to be incorrect after several maintenance/release cycles. You'll need
to spend as much time figuring out this problem as you are deciding who
allocates the buffer where.

S
 
G

goose

svata said:
Hello to all,

as a result from my previous post I'm busy with splitting code into
functions.
The one problem ( out of many ) I encounter is how to properly
use/code a function which returns either array of characters(string) or
a pointer to this array.

I read some articles, some other posts and come to this solution:


char *read_name(void){
static char item_name[11];
char *p_item_name;

printf("Enter the description: ");
if (fgets(item_name, sizeof(item_name), stdin) != NULL){
/* if the input contains a new line */
if (( p_item_name = strchr(item_name, '\n')) != NULL ){
*p_item_name = '\0'; /* get rid of new line */
}
else {
while(getchar() != '\n'){ /* get rid of the rest in the buffer */
;
}
}
}
return item_name; /* return string in the form of character array */
}

Are there other solutions? What are the pros and cons using
array/pointer?

Stephen below suggests the caller supplying the buffer
(and length). It is sometimes desirable for the caller
to repeatedly call the function each time receiving as
much of the data as can fit in the buffer.

e.g.

int foo (char *buf, int length)
{
/* copy data_to_be_returned into buf, not exceeding length */
return 0;
}
....
char buf[20];
int length = foo (buf, sizeof buf);
while (length) {
length = foo (buf, sizeof buf);
/* Use the data here */
}

You can also make the called function behave differently
depending on the argument supplied, supplying a NULL pointer
for the buffer would return the length needed (hard to do with
the above example). This has the disadvantage of seriously
confusing the reader, so comment clearly in header, etc.

e.g.

int foo (char *buf)
{
/* process and store data to be returned in a static buffer */
if (!buf) {
return length_of_data_to_be_returned;
} else {
memcpy (buf, data_to_be_returned, length_of_data_to_be_returned);
}
return 0;
}
....
int length = foo (NULL);
char *buf = malloc (length);
if (!buf) {
/* error!!! */
} else {
foo (buf);
}
....
free (buf);


Basically, every solution will have pros and cons, and
it's up to you to choose which is best for the situation.
There is no "one-good-way" that can be applied to all
situations; be thankful that you've got such a variety
of methods available :)

goose,
hth
 
E

Eric Sosman

svata wrote On 11/13/06 09:44,:
[...]
The one problem ( out of many ) I encounter is how to properly
use/code a function which returns either array of characters(string) or
a pointer to this array.
[...]

Others have addressed your immediate question. I'd like
to draw your attention to two other issues:

printf("Enter the description: ");
if (fgets(item_name, sizeof(item_name), stdin) != NULL){

Question 12.4 in the comp.lang.c Frequently Asked Questions
list http://www.c-faq.com/ explains why you'd be well-advised to
insert fflush(stdout); between these two lines.
/* if the input contains a new line */
if (( p_item_name = strchr(item_name, '\n')) != NULL ){
*p_item_name = '\0'; /* get rid of new line */
}
else {
while(getchar() != '\n'){ /* get rid of the rest in the buffer */
;
}

A good effort, but it overlooks one possibility: What does
getchar() return at end-of-input (or I/O error)? What will
this code do if that happens?
 
O

Old Wolf

svata said:
Hello to all,

as a result from my previous post I'm busy with splitting code into
functions. The one problem ( out of many ) I encounter is how to
properly use/code a function which returns either array of
characters(string) or a pointer to this array.

I prefer making the caller pass in a buffer and a buffer size, and
then the function fills in the buffer and returns the length filled in.
(Or returns a status code and passes the length back via a
pointer parameter, etc.)

This way, the function is not tied to any particular memory
management scheme, and can be called from various places in
your code without having to waste any memory or perform any
acrobatics.

It is also common to allow the function to accept NULL as the
buffer, in which case it will return the size of buffer required, and
then the caller can allocate space if necessary and call the
function again.
 
K

Keith Thompson

svata said:
The one problem ( out of many ) I encounter is how to properly
use/code a function which returns either array of characters(string) or [...]
Are there other solutions? What are the pros and cons using
array/pointer?

See the comp.lang.c FAQ, <http://www.c-faq.com/>, particularly
questions 7.5a and 7.5b.
 
S

svata

Eric said:
A good effort, but it overlooks one possibility: What does
getchar() return at end-of-input (or I/O error)? What will
this code do if that happens?

It is EOF, am I right?

svata
 
E

Eric Sosman

svata wrote On 11/14/06 05:44,:
Eric Sosman wrote:




It is EOF, am I right?

Yes, that's what getchar() will return. Now, what
will your code do if that happens?
 
S

svata

Eric said:
Yes, that's what getchar() will return. Now, what
will your code do if that happens?

So, it should look as follows:

while(getchar() != EOF && getchar() != '\n'){
;
}

One has to check for EOF as well, I suppose.

svata
 
C

Chris Dollin

svata said:
So, it should look as follows:

while(getchar() != EOF && getchar() != '\n'){
;
}

One has to check for EOF as well, I suppose.

You realise that code will test two different characters?
(Even if they have the same value ...)
 
S

santosh

svata said:
So, it should look as follows:

while(getchar() != EOF && getchar() != '\n'){
;
}

One has to check for EOF as well, I suppose.

Don't call getchar() twice. Each call will return a different value.
Also you're discarding both return values. I think you may find the
following code snippet more suited to what you were probably trying to
do.

int c;
....

while((c = getchar()) != EOF) {
if(c != '\n') {
DO_SOMETHING;
}
}

Now a return value of EOF could be caused either by end of file or some
kind of I/O error. Presumably, you'd consider the former to be a normal
condition and the latter as an error of some severity. To find which of
them caused getchar() to return EOF, you might use ferror() and/or
feof(). The former returns non-zero if the stream has encountered an
I/O error, while the latter returns true when end of file has been
reached on the stream. Once you determine that then appropriate action
can be taken. Use clearerr() to reset the stream's end of file and
error indicators.
 
C

CBFalconer

svata said:
So, it should look as follows:

while(getchar() != EOF && getchar() != '\n'){
}

One has to check for EOF as well, I suppose.

This is agonizing. To put it out of its misery:

int flushln(FILE *f) {
int ch;

while ((EOF != (ch = getc(f)) && (ch != '\n')) continue;
return ch;
}

Replace your code with "someint = flushln(stdin);" and decide what
needs doing when (and if) someint becomes EOF.
 
S

Simon Biber

CBFalconer said:
This is agonizing. To put it out of its misery:

int flushln(FILE *f) {
int ch;

while ((EOF != (ch = getc(f)) && (ch != '\n')) continue;
return ch;
}

This is correct code, but tricky code. It's a sufficiently common idiom
in C programs that I would expect any programmer to be able to
understand it, along with the even commoner idiom:
while((ch = getc(fp)) != EOF) { ... }

That said, however, in my opinion it is best practise to avoid trying to
put the whole loop contents into the loop condition when it's not needed.

If one wants to write simple, easy to read C, I think one should adhere
to the rule that loop bodies are supposed to make changes to program
state, while (while, for, if) conditions are supposed to make a decision
based on the current program state but not make changes to that state.

Here's another way to write it, which is simpler to read in my opinion:

int flushln(FILE *fp)
{
int ch;

do
{
ch = getc(fp);
}
while(ch != EOF && ch != '\n');

return ch;
}
 
S

santosh

Simon said:
This is correct code, but tricky code. It's a sufficiently common idiom
in C programs that I would expect any programmer to be able to
understand it, along with the even commoner idiom:
while((ch = getc(fp)) != EOF) { ... }

That said, however, in my opinion it is best practise to avoid trying to
put the whole loop contents into the loop condition when it's not needed.

If one wants to write simple, easy to read C, I think one should adhere
to the rule that loop bodies are supposed to make changes to program
state, while (while, for, if) conditions are supposed to make a decision
based on the current program state but not make changes to that state.

Here's another way to write it, which is simpler to read in my opinion:

int flushln(FILE *fp)
{
int ch;

do
{
ch = getc(fp);
}
while(ch != EOF && ch != '\n');

return ch;
}

This may be okay for flushing input but if you want to process it, then
having the test for EOF and NL at the bottom is wasteful as the code
within the DO clause has to do the test anyway.
 
R

Richard Tobin

do
{
ch = getc(fp);
}
while(ch != EOF && ch != '\n');
[/QUOTE]
This may be okay for flushing input but if you want to process it, then
having the test for EOF and NL at the bottom is wasteful as the code
within the DO clause has to do the test anyway.

A reasonable compiler will optimise that if you could. Even if it
doesn't, clarity is usually much more important than that kind of
micro-optimisation.

-- RIchard
 

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,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top