[comp.lang.c] SIMPLE malloc & pointer question

jdm5548@diamond.tamu.edu (James Darrell McCauley) (08/07/90)

I have the following code:
---cut here
#include<stdio.h>
#include<malloc.h>
main()
{
  int *a,*b;

  b=(int *)malloc( (unsigned) 4*sizeof(int));
  b[2]=5;
  printf("main(): b[2]=%d\n",b[2]);
  inita(a,b);
  printf("main(): a[2]=%d\n",a[2]);
}

inita (a,b)
int a[],b[];
{
  a=(int *)malloc( (unsigned) 4*sizeof(int));
  a[2]=3;
  printf("inita(): a[2]=%d\n",a[2]);
  printf("inita(): b[2]=%d\n",b[2]);
}
---cut here
which produces the following output:

main(): b[2]=5
inita(): a[2]=3
inita(): b[2]=5
Segmentation fault (core dumped)

Could some kind guru point out the error? Thank you very much.
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
James Darrell McCauley                 jdm5548@diamond.tamu.edu
Dept of Ag. Engineering                          (409) 845-6484
Texas A&M University, College Station, Texas 77843-2117, USA
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

jdm5548@diamond.tamu.edu (James Darrell McCauley) (08/07/90)

I forgot to add this, in case the problem is not just my own
ignorance:  SUN 4.0.3 using /bin/cc
--- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
James Darrell McCauley                 jdm5548@diamond.tamu.edu
Dept of Ag. Engineering                          (409) 845-6484
Texas A&M University, College Station, Texas 77843-2117, USA
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

burley@world.std.com (James C Burley) (08/07/90)

I think the problem is that you're expecting inita to return the pointer it
allocated for <a>, but that doesn't happen.  main passes to inita the
current values for pointers <a> and <b>.  inita immediately overwrites its
own LOCAL COPY (as always in C) with the address of allocated memory, then
writes through that address in "a[2]=3;".  Then it returns to main.

Now, main still has the old (uninitialized) value of <a>, so when it tries
to read through that address, anything (including a segment violation) can
happen.  Even a random number getting output.  Meanwhile, the pointer to
inita's heap-allocated area has been lost forever, since it was kept only
in <a>, which is now popped off the stack (ok, it's probably still there
somewhere, but not after the next function call...).

Try something like this instead:

inita(&a,b);  /* Call inita, a is input/output arg, b is input only. */
...
inita(a,b)
int *a[];
int b[];
{
*a = (int *) malloc...
*a[2] = 3;
printf(...*a[2]);
...
}

I might have the precedence wrong -- too zonked to be sure without further
playing -- but I hope you get the idea.  Here, inita is using indirection
through a local copy of a pointer to main's (pointer to) <a>, so it can modify
main's copy of <a>.  It still does basically the same thing except that after
returning, the pointer to the heap-allocated area is still present in main's
copy of <a>, and thus your program would work.  Unless you need to say
"(*a)[2] = 3;" and so on, in which case excuse my sloppiness, please!

James Craig Burley, Software Craftsperson    burley@world.std.com

diamond@tkou02.enet.dec.com (diamond@tkovoa) (08/07/90)

In article <7206@helios.TAMU.EDU> jdm5548@diamond.tamu.edu (James Darrell McCauley) writes:

 >main() {
 >  int *a,*b;
Initially, a and b are NULL pointers (do not point to anything usable).
 >  b=(int *)malloc( (unsigned) 4*sizeof(int));
Now b points to some storage that the program can use, and a is still null.
 >  inita(a,b);
Now b still points to some storage that the program can use, and a is still
null.  The null pointer in a was copied to inita's first argument.  The
useful pointer in b was copied to initb's second argument.  Nothing was
copied back.  C function calls are call-by-value, not call-by-value-return,
not call-by-reference.
 >}
 >inita (a,b) int a[],b[]; {
 >  a=(int *)malloc( (unsigned) 4*sizeof(int));
This changes one of inita's variables from null to a useful pointer.
Unfortunately, no one gives the useful pointer back to main.
 >}

(Note to the FAQ list maintainer:  Does the FAQ list mention call-by-value?)
-- 
Norman Diamond, Nihon DEC     diamond@tkou02.enet.dec.com
This is me speaking.  If you want to hear the company speak, you need DECtalk.

pierre@rhi.hi.is (Kjartan Pierre Emilsson) (08/07/90)

From article <7206@helios.TAMU.EDU>, by jdm5548@diamond.tamu.edu (James Darrell McCauley):
> I have the following code:
> main()
> {
>   int *a,*b;
> 
>   b=(int *)malloc( (unsigned) 4*sizeof(int));
>   b[2]=5;
>   printf("main(): b[2]=%d\n",b[2]);
>   inita(a,b);
>   printf("main(): a[2]=%d\n",a[2]);
> }
> 
> inita (a,b)
> int a[],b[];
> {
>   a=(int *)malloc( (unsigned) 4*sizeof(int));

	...stuff deleted...
> }
> which produces the following output:
> 
	... some output ...

> Segmentation fault (core dumped)
> 

Arguments to function in C are passed by value, which means that a function
cannot alter the content of variables passed to it, but to do so you must pass
the *adress* of the variable you want to change (the pointer to the memory
location of the data you want to change).  A correct version of inita() would
thus be:

		inita(a,b)
		int **a,*b;
		{
			*a = (int *)malloc(4*sizeof(int));
			
		(*a)[2] = 1.0;
		}

Which should be called as inita(&a,b).

-------------------------------------------------------------------------------
Kjartan Pierre Emilsson
Science Institute of The University of Iceland
Dunhaga 3
Reykjavik
Iceland    								email: pierre@krafla.rhi.hi.is

richard@aiai.ed.ac.uk (Richard Tobin) (08/07/90)

In article <7206@helios.TAMU.EDU> jdm5548@diamond.tamu.edu (James Darrell McCauley) writes:

>  inita(a,b);
>  printf("main(): a[2]=%d\n",a[2]);

>inita (a,b)
>int a[],b[];
>{
>  a=(int *)malloc( (unsigned) 4*sizeof(int));
>  a[2]=3;

Arguments in C are passed by value, not reference.  inita() gets a copy
of main()'s variable "a", so when inita() returns the value in main()
is unchanged.

If you want to modify a variable in the parent procedure, you should
pass a pointer to it:

  inita(&a,b);

and declare the argument appropriately:

  int **a;			/* a is a pointer to a pointer to integers */

and assign to what the argument points to, not the argument itself

  (*a)=(int *)malloc( (unsigned) 4*sizeof(int));
  (*a)[2]=3;

-- Richard

-- 
Richard Tobin,                       JANET: R.Tobin@uk.ac.ed             
AI Applications Institute,           ARPA:  R.Tobin%uk.ac.ed@nsfnet-relay.ac.uk
Edinburgh University.                UUCP:  ...!ukc!ed.ac.uk!R.Tobin

ping@cubmol.bio.columbia.edu (Shiping Zhang) (08/07/90)

In article <7206@helios.TAMU.EDU> jdm5548@diamond.tamu.edu (James Darrell McCauley) writes:
>I have the following code:
>---cut here
>#include<stdio.h>
>#include<malloc.h>
>main()
>{
>  int *a,*b;
>
>  b=(int *)malloc( (unsigned) 4*sizeof(int));
>  b[2]=5;
>  printf("main(): b[2]=%d\n",b[2]);
>  inita(a,b);
>  printf("main(): a[2]=%d\n",a[2]);
>}
>
>inita (a,b)
>int a[],b[];
>{
>  a=(int *)malloc( (unsigned) 4*sizeof(int));
>  a[2]=3;
>  printf("inita(): a[2]=%d\n",a[2]);
>  printf("inita(): b[2]=%d\n",b[2]);
>}
>---cut here
>which produces the following output:
>
>main(): b[2]=5
>inita(): a[2]=3
>inita(): b[2]=5
>Segmentation fault (core dumped)
>

Remember that in C, function arguments are passed by value and
their original values are not changed no matter what operation
has been applied to them in the called functions. So in inita(),
a is assigned a value (the address of some memery location), but
the value is not saved when return from that function and a points
to nowhere or anywhere.
The simplest way to solve this problem in this case is just to move
the first statement of inita() to somewhere in the main function
before inita is called.

-ping

mcdaniel@adi.com (Tim McDaniel) (08/07/90)

diamond@tkou02.enet.dec.com (diamond@tkovoa) writes:

   In article <7206@helios.TAMU.EDU> jdm5548@diamond.tamu.edu (James Darrell McCauley) writes:

    >main() {
    >  int *a,*b;
   Initially, a and b are NULL pointers (do not point to anything usable).

K&R2, sec. A8.7, page 219:
   The initial value of an automatic object not explicitly initialized
   is undefined.

Not NULL (which is a definite value), undefined.  "a == NULL" need not
be true; in fact, it is permitted to cause a fatal fault.

In the referenced article, substitute "undefined" for "null".
--
"I'm not a nerd -- I'm 'socially challenged'."

Tim McDaniel
Internet: mcdaniel@adi.com             UUCP: {uunet,sharkey}!amara!mcdaniel

mcdaniel@adi.com (Tim McDaniel) (08/07/90)

jdm5548@diamond.tamu.edu (James Darrell McCauley) asked about setting
a function argument.  The answer has been given elsewhere (argument
values are local; changes are not passed back to the parent).  I just
have small style quibbles.

   main()
   { ...
   inita(a,b);
   ... }

   inita (a,b)
   int a[],b[];
   {
     a=(int *)malloc( (unsigned) 4*sizeof(int));
     ...
   }

I have style rules to avoid confusion in the reader and to avoid bugs:
* I declare before use, even if the compiler seems to let me get away
  with it.
  inita was not a problem only because it returns type int.
* I don't try to lie to the compiler.
  If a function doesn't return a value, I define it "void".
  If a function is taking a pointer as argument, I say so, because you
  can't pass arrays as arguments in C.

Inita falls under both these points.

So I would have written

   void inita (a,b)
   int *a, *b;
   { ... }

   main ()
   { ... }

--
"I'm not a nerd -- I'm 'socially challenged'."

Tim McDaniel
Internet: mcdaniel@adi.com             UUCP: {uunet,sharkey}!amara!mcdaniel

jrbd@craycos.com (James Davies) (08/08/90)

Just to beat this into the ground a bit further (like most simple
problems posted to the net, it's already halfway to the earth's core
by now...), the first thing to do when you have questions about
a particular program should be to run it through lint.
In this case, lint says:

	$ lint xxx.c
	xxx.c(11): warning: a may be used before set
	printf returns value which is always ignored

The first warning should be a strong clue as to what is wrong...
Is lint mentioned in the FAQ list?

george@hls0.hls.oz (George Turczynski) (08/08/90)

Well, for starters, try this code instead:

/*** Cut here ***/
#include<stdio.h>
#include<malloc.h>
main()
{
  int *a,*b;

  b=(int *)malloc( (unsigned) 4*sizeof(int));
  b[2]=5;
  printf("main(): b[2]=%d\n",b[2]);

  inita(&a,b);	/*	<<==== */

  printf("main(): a[2]=%d\n",a[2]);
}

inita (a,b)
int *a[],b[];	/*  <<====  */
{
  *a= (int *)malloc( (unsigned) 4*sizeof(int));	/*  <<====  */

  (*a)[2]=3;	/*  <<====  */

  printf("inita(): a[2]=%d\n",(*a)[2]);	/*  <<====  */

  printf("inita(): b[2]=%d\n",b[2]);
}

/*** Cut here ***/

It produces the output:

"scratch: a.out
main(): b[2]=5
inita(): a[2]=3
inita(): b[2]=5
main(): a[2]=3
scratch: "

Which is probably what you expected of your code ?

The reason it failed is because you passed the value of (int *)a
to inita(), and not its address.  The value was of course NULL,
but was then assigned a value by your call to malloc(), and this is
perfectly valid.  That is why a[2] was meaningful in inita().

The problem is that you altered a stack variable `a' in inita() and 
not the stack variable `a' in main(), which is what you thought you
did. When you got back into main(), `a' was still NULL, and voila,
`a[2]' will cause a SIGSEGV to be sent to the process !

I hope you can follow my altered version of your code.  There are
changes where you see the "/*  <<====  */" symbol.  The `(*a)' must
be used as the `[]' binds tighter than the `*'.  If you leave the `()'
out you will still get a segmentation violation, but for a different
reason.

I hope this has answered any questions you had.

Have a nice day...

George P. J. Turczynski.          | ACSnet: george@highland.oz 
                                  | Phone: 61 48 683490
Computer Systems Engineer.        | Fax:   61 48 683474
                                  |----------------------
Highland Logic Pty. Ltd.          | I can't speak for the
Suite 1, 348-354 Argyle Street    | company, I can barely
Moss Vale. NSW. 2577 Australia    | speak for myself...

ptb@ittc.wec.com (Pat Broderick) (08/08/90)

In article <7206@helios.TAMU.EDU>, jdm5548@diamond.tamu.edu (James Darrell McCauley) writes:
> I have the following code:
> ---cut here
> #include<stdio.h>
> #include<malloc.h>
> main()
> {
>   int *a,*b;
> 
>   ...
>   inita(a,b);
>   printf("main(): a[2]=%d\n",a[2]);
> }
> 	[stuff deleted]

The problem with the code is that "a", is initialized in function inita(), its
value is never known in main().  When you try to print a[2] you get the
SEGV.  Several solutions are possible, you might try either of the
following:

   (1) perform the malloc() for a in main()
   (2) define inita() as a function returning a pointer, e.g.

int *inita (a,b)
int a[],b[];
{
  a=(int *)malloc( (unsigned) 4*sizeof(int));
  a[2]=3;
  printf("inita(): a[2]=%d\n",a[2]);
  printf("inita(): b[2]=%d\n",b[2]);
  return a; 
}

In main() your call would then look like:

   a = inita(a,b);

Hope this helps

-- 
Patrick T. Broderick           |ptb@ittc.wec.com |
                               |uunet!ittc!ptb   |
                               |(412)733-6265    |

diamond@tkou02.enet.dec.com (diamond@tkovoa) (08/09/90)

In article <7206@helios.TAMU.EDU> jdm5548@diamond.tamu.edu (James Darrell McCauley) writes:
>>>main() {
>>>  int *a,*b;

There must have been some static (:-) affecting my mind when I replied:
>>Initially, a and b are NULL pointers (do not point to anything usable).
(But I got the rest of it right.)

In article <MCDANIEL.90Aug7105551@dolphin.adi.com> mcdaniel@adi.com (Tim McDaniel) writes:
>Not NULL (which is a definite value), undefined.
Yes, thank you.  (And he didn't even flame me.)
-- 
Norman Diamond, Nihon DEC     diamond@tkou02.enet.dec.com
This is me speaking.  If you want to hear the company speak, you need DECtalk.