function returning pointer

  • Thread starter Ron Peterson (012ED25E)
  • Start date
R

Ron Peterson (012ED25E)

I don't understand the difference between the following:

void foo( char *s ) {
s = malloc( 10 );
}

and

char * bar( char * s ) {
s = malloc( 10 );
return s;
}

If I now have char *a, and I say foo( a ), how is that different from
saying a = bar( a )? I've deduced from some code that I'm writing
that I should use the second form; but why?
 
S

Seebs

void foo( char *s ) {
s = malloc( 10 );
}

This allocates memory, but has no other effect.

When you call a function, it is given a COPY of the arguments you pass
it. This function modifies the local variable 's' that is its local copy
of the parameter. It does not modify anything else.
char * bar( char * s ) {
s = malloc( 10 );
return s;
}

This does the same thing -- but it returns the pointer you allocated,
so you can use it later.
If I now have char *a, and I say foo( a ), how is that different from
saying a = bar( a )? I've deduced from some code that I'm writing
that I should use the second form; but why?

Imagine a function:
void foo2(int s) {
s = 23;
}

Because foo(a) doesn't change a, any more than foo2(3) changes 3.

-s
 
I

Ioannis Vranos

Ron said:
I don't understand the difference between the following:

void foo( char *s ) {
s = malloc( 10 );
}

This creates a local char pointer variable named s, that is a copy of the
pointer variable you pass to the function.

Then you allocate 10 Bytes in the free store if the malloc() call is
successful, which are leaked when the execution of the program reaches the
end of the scope of function foo().

That is, the char pointer variable s is destroyed, but the allocated space
of the 10 Bytes is not released.


The correct version for the above is:


#include <stdlib.h>


void foo( char *s ) {
s = malloc( 10 );

free(s);
}



and

char * bar( char * s ) {
s = malloc( 10 );
return s;
}

If I now have char *a, and I say foo( a ), how is that different from
saying a = bar( a )? I've deduced from some code that I'm writing
that I should use the second form; but why?


I do not understand the question. What you probably want to do is the style:


#include <stdlib.h>


char * bar(void) {

char *s;

/* ... */

s = malloc( 10 );

// ...

return s;
}




--
Ioannis Vranos

C95 / C++03 Software Developer

http://www.cpp-software.net
 
R

Ron Peterson (012ED25E)

When you call a function, it is given a COPY of the arguments you pass
it.

Oh duh. I haven't done any C for so long I had a total brain freeze.
Of course. Thanks everyone.

-Ron-
 
B

Beej Jorgensen

Ron Peterson (012ED25E) said:
char * bar( char * s ) {

The others have pointed out the errors here, but I wanted to note that
there is a common idiom in the C libs to return a copy of an
operated-upon item. For example:

char *strcpy(char *dest, const char *src);

which returns "dest" as opposed to returning void.

The idea is, I think, that the caller might want to use the result in
some other immediate capacity. A contrived example:

printf("%s\n", strcpy(foo, bar)); // prints foo

That being said, I don't think I've ever seen strcpy()'s return value
used.

-Beej
 
F

frank

You should not use either form.
The first doesn't work and the second makes no use of the value of the
function call argument.

Both of the following are workable:

void bar( char **s )
{
*s = malloc( 10 );
}

char *bar( void )
{
char *s = malloc( 10 );

return s;
}

I tried to get the returns from malloc but hit a bump somewhere:

dan@dan-desktop:~/source$ gcc -std=c99 -Wall -Wextra malloc1.c -o out
malloc1.c: In function ‘main’:
malloc1.c:25: warning: unused variable ‘c’
dan@dan-desktop:~/source$ ./out
Segmentation fault
dan@dan-desktop:~/source$ cat malloc1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void bar( char **s )
{
*s = malloc( 10 );
}

char *baz( void )
{
char *s = malloc( 10 );

return s;
}


int
main(void)
{
void bar ( char ** );
char * baz ( void );
char ** a;
char * b;
int c;

bar ( a );
b = baz( );
#if 0
c = * b;
strcpy(b, "qwerty");
printf ("%s\n", b);
#endif


return 0;
}
// gcc -std=c99 -Wall -Wextra malloc1.c -o out
dan@dan-desktop:~/source$

One other thing. I just tried to man indent to find the usage for
setting the tablength at 4, as pete had it for his code. How do you do
that?
 
L

Lew Pitcher

On November 19, 2009 18:53, in comp.lang.c, frank ([email protected])
wrote:
[snip]
I tried to get the returns from malloc but hit a bump somewhere:

dan@dan-desktop:~/source$ gcc -std=c99 -Wall -Wextra malloc1.c -o out
malloc1.c: In function ‘main’:
malloc1.c:25: warning: unused variable ‘c’
dan@dan-desktop:~/source$ ./out
Segmentation fault
dan@dan-desktop:~/source$ cat malloc1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void bar( char **s )

Function bar accepts a pointer to a pointer-to-char, right?
{
*s = malloc( 10 );

Here, you take that pointer to a pointer-to char, find what it points to,
and change that value so that it now points to the data area you have
malloc()ed.

In other words,
Before the line,
s pointed to a pointer, and that pointer pointed somewher
After the line,
s still pointed to the same place, but the contents of that place have
been changed to now contain the value returned by malloc()

With me so far?
}

char *baz( void )
{
char *s = malloc( 10 );

return s;
}


int
main(void)
{
void bar ( char ** );
char * baz ( void );
char ** a;

Here, you allocate a as a pointer to pointer-to-char. But, you leave it
uninitialized; a doesn't point at anything in particular (it's value is
indeterminate).

char * b;
int c;

bar ( a );

Now, you pass a, an unitialized pointer to bar(). Remember what bar() will
do; it will try to modify the area that this pointer points to.

But, this pointer has an indeterminate value; it /could/ point anywhere,
including outside of the space in which your program and it's data reside.

<off_topic>
Now, when a Unix process (that being the platform you are apparently
compiling and running on) tries to access memory outside of the bounds of
the process's memory map, Unix protects that memory, and sends a SIGSEGV
signal to the process. Uncaught, this SIGSEGV will cause the process to
abort, with a "Segmentation Violation" error message. That's what you got.
</off_topic>

The cure for your problem is to, prior to invoking function bar(),
*initialize* the pointer so that it points at a pointer-to-char.

Given your existing code, what you really wanted to do was...

a = &b;
bar ( a );

or, in shorter form

bar (&b);
 
K

Keith Thompson

frank said:
I tried to get the returns from malloc but hit a bump somewhere: [...]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void bar( char **s )
{
*s = malloc( 10 );
}

char *baz( void )
{
char *s = malloc( 10 );

return s;
}


int
main(void)
{
void bar ( char ** );
char * baz ( void );
char ** a;
char * b;
int c;

bar ( a );
b = baz( );
#if 0
c = * b;
strcpy(b, "qwerty");
printf ("%s\n", b);
#endif


return 0;
}
[...]

As Lew Pitcher already mentioned, the problem is that ``a'' is
uninitialized when you pass it to bar.

Since function arguments are passed by value, passing (the value of)
an uninitialized object to any function is bad news. In your main
function above, you declare ``a'', but you don't initialize it or
assign a value to it; you then pass it as an argument in ``bar(a)''.

This is equally bad regardless of whether ``a'' is a char, a pointer,
a pointer to a pointer, or any other type of object. The type of the
object is immaterial; referring to the value of an uninitialized
object is bad. (The word "bad" is admittedly vague. In this case,
it's undefined behavior; in some cases it might not be. But it's
almost certainly not what you want to do, unless examining garbage in
memory is your idea of a good time).
One other thing. I just tried to man indent to find the usage for
setting the tablength at 4, as pete had it for his code. How do you do
that?

Search for the word "indentation" in the man page.
 
F

frank

On November 19, 2009 18:53, in comp.lang.c, frank
Function bar accepts a pointer to a pointer-to-char, right?


Here, you take that pointer to a pointer-to char, find what it points
to, and change that value so that it now points to the data area you
have malloc()ed.

In other words,
Before the line,
s pointed to a pointer, and that pointer pointed somewher
After the line,
s still pointed to the same place, but the contents of that place have
been changed to now contain the value returned by malloc()

With me so far?
yup.

Here, you allocate a as a pointer to pointer-to-char. But, you leave it
uninitialized; a doesn't point at anything in particular (it's value is
indeterminate).



Now, you pass a, an unitialized pointer to bar(). Remember what bar()
will do; it will try to modify the area that this pointer points to.

But, this pointer has an indeterminate value; it /could/ point anywhere,
including outside of the space in which your program and it's data
reside.

<off_topic>
Now, when a Unix process (that being the platform you are apparently
compiling and running on) tries to access memory outside of the bounds
of the process's memory map, Unix protects that memory, and sends a
SIGSEGV signal to the process. Uncaught, this SIGSEGV will cause the
process to abort, with a "Segmentation Violation" error message. That's
what you got. </off_topic>

The cure for your problem is to, prior to invoking function bar(),
*initialize* the pointer so that it points at a pointer-to-char.

Given your existing code, what you really wanted to do was...

a = &b;
bar ( a );

or, in shorter form

bar (&b);

Alright, thx Lew. When I try to work with indirection and do it from
scratch, I think I might resemble a beautiful animal like a caribou: 15
minutes after it's born, covered with afterbirth, wobbly on it's too-long
limbs.

So when you have a constructor that uses char **, what you want to do is
declare a pointer to char and then pass it preceded by the reference
operator, & .

I figured I would have this correct if I could print out the vlaues that
malloc returned. Indeed, it's plum necessary for robust code to at least
check for a positive value.

I've got the parts here, dan@dan-desktop:~/source$ gcc -std=c99 -Wall -
Wextra malloc1.c -o out
malloc1.c: In function ‘bar’:
malloc1.c:8: warning: initialization makes integer from pointer without a
castdan@dan-desktop:~/source$ gcc -std=c99 -Wall -Wextra malloc1.c -o out
malloc1.c: In function ‘bar’:
malloc1.c:8: warning: initialization makes integer from pointer without a
cast
malloc1.c:9: warning: format ‘%d’ expects type ‘int’, but argument 2 has
type ‘char **’
malloc1.c:8: warning: unused variable ‘r’
malloc1.c: In function ‘main’:
malloc1.c:26: warning: unused variable ‘r’
dan@dan-desktop:~/source$ ./out
-1081554784
qwerty
0
dan@dan-desktop:~/source$ cat malloc1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void bar( char **s )
{
*s = malloc( 10 );
int r = &s;
printf("%d\n", s);
}

char *baz( void )
{
char *s = malloc( 10 );

return s;
}


int
main(void)
{
void bar ( char ** );
char * baz ( void );
char * b;
int c, r;

bar ( &b );
b = baz( );
#if 1
c = * b;
strcpy(b, "qwerty");
printf ("%s\n", b);
printf ("%d\n", c);
#endif


return 0;
}
// gcc -std=c99 -Wall -Wextra malloc1.c -o out
dan@dan-desktop:~/source$

malloc1.c:9: warning: format ‘%d’ expects type ‘int’, but argument 2 has
type ‘char **’
malloc1.c:8: warning: unused variable ‘r’
malloc1.c: In function ‘main’:
malloc1.c:26: warning: unused variable ‘r’
dan@dan-desktop:~/source$ ./out
-1081554784
qwerty
0
dan@dan-desktop:~/source$ cat malloc1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void bar( char **s )
{
*s = malloc( 10 );
int r = &s;
printf("%d\n", s);
}

char *baz( void )
{
char *s = malloc( 10 );

return s;
}


int
main(void)
{
void bar ( char ** );
char * baz ( void );
char * b;
int c, r;

bar ( &b );
b = baz( );
#if 1
c = * b;
strcpy(b, "qwerty");
printf ("%s\n", b);
printf ("%d\n", c);
#endif


return 0;
}
// gcc -std=c99 -Wall -Wextra malloc1.c -o out
dan@dan-desktop:~/source$
but I seem to be unable to dereference:

dan@dan-desktop:~/source$ gcc -std=c99 -Wall -Wextra malloc1.c -o out
malloc1.c: In function ‘bar’:
malloc1.c:8: warning: initialization makes integer from pointer without a
cast
malloc1.c:9: warning: format ‘%d’ expects type ‘int’, but argument 2 has
type ‘char **’
malloc1.c:8: warning: unused variable ‘r’
malloc1.c: In function ‘main’:
malloc1.c:26: warning: unused variable ‘r’
dan@dan-desktop:~/source$ ./out
-1081554784
qwerty
0
dan@dan-desktop:~/source$ cat malloc1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void bar( char **s )
{
*s = malloc( 10 );
int r = &s;
printf("%d\n", s);
}

char *baz( void )
{
char *s = malloc( 10 );

return s;
}


int
main(void)
{
void bar ( char ** );
char * baz ( void );
char * b;
int c, r;

bar ( &b );
b = baz( );
#if 1
c = * b;
strcpy(b, "qwerty");
printf ("%s\n", b);
printf ("%d\n", c);
#endif


return 0;
}
// gcc -std=c99 -Wall -Wextra malloc1.c -o out
dan@dan-desktop:~/source$

Any takers on this one? One problem with programs that have over fifty
options is that you can't find the one option that any indent program
really needs, i.e., the tab length.
 
F

frank

It's good manners to snip a long post like that.

Oops, sorry. I didn't want to snip too much of Lew's comments, because
I'm not quite straightened out on this yet. I over-selected out of
terminal to get way too much of it. :(
On my machine "info indent", open options summary and search for tab.

,----
| `-tsN'
| `--tab-sizeN'
| Set tab size to N spaces.
| *Note Indentation::.
`----

I guess you could have guessed "length" "size" or "tab" were pertinent
search criteria :-;

dan@dan-desktop:~/source$ indent -i4 malloc1.c

This works.
 
F

frank

frank said:
On November 19, 2009 18:53, in comp.lang.c, frank [...]
One other thing. I just tried to man indent to find the usage for
setting the tablength at 4, as pete had it for his code. How do you
do that?

Any takers on this one? One problem with programs that have over fifty
options is that you can't find the one option that any indent program
really needs, i.e., the tab length.

I already gave you a strong hint: "man indent" and search for the word
"indentation". The indent program has a number of options to control
indentation for specific constructs, and one to control overall
indentation. The man page explains it better than I could (and, unlike
anything I could tell you, is probably specific to the version you're
using).

Sorry, Keith, I didn't see a response before. Am I the only person whose
newsreader doesn't see the original post in these threads?

I must have looked right past it a couple times. I really struggle to
get useful things out of the man pages. Besides all of the learning
curve of dealing with a new paradigm for an OS, it doesn't really want to
respond to my keyboard or mouse. It takes over a terminal and steps on
everything I'm doing. Maybe I will come to like this spartan reference.
Maybe linux users might raise their standards for help materials.
 
F

frank

This creates a local char pointer variable named s, that is a copy of
the pointer variable you pass to the function.

Then you allocate 10 Bytes in the free store if the malloc() call is
successful, which are leaked when the execution of the program reaches
the end of the scope of function foo().

That is, the char pointer variable s is destroyed, but the allocated
space of the 10 Bytes is not released.


The correct version for the above is:


#include <stdlib.h>


void foo( char *s ) {
s = malloc( 10 );

free(s);
}

I think "correctness" is a function of what you want to have happen. If
foo is to be a constructor, then the responsibility to free s goes to the
caller.
 
K

Keith Thompson

frank said:
dan@dan-desktop:~/source$ indent -i4 malloc1.c

This works.

And that's my preference as well. The "-ts" or "--tab-size"
option is useful only if you want to use tabs for indentation.
Opinions on this vary widely, but personally I strongly prefer to
use only spaces for indentation.
 
F

frank

And that's my preference as well. The "-ts" or "--tab-size" option is
useful only if you want to use tabs for indentation. Opinions on this
vary widely, but personally I strongly prefer to use only spaces for
indentation.

People who would rather have tabs than the equivalent whitespace must
have less frustration with them than I do.
 
F

frank

You don't pass the results of the malloc back to the caller so how do
you expect to free it?

Elsesubthread, I have not *yet* gotten a constructor to interact with a
caller so as to create memory and pass back the means to do something
with it. Don't bet against me for too long.
 
P

Phil Carmody

frank said:
frank said:
On Sat, 21 Nov 2009 20:29:14 +0100, Richard wrote: [...]
On my machine "info indent", open options summary and search for tab.

,----
| `-tsN'
| `--tab-sizeN'
| Set tab size to N spaces.
| *Note Indentation::.
`----

I guess you could have guessed "length" "size" or "tab" were pertinent
search criteria :-;

dan@dan-desktop:~/source$ indent -i4 malloc1.c

This works.

And that's my preference as well. The "-ts" or "--tab-size" option is
useful only if you want to use tabs for indentation. Opinions on this
vary widely, but personally I strongly prefer to use only spaces for
indentation.

People who would rather have tabs than the equivalent whitespace must
have less frustration with them than I do.

The entire linux development community seems to cope. I thought I'd hate
it at first, but putting all responsibility for indenting on my editor
makes it a no-brainer. Keeping lines less than 80 characters in width
is the only hard part. (This massive indentation of course is probably
one of the reasons that echelons of ifs are rendered instead as a flat
sequence of gotos, which many view as a step backwards.)

Phil
 
F

frank

The entire linux development community seems to cope. I thought I'd hate
it at first, but putting all responsibility for indenting on my editor
makes it a no-brainer. Keeping lines less than 80 characters in width is
the only hard part. (This massive indentation of course is probably one
of the reasons that echelons of ifs are rendered instead as a flat
sequence of gotos, which many view as a step backwards.)

Phil

This might shock you, but Jacob's lcc is one of the better windows tools
for C. You really can't pull the whole thing off on a command line like
you can on a terminal with linux. So windows-users need an IDE, and tabs
confuse the heck out of them, in particular when you're adding keystrokes
here and pasting in a subroutine there.

The obvious remedy is "switch to linux," but I was only able to pull it
off because my good friend here in the Duke City is a sysadmin and able
to straighten me out on this stuff.
 
P

Phil Carmody

frank said:
This might shock you, but Jacob's lcc is one of the better windows tools
for C. You really can't pull the whole thing off on a command line like
you can on a terminal with linux. So windows-users need an IDE, and tabs
confuse the heck out of them, in particular when you're adding keystrokes
here and pasting in a subroutine there.

You're confusing 'shock' for 'complete disinterest'.

'One of the better windows tools' is a phrase as meaningful to me
as 'one of the nicer smelling farts'.
The obvious remedy is "switch to linux," but I was only able to pull it
off because my good friend here in the Duke City is a sysadmin and able
to straighten me out on this stuff.

There's also an internet out there, bellieve it or not; the IRC channels
are great for assistance in the debian-based arena (so ubuntu too).

Phil
 
N

Nick Keighley

me too. I loathe tabs.

if your layout standard pushes you towards gots then your layout
standard needs re-thinking. But I don't find sticking to 80 colums
particularly difficult and I *never* use goto (the last time I did an
Italian collegue decribed me as a "software beast"- a title carry with
pride).

why not? I mean I like IDEs but I don't see why lcc couldn't run as
command line tool. gcc does and gcc runs on windows. I'm pretty sure
you could run Microsoft's compiler from a command line. gcc has
zillions of command line options but no one claims it can't be run
from a command line.

I don't accept this

no they don't. I've maintained tabbed code on Visual Studio and VC
does not get confused. If you editor is set up to match the tab
spacing used by the code there is no problem. I personnally don't like
tabs and I do like IDEs but you shouldn't use arguments that don't
seem to be true.

I have never had a problem and I don't see why an IDE would make a
difference. But then I use a windows (in this case I don't necessarily
mean MS Windows) based editor for Unix code as well. What is so
special about unix editors that you claim they handle tabs better than
MS Windows editors can?

<snip>


--
During the day as you start up many applications the computer
Random
Access Memory is used to support these applications. Eventually
the
computer will start to slow down as more memory is put into use.
Shutting applications down quite often does not release all the
memory back into use. Restarting the computer each day will start
the
day with a clear memory - enabling the computer to go faster.
(advice from IT)
 

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,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top