Could you please help me for a simple question?

X

Xiaoshen Li

Dear All:

I am learning C and doing a program. This program hopes to use one
function getbuf() to return two char * type. One through return
statement. Another through the argument.

The program is following:

char *getbuf(char* pC);

int main()
{
char *b, *r;

b=getbuf(r);
printf("In main, b: %s \n", b);
printf("In main, r: %s \n", r);
return 0;
}


char* getbuf(char* pC)
{
static char buff[]="good morning";
pC ="good afternoon";
return (char*) buff;
}

However, after calling the function getbuf(), pointer b gets the value
"good morning" correctly. But pointer r cannot get the value "good
afternoon" from the function getbuf(). I thought by plugging pointer r
as the argument, r becomes the same pointer as pC in the function
getbuf(). And setting pC's value inside getbuf() will give r the same
value. But r never get the value "good afternoon".
Could you kindly help me out? This has driven me crazy. Thank you very much.
 
M

Michael Mair

Xiaoshen said:
Dear All:

I am learning C and doing a program. This program hopes to use one
function getbuf() to return two char * type. One through return
statement. Another through the argument.

The program is following:

char *getbuf(char* pC);

int main()
{
char *b, *r;

Initialise b and r with NULL.
b=getbuf(r);

You assign the returned address to b. This works.
printf("In main, b: %s \n", b);
printf("In main, r: %s \n", r);
return 0;
}


char* getbuf(char* pC)
{
pC is a copy of the uninitialised pointer r.
static char buff[]="good morning";
pC ="good afternoon";
You are overwriting the _copy_ pC but are not changing
return (char*) buff;
You return the address of buff[0]
}

However, after calling the function getbuf(), pointer b gets the value
"good morning" correctly. But pointer r cannot get the value "good
afternoon" from the function getbuf(). I thought by plugging pointer r
as the argument, r becomes the same pointer as pC in the function
getbuf(). And setting pC's value inside getbuf() will give r the same
value. But r never get the value "good afternoon".
Could you kindly help me out? This has driven me crazy. Thank you very
much.

If you want to change something in a function, you have to pass its
address. I.e.
char *getbuf(char **ppC)
{
....
*ppC = "good afternoon";
....
}
and
b = getbuf(&r);

Strings can obscure this rule to beginners but whenever you only
want to work with the "value" of the string, you pass the address
of its first element. If you want to change the address your char*
points to, you have to pass the address of the char* variable.


Cheers
Michael
 
M

Mark F. Haigh

Xiaoshen said:
Dear All:

I am learning C and doing a program. This program hopes to use one
function getbuf() to return two char * type. One through return
statement. Another through the argument.

The program is following:
<snip>

This is probably what you want:


#include <stdio.h>

char *getbuf(char **pC)
{
static char buff[] = "good morning";
*pC = "good afternoon";
return buff;
}

int main()
{
char *b, *r;

b = getbuf(&r);
printf("In main, b: %s \n", b);
printf("In main, r: %s \n", r);
return 0;
}

Which produces:

[mhaigh@icepick ~]$ ./foo
In main, b: good morning
In main, r: good afternoon
[mhaigh@icepick ~]$


When you want to change an argument's value, you need to pass the
address of the argument. Otherwise, all you change is the copy of pC
that getbuf receives (which is then discarded when the function
returns). Note that returning a static buffer can be considered bad
form if you or the users of your code have to deal with threads (which
are not on topic here).


---- ---
| pC | -----------> | r | -----1---> ?
---- ---\
\
\
\---2---> "good afternoon"


Modifying *pC causes ---1---> to switch to ---2--->, because you're
really modifying 'r', and causing it to point to "good afternoon".

Does that make any sense?


Mark F. Haigh
(e-mail address removed)
 
X

Xiaoshen Li

Thank you so much. Yes, it works. But I still don't understand the two
lines:
char *getbuf(char **pC)
b = getbuf(&r);

Suppose r(a pointer, also is a variable) is at memory location 1000 and
pointing to memory location 100, which can store characers. b =
getbuf(&r) is passing memory address 100 as argument. I know char **pC
is a pointer pointing to a pointer which points to a memory location for
characters. I don't understand when the function getbuf(char **pC) gets
the memory address 100, what does it do in detail?

Suppose:
r pointer:
---- ----
| r | --------> | |
---- ----
addr:1000 addr:100(=&r) value: =*r, will be some characters.
value: mem. addr,
which is 100.

pC(pointer to pointer):
---- --- -----
| pC | -----------> | A | --------> | B |
---- --- -----
addr: suppose 80 addr: 50(=&pC) addr: suppose 11(=&&pC or &*pC????)
value: addr. of A,
suppose 50 value: *pC(Isn't suppose 11?!)


According to your figure provided, I guess the compiler copies the
address location 100 and change the memory address of A from 50 to
100(Could you explain in detail?). Inside the function, when I change
*pC value( the value in memory address 100), *r(same, the value in
memory address 100) is changed too. I don't understand why the argument
type needs to be pointer to pointer type. It seems B (in my pC figure
above) is never involved with anything. If the argument is only pointer
type (not pointer to pointer type), why it cannot take the memory
address 100?

Thank you very much.
Xiaoshen said:
Dear All:

I am learning C and doing a program. This program hopes to use one
function getbuf() to return two char * type. One through return
statement. Another through the argument.

The program is following:

<snip>

This is probably what you want:


#include <stdio.h>

char *getbuf(char **pC)
{
static char buff[] = "good morning";
*pC = "good afternoon";
return buff;
}

int main()
{
char *b, *r;

b = getbuf(&r);
printf("In main, b: %s \n", b);
printf("In main, r: %s \n", r);
return 0;
}

Which produces:

[mhaigh@icepick ~]$ ./foo
In main, b: good morning
In main, r: good afternoon
[mhaigh@icepick ~]$


When you want to change an argument's value, you need to pass the
address of the argument. Otherwise, all you change is the copy of pC
that getbuf receives (which is then discarded when the function
returns). Note that returning a static buffer can be considered bad
form if you or the users of your code have to deal with threads (which
are not on topic here).


---- ---
| pC | -----------> | r | -----1---> ?
---- ---\
\
\
\---2---> "good afternoon"


Modifying *pC causes ---1---> to switch to ---2--->, because you're
really modifying 'r', and causing it to point to "good afternoon".

Does that make any sense?


Mark F. Haigh
(e-mail address removed)
 
X

Xiaoshen Li

Thank you so much. I think I figured out already.

I studied the swap(int *a, int *b) example.

swap(int *a, int *b)

calling by swap (&aInt, &bInt);


If I think string as pointer type, now I understand why it needs pointer
to pointer type argument(actually it is pointer to string type). So it
is one level more complicated than swaping two intergers.
 
E

Eric Sosman

Michael said:
Xiaoshen said:
[...]
char *b, *r;

Initialise b and r with NULL.
b=getbuf(r);

There's clearly a problem with r, but there's no reason
to initialize b merely to overwrite it immediately with the
function result. Also, the problem with r (see up-thread
for Xiaoshen Li's code) has nothing to do with initialization;
initializing r will not solve it.

Some people recommend initializing all pointers to NULL
at declaration, on the grounds that it's dangerous to have
uninitialized pointers lying about. I believe the practice
is misguided, because it doesn't address the underlying bug,
namely, that the program expects the pointer to point at
something but fails to make sure that it does. A pointer
that is supposed to indicate "next empty spot in buffer" is
just as wrong if it's NULL as if it were indeterminate. All
you gain by initializing to NULL is a certain amount of run-
to-run predictability in the failure symptom.

There's much to be said for repeatability; "Heisenbugs"
can be brutally hard to find. But consider what you're giving
away in the cause of repeatability: The initialization utterly
defeats the compiler's attempt to diagnose your use of a variable
into which no value has been stored! Most compilers analyze the
data flow as part of their optimization efforts, and can easily
warn about the use of uninitialized variables -- but if you
make a practice of initializing them "just in case," the compiler
cannot help you.

Try this erroneous program with "gcc -Wall -O":

#include <stdio.h>
int main(void) {
char *p;
puts (p);
return 0;
}

gcc spots the error and warns you about it. Now initialize p to
NULL and try again: the program is still wrong and still produces
undefined behavior, but gcc accepts it silently. It is surely
better to fix a bug than to mount a half-hearted defense against
it, and when the defense actually impedes the diagnosis ...

Initialize things when the program's own logic requires them
to be initialized. Don't initialize things "just in case;" it
only throws a blanket over unsolved problems.
 
D

Darklight

Mark said:
Xiaoshen said:
Dear All:

I am learning C and doing a program. This program hopes to use one
function getbuf() to return two char * type. One through return
statement. Another through the argument.

The program is following:
<snip>

This is probably what you want:


#include <stdio.h>

char *getbuf(char **pC)
{
static char buff[] = "good morning";
*pC = "good afternoon";
return buff;
}

int main()
{
char *b, *r;

b = getbuf(&r);
printf("In main, b: %s \n", b);
printf("In main, r: %s \n", r);
return 0;
}

Which produces:

[mhaigh@icepick ~]$ ./foo
In main, b: good morning
In main, r: good afternoon
[mhaigh@icepick ~]$


When you want to change an argument's value, you need to pass the
address of the argument. Otherwise, all you change is the copy of pC
that getbuf receives (which is then discarded when the function
returns). Note that returning a static buffer can be considered bad
form if you or the users of your code have to deal with threads (which
are not on topic here).

#include<stdio.h>
char* getbuf(char *pC);

int main()
{
char *b, *r;

b=getbuf(&r);
printf("In main, b: %s \n", b);
printf("In main, r: %s \n", r);
return 0;
}


char* getbuf(char *pC)
{
static char buff[]="good morning";
*pC ="good afternoon";
return (char*) buff;
}

why dose this still work
 
M

Michael Mair

Eric said:
Michael said:
Xiaoshen said:
[...]
char *b, *r;


Initialise b and r with NULL.
b=getbuf(r);

There's clearly a problem with r, but there's no reason
to initialize b merely to overwrite it immediately with the
function result. Also, the problem with r (see up-thread
for Xiaoshen Li's code) has nothing to do with initialization;
initializing r will not solve it.

I agree with the former: Initialising a variable by default
even if the "real" definition follows in the next couple of lines
with obviously no use in between is completely unnecessary
and, as you show below, can even hide errors.
If, however, the definition follows only comparatively "late"
or the variable is passed as function argument, I definitely
want to have an initialisation to a sensible value. So, even
if we change the signature and workings of getbuf() such
that we pass &r, I still would want to have r initialised to
NULL.
Some people recommend initializing all pointers to NULL
at declaration, on the grounds that it's dangerous to have
uninitialized pointers lying about. I believe the practice
is misguided, because it doesn't address the underlying bug,
namely, that the program expects the pointer to point at
something but fails to make sure that it does. A pointer
that is supposed to indicate "next empty spot in buffer" is
just as wrong if it's NULL as if it were indeterminate. All
you gain by initializing to NULL is a certain amount of run-
to-run predictability in the failure symptom.

There's much to be said for repeatability; "Heisenbugs"
can be brutally hard to find. But consider what you're giving
away in the cause of repeatability: The initialization utterly
defeats the compiler's attempt to diagnose your use of a variable
into which no value has been stored! Most compilers analyze the
data flow as part of their optimization efforts, and can easily
warn about the use of uninitialized variables -- but if you
make a practice of initializing them "just in case," the compiler
cannot help you.

Try this erroneous program with "gcc -Wall -O":

#include <stdio.h>
int main(void) {
char *p;
puts (p);
return 0;
}

gcc spots the error and warns you about it. Now initialize p to
NULL and try again: the program is still wrong and still produces
undefined behavior, but gcc accepts it silently. It is surely
better to fix a bug than to mount a half-hearted defense against
it, and when the defense actually impedes the diagnosis ...

That's a really nice example.

Initialize things when the program's own logic requires them
to be initialized. Don't initialize things "just in case;" it
only throws a blanket over unsolved problems.

Yes -- and no: Even though the example is quite convincing on the
first glance, I still prefer the "repeatable bug".
Especially if I have many "layers" of functions, I can easily
find the culprit passing in a null pointer. I can ward against
null pointers but I cannot have safeguards against uninitialised
pointers.
I have had enough trouble with other people's functions which
worked erroneously "nearly always" and will happily wade through
a long list of splint/PCLint warnings if necessary.

Thus the rule of thumb from above: Obviously unnecessary
initialisations can be avoided; whenever we have first definitons
in different control flow branches or a potential uninitialised use
of the variable/object, an initialisation is mandatory. If there
are more than a few lines of code between declaration and first
definition, I definitely prefer the initialisation to happy surprises
later on, if someone (including myself at a later time) "forgets"
about the "uninitialised" state.


Cheers
Michael
 
C

Christopher Benson-Manica

Eric Sosman said:
Initialize things when the program's own logic requires them
to be initialized. Don't initialize things "just in case;" it
only throws a blanket over unsolved problems.

<suggestion seriousness="25%">

One could, if one were so inclined, arrange to have one's cake with
the ability to eat it later if desired...

#ifdef INITIALIZE_POINTERS
#define DECLARE_PTR( type, identifier ) type *identifier=NULL;
#else
#define DECLARE_PTR( type, identifier ) type *identifier;
#endif

Not that I'm advocating such things, mind you.
 
M

Mike Wahler

Christopher Benson-Manica said:
<suggestion seriousness="25%">

One could, if one were so inclined, arrange to have one's cake with
the ability to eat it later if desired...

#ifdef INITIALIZE_POINTERS
#define DECLARE_PTR( type, identifier ) type *identifier=NULL;
#else
#define DECLARE_PTR( type, identifier ) type *identifier;
#endif

Not that I'm advocating such things, mind you.

As the messages in this thread show, opinions do differ
about this issue. As for myself, I've long ago acquired
the habit of initializing everything, always. I've not
had any problems with that approach. Reproducibility
(of errant behavior) is important to me. Of course
uninitialized objects aren't the only possible contributors
to this behavior, but it's easy to eliminate them,
so I do. :)

$.02,
-Mike
 
M

Malcolm

Xiaoshen Li said:
int main()
{
char *b, *r;

b=getbuf(r);
printf("In main, b: %s \n", b);
printf("In main, r: %s \n", r);
return 0;
}


char* getbuf(char* pC)
{
static char buff[]="good morning";
pC ="good afternoon";
return (char*) buff;
}

However, after calling the function getbuf(), pointer b gets the value
"good morning" correctly. But pointer r cannot get the value "good
afternoon" from the function getbuf(). I thought by plugging pointer r as
the argument, r becomes the same pointer as pC in the function getbuf().
And setting pC's value inside getbuf() will give r the same value. But r
never get the value "good afternoon".
Could you kindly help me out? This has driven me crazy. Thank you very
much.

This is a more idiomatic way of writing the program.

int main(void)
{
char *b;
char r[256];

b=getbuf(r);
printf("In main, b: %s \n", b);
printf("In main, r: %s \n", r);
return 0;
}

char *getbuf(char *pC)
{
static char buff[] = "Good morning";
strcpy(pC, "Good afternoon");
}

The problem is that C will not manage space for you. You get the illusion
that it does because string literals (stings in quotes) are embedded into
read-only memory. However you normally need to specify your string working
space explicitly.
 
O

Old Wolf

Xiaoshen said:
Thank you so much. Yes, it works. But I still don't understand the
two lines:
char *getbuf(char **pC)
b = getbuf(&r);

Suppose r(a pointer, also is a variable) is at memory location 1000
and pointing to memory location 100, which can store characers. b =
getbuf(&r) is passing memory address 100 as argument. I know char
**pC is a pointer pointing to a pointer which points to a memory
location for characters. I don't understand when the function
getbuf(char **pC) gets the memory address 100, what does it do
in detail?

Suppose:
r pointer:
---- ----
| r | --------> | |
---- ----
addr:1000 addr:100(=&r) value: =*r, will be some characters.
value: mem. addr,
which is 100.

&r is not 100. &r means the address of r, which is 1000 in this
example. &r is completely unrelated to what r is pointing to.
You're treating it as "the address of what r is pointing to",
which would be written as &*r, or more simply, r.

Also, the function getbuf(char **pC) never gets the memory address
100. It could get that address by checking the value of *pC,
but it never does, it only assigns a new value to *pC.
In terms of the above picture, the getbuf() function makes "r" point
to addr 200 instead (forexample). "r" itself always stays at
addr 1000.
 
E

Eric Sosman

Michael said:
Eric said:
Initialize things when the program's own logic requires them
to be initialized. Don't initialize things "just in case;" it
only throws a blanket over unsolved problems.

Yes -- and no: Even though the example is quite convincing on the
first glance, I still prefer the "repeatable bug".
Especially if I have many "layers" of functions, I can easily
find the culprit passing in a null pointer. [...]

Not so easily, if NULL is a plausible value. Consider
passing NULL as the argument to fflush() or as the second
argument to setvbuf(), for example: these Standard functions
attribute a special, valid meaning to a NULL pointer, and
take special action when they get one.

Nothing prevents user-defined functions from using NULL
as a perfectly valid value in this way, and if you pass a
NULL by mistake, all your testing will leave some parts of
those functions un-exercised.

It might be nice if C had a special value that was invalid
for all purposes except assignment, that was guaranteed to
cause traceable trouble even if used in a comparison. It doesn't
have such a thing; in particular, NULL is not such a thing.

Tell me: Do you advocate initializing all floating-point
variables to defend against the chance that they might be
signalling NaNs?
 
M

Michael Mair

Eric said:
Michael said:
Eric said:
Initialize things when the program's own logic requires them
to be initialized. Don't initialize things "just in case;" it
only throws a blanket over unsolved problems.


Yes -- and no: Even though the example is quite convincing on the
first glance, I still prefer the "repeatable bug".
Especially if I have many "layers" of functions, I can easily
find the culprit passing in a null pointer. [...]

Not so easily, if NULL is a plausible value. Consider
passing NULL as the argument to fflush() or as the second
argument to setvbuf(), for example: these Standard functions
attribute a special, valid meaning to a NULL pointer, and
take special action when they get one.

Nothing prevents user-defined functions from using NULL
as a perfectly valid value in this way, and if you pass a
NULL by mistake, all your testing will leave some parts of
those functions un-exercised.

Yep. I consider this for non-allocating functions or functions
which have to be bullet-proof (and a defined (error) behaviour on
NULL) to be a design flaw in most cases. One certainly finds valid
applications but IMO doing without abusing or over-using NULL is
better.

It might be nice if C had a special value that was invalid
for all purposes except assignment, that was guaranteed to
cause traceable trouble even if used in a comparison. It doesn't
have such a thing; in particular, NULL is not such a thing.

But it is in many cases the nearest thing and can be easily used
in this way. I am not talking about some run of the mill test
program for comp.lang.c or a small utility but about medium-sized
and large programs.
Tell me: Do you advocate initializing all floating-point
variables to defend against the chance that they might be
signalling NaNs?

Hmmm, you got me there: All the floating point "variables"
I worked with either were members of structures which were
explicitly set after allocating storage for the structure
or were temps to read in or calculate changes on these floats.
The temps always were either initialized to a context dependent
default value -- which usually was either 0.0 or 1.0 -- or
were obviously assigned to after a couple of lines.
I have never worked with floating point variables in large
programs outside of this "frame".
However, if for example the set of valid values for a
floating point variable is an interval [a, b], then I would
initialise the variable to a value outside the interval or
to either the smallest or largest value (realised by symbolic
constant; infinity if available/reasonable[*]).


Cheers
Michael
______
[*] After suffering the strange joys of num/infinity yielding
a "0.0" which behaved exactly like a NaN but always was printed
as 0.0, I am a little bit more cautious...
 
C

Charles Richmond

Old said:
&r is not 100. &r means the address of r, which is 1000 in this
example. &r is completely unrelated to what r is pointing to.
You're treating it as "the address of what r is pointing to",
which would be written as &*r, or more simply, r.

&*r === "is this the party to whom I am speaking???"
 

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,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top