Why can I not alloc memory in called function?

A

Angus

Hello

I am passing a char* pointer into a function and in the function I malloc
but when the function returns the pointer seems to go out of scope? I
thought that if I passed a pointer, then I would allocate memory to the
pointer? What am I doing wrong?

#include <stdlib.h>

void doit(char* allostring)
{
allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);
}

int main()
{
char* strtopass = 0;
doit(strtopass);

free(strtopass);

return 0;
}
 
O

osmium

Angus said:
I am passing a char* pointer into a function and in the function I
malloc but when the function returns the pointer seems to go out of
scope? I thought that if I passed a pointer, then I would allocate
memory to the pointer? What am I doing wrong?

#include <stdlib.h>

void doit(char* allostring)
{
allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);
}

int main()
{
char* strtopass = 0;
doit(strtopass);

free(strtopass);

return 0;
}

Variables are passed by value in C. So doit was operating on a local *copy*
of variable of interest. You can work around this by using pointers.
 
N

Nick Keighley

I am passing a char* pointer into a function and in the function I malloc
but when the function returns the pointer seems to go out of scope?  I
thought that if I passed a pointer, then I would allocate memory to the
pointer?  What am I doing wrong?

#include <stdlib.h>

void doit(char* allostring)
{
    allostring = malloc(6);
    memcpy(allostring, "Hello\0", 6);

}

int main()
{
    char* strtopass = 0;
    doit(strtopass);

    free(strtopass);

    return 0;
}

see the FAQ

http://c-faq.com/ptrs/passptrinit.html
 
A

Angus

But strtopass is a char* - so it is a pointer? In this code, am I not
passing the address of the strtopass variable?

I can see how to do this with eg a int* - but how do I do it with a char*?
 
C

Chad

Variables are passed by value in C.  So doit was operating on a local *copy*
of variable of interest.  You can work around this by using pointers.

Should would something like the following be a possible fix....

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

char* doit(char* allostring)
{
allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);
return allostring;
}

int main(void)
{
char* strtopass = 0;
strtopass = doit(strtopass);

printf("%s\n", strtopass);
free(strtopass);

return 0;

}
 
F

Fishy

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

char* doit(char* allostring)
{
  allostring = malloc(6);
  memcpy(allostring, "Hello\0", 6);
  return allostring;

}

int main(void)
{
  char* strtopass = 0;
  strtopass = doit(strtopass);

  printf("%s\n", strtopass);
  free(strtopass);

  return 0;

}

This does solve the problem in case it is limited to only memory
location only. If one intends to do the same with several locations
then following would help.

void doit(char ** allostring)
{
*allostring = malloc(6);
memcpy(*allostring, "Hello\0", 6);
}

int main()
{
char *strtopass = 0;
doit(&strtopass);

printf("%s\n",strtopass);

free(strtopass);

return 0;
}
 
A

Angus

Hi

Yes I noticed that does work, but sometimes you might want to pass in say
two paramaters in this way.

If I use same function char* doit(char* allostring) but call it like this:
doit(strtopass);

Then it does not work. How would I write the function for the string to
refer to the passed address in memory after the function returns?

It must be possible, because if I do this:

void addthree(int* i)
{
*i +=3;
}

And call like this:
int num = 0;
addthree(&num);

Then num = 3 after the function returns.

So how do I do that for a char*?

Angus


Variables are passed by value in C. So doit was operating on a local
*copy*
of variable of interest. You can work around this by using pointers.

Should would something like the following be a possible fix....

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

char* doit(char* allostring)
{
allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);
return allostring;
}

int main(void)
{
char* strtopass = 0;
strtopass = doit(strtopass);

printf("%s\n", strtopass);
free(strtopass);

return 0;

}
 
A

Angus

I thought I had tried that.

Yes that works. thanks.


Fishy said:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* doit(char* allostring)
{
allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);
return allostring;

}

int main(void)
{
char* strtopass = 0;
strtopass = doit(strtopass);

printf("%s\n", strtopass);
free(strtopass);

return 0;

}

This does solve the problem in case it is limited to only memory
location only. If one intends to do the same with several locations
then following would help.

void doit(char ** allostring)
{
*allostring = malloc(6);
memcpy(*allostring, "Hello\0", 6);
}

int main()
{
char *strtopass = 0;
doit(&strtopass);

printf("%s\n",strtopass);

free(strtopass);

return 0;
}
 
O

osmium

Angus said:
Yes I noticed that does work, but sometimes you might want to pass in say
two paramaters in this way.

If I use same function char* doit(char* allostring) but call it like
this:
doit(strtopass);

Then it does not work. How would I write the function for the string to
refer to the passed address in memory after the function returns?

It must be possible, because if I do this:

void addthree(int* i)
{
*i +=3;
}

And call like this:
int num = 0;
addthree(&num);

Then num = 3 after the function returns.

So how do I do that for a char*?

Angus




Should would something like the following be a possible fix....

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

char* doit(char* allostring)
{
allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);
return allostring;
}

int main(void)
{
char* strtopass = 0;
strtopass = doit(strtopass);

printf("%s\n", strtopass);
free(strtopass);

return 0;

}

If you have actually read the FAQ and still have questions, please tell us
that.

Please put your replies at the bottom of the message you are responding to.
 
B

Barry Schwarz

But strtopass is a char* - so it is a pointer? In this code, am I not
passing the address of the strtopass variable?

Please don't top post.

Read the reply again, especially the first sentence. It answers your
question before you asked it.
 
I

Ike Naar

allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);

malloc can fail, and return NULL. You should check for this.

String constants already contain the zero terminator.
"Hello" is the 6-byte char array {'H','e','l','l','o','\0'},
"Hello\0" is the 7-byte char array {'H','e','l','l','o','\0','\0'}.
There is no good reason to use "Hello\0" instead of the simpler "Hello".

Avoid magic numbers, and leave byte counting to the compiler.
That way, if you ever decide to change "Hello" to "Hi", or to "Buon giorno",
you won't have to change 6 to some other constant in several places.

A probably more robust way to write the code fragment is:

char const hello[] = "Hello";
allostring = malloc(sizeof hello);
if (allostring != NULL)
strcpy(allostring, hello);
 
O

osmium

Ike Naar said:
Angus <[email protected]> said:
allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);

malloc can fail, and return NULL. You should check for this.

String constants already contain the zero terminator.
"Hello" is the 6-byte char array {'H','e','l','l','o','\0'},
"Hello\0" is the 7-byte char array {'H','e','l','l','o','\0','\0'}.
There is no good reason to use "Hello\0" instead of the simpler "Hello".

Avoid magic numbers, and leave byte counting to the compiler.
That way, if you ever decide to change "Hello" to "Hi", or to "Buon
giorno",
you won't have to change 6 to some other constant in several places.

A probably more robust way to write the code fragment is:

char const hello[] = "Hello";
allostring = malloc(sizeof hello);
if (allostring != NULL)
strcpy(allostring, hello);

Well, if you are on a robustness kick, might it be a good idea for the
function to inform the caller, or somebody, when the function fails to do
what it was supposed to do?
 
I

Ike Naar

[...][...]
Well, if you are on a robustness kick, might it be a good idea for the
function to inform the caller, or somebody, when the function fails to do
what it was supposed to do?

Yes, of course. But, in this case, this information comes for free.

The function, as written by the OP, does nothing useful (well, it
leaks some memory), but others have already explained how that can
be fixed. If those fixes are applied, the caller can detect that
a malloc failure occurred by looking at the function's output,
which, in that case, is NULL.
 
O

osmium

Ike Naar said:
[...]
allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);
[...]
Well, if you are on a robustness kick, might it be a good idea for the
function to inform the caller, or somebody, when the function fails to do
what it was supposed to do?

Yes, of course. But, in this case, this information comes for free.

The function, as written by the OP, does nothing useful (well, it
leaks some memory), but others have already explained how that can
be fixed. If those fixes are applied, the caller can detect that
a malloc failure occurred by looking at the function's output,
which, in that case, is NULL.

So it is your belief that error reporting is sufficient if the user can
deduce that the proper side effects occurred?

Please elaborate.
 
S

Stephen Sprunk


Stop top-posting. I've corrected the remainder of your reply.
Chad said:
Angus wrote:
I am passing a char* pointer into a function and in the function I
malloc but when the function returns the pointer seems to go out of
scope? I thought that if I passed a pointer, then I would allocate
memory to the pointer? What am I doing wrong?

[code removed]

Variables are passed by value in C. So doit was operating on a local
*copy* of variable of interest. You can work around this by using
pointers.

Should would something like the following be a possible fix....

[code removed]

Yes I noticed that does work, but sometimes you might want to pass in say
two paramaters in this way.

If your function needs two "output" parameters, it is almost certain
that you've designed the function incorrectly.
If I use same function char* doit(char* allostring) but call it like this:
doit(strtopass);

Then it does not work.

No, it will not. You've already been told why: you are passing in the
_value_ of strtopass, not a _pointer_ to strtopass.
How would I write the function for the string to
refer to the passed address in memory after the function returns?

It must be possible, because if I do this:

void addthree(int* i)
{
*i +=3;
}

And call like this:
int num = 0;
addthree(&num);

Then num = 3 after the function returns.

So how do I do that for a char*?

You do it the exact same way:

#include <stdlib.h>

void doit(char** allostring) {
*allostring = malloc(6);
memcpy(*allostring, "Hello\0", 6);
}

int main() {
char* strtopass = 0;
doit(&strtopass);
free(strtopass);
return 0;
}

However, the use of "output" parameters does not follow C conventions
and should be avoided when possible; it is more idiomatic to write:

#include <stdlib.h>

char* doit() {
char *allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);
return allostring;
}

int main() {
char* strtopass = doit();
free(strtopass);
return 0;
}


S
 
E

Eric Sosman

[...]
However, the use of "output" parameters does not follow C conventions
and should be avoided when possible; it is more idiomatic to write:

#include<stdlib.h>

char* doit() {
char *allostring = malloc(6);
memcpy(allostring, "Hello\0", 6);

Note the "output parameter" in this call. It's possible, I
guess, for a Standard library function to have an unconventional
interface, but I see nothing unconventional about the first
arguments of memcpy, strcpy, strcat, sprintf, qsort, ... A case
could be made that setjmp is unconventional and that the "%n"
conversion specifier is unusual -- but there seem to be enough
output parameters floating around that they could hold their own
convention and nominate their own candidates ...

(Also, if it's "more idiomatic" to omit checking malloc's
value for NULL, and "more idiomatic" to copy strings with hand-
counted memcpy than with strcpy, then I think a "t" has been
omitted somewhere.)
 
D

Denis McMahon

I am passing a char* pointer into a function and in the function I malloc
but when the function returns the pointer seems to go out of scope? I
thought that if I passed a pointer, then I would allocate memory to the
pointer? What am I doing wrong?

1) Failing to understand that when you pass a pointer to a function,
it's just a variable.
2) Failing to understand that if you want to change a variable in a
function, you have to pass a pointer to it.
3) Failing to understand that 2 and 3 mean that if you want to change a
pointer in a function, you have to pass its address to the function, not it.
4) Asking others to do your coursework.

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

void d(char **a)
{
*a = malloc(6);
memcpy(*a, "Hello\0", 6);
}

int main()
{
char* s = 0;
d(&s);
printf("%s\n",s);
free(s);
return 0;
}

Rgds

Denis McMahon
 
I

Ike Naar

So it is your belief that error reporting is sufficient if the user can
deduce that the proper side effects occurred?

If it is expected that the caller can handle the error in a better way
than the function can, then the function should just notify the caller
that an error occurred, and leave the handling to the caller.

Several functions from the standard library (e.g. malloc, fopen) work just
like that.
 
O

osmium

Ike Naar said:
If it is expected that the caller can handle the error in a better way
than the function can, then the function should just notify the caller
that an error occurred, and leave the handling to the caller.

Several functions from the standard library (e.g. malloc, fopen) work just
like that.

No they don't! malloc returns a void*, and NULL is a legitimate value that
can be assigned to a void*.

In the case at hand, doit returns void, nothing in the world to do with
void*. void means this function does not return a value. Your suggestion
was to look at the *parameter* after the call and see if IT was null.
 
B

Barry Schwarz

No they don't! malloc returns a void*, and NULL is a legitimate value that
can be assigned to a void*.

Are you deliberately trying to be obtuse. The NULL value returned
does exactly what was suggested, notify the user that the request
failed.
In the case at hand, doit returns void, nothing in the world to do with
void*. void means this function does not return a value. Your suggestion
was to look at the *parameter* after the call and see if IT was null.

Are you deliberately trying to be obtuse? There are numerous ways for
a function to notify the caller that a problem occurred. It need not
be the value in a return statement.

The actual suggestion was "The function, as written by the OP, does
nothing useful (well, it leaks some memory), but others have already
explained how that can be fixed. If those fixes are applied, the
caller can detect that a malloc failure occurred by looking at the
function's output, which, in that case, is NULL." The fixes referred
to changed the calling argument to the address of the pointer and
changed the function to store the value returned by malloc in that
pointer. So while the function returns nothing in the sense of a
return statement, it certainly does have output which is immediately
available to the calling function. Just as with malloc, a NULL value
in this pointer would inform the calling function of a problem.
 

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,744
Messages
2,569,484
Members
44,905
Latest member
Kristy_Poole

Latest Threads

Top