Fog Creek Software
Discussion Board




Passing args by reference.

I'm sure this one is a breezer. I'm sure my auntie can do it with her eyes closed. But for the life of me, I can't seem to understand the syntax of passing arguments by reference in C.
I have this:

main(){
  int *value;
  value = 7;
  add(value);
  printf("%i\n", value);
}

void do_something( int *value ){
  value = 8;
}


I have never, *ever* made value print "8". I tried several variations of the syntax, but it just doesn't happen. Sometimes the executable breaks, sometimes it freezes, but it never does the simplest thing: print "8".

Anyone?
(BTW, I know I'm going to be flamed BAR)

n00b
Tuesday, June 01, 2004

If by "add" you mean "do_something", then you should be doing *value = 8; and passing the address of value.

e.g.:

void main()
{
  int a = 0;
  cout << "a before doing something: " << a << endl;
  do_something(&a);
  cout << "a after doing something: " << a << endl;
}

void do_something(int* pa)
{
  *pa = 8;
}


Tuesday, June 01, 2004

main(){
  int *value;
  *value = 7;
  add(value);
  printf("%i\n", value);
}

void do_something( int *value ){
  *value = 8;
}

Green Pajamas
Tuesday, June 01, 2004

actually this must be: 
printf("%i\n", *value);

Green Pajamas
Tuesday, June 01, 2004

Oops. You haven't even allocated memory for the variable!

You need to learn about the pointers.

Green Pajamas
Tuesday, June 01, 2004

noob:
  int *value;
  value = 7;

What's at address 7?  You're setting the pointer named 'value' to point at the integer starting at the 7th byte.

Green:

  int *value;
  *value = 7;

GPF.


Tuesday, June 01, 2004

Keep it up. I'm listening.

n00b
Tuesday, June 01, 2004

And when do I know that I need
  int *value;
and not
  int value;
?

n00b
Tuesday, June 01, 2004

I had my dog, 'Him' sniff this thread for homework.  He said it didn't smell too bad.

What book are working out of?

It seems to me that you need to understand pointers.

int main(void)
{
  int i = 1;  // declare variable i and assign a value of 1
  int *p = &i; // declare a pointer to variable i

  printf("%d\n", i);  // prints the value of i
  printf("%d\n", p); // prints the address of p
  printf("%d\n", *p); // prints the value pointed to by p
}

You normally pass by reference in order to prevent data duplication or to modify what is pointed to by the passed parameter.

int main(void)
{
  int i = 1;

  func(i);
}

func(int v)
{
  //  passed by value can't modify original
  v = v + 1;
  printf("%d\n", v); // prints 2
}

==================

int main(void)
{
  int i = 1;

  func(&i); // pass the address of i

  printf("%d\n", *v);  // should print 2
}

void func(int *v) // accept the address of the passed param
{
  printf("%d\n", *v); // dereference to get the value prints 1
 
  *v = 2; // assign new value
}

Anon
Tuesday, June 01, 2004

"int a_value" allocates a block of 4-bytes (normally) somewhere in memory to hold an integer value.  &value tells you where it allocated that 4-bytes.

"int *a_pointer" allocates a pointer that can hold a pointer to any memory location, and when dereferenced will act as an integer.

"a_pointer = &a_value" assigns the memory location of the value to the pointer, so now if you dereference the pointer you get the value in a_value.

int a_value = 5;
int *a_pointer;
a_pointer = &a_value;
*a_pointer = 9;

a_value now holds the value you 9 instead of 5. This is the principal behind maleable arguments. Of course you can make maleable arguments in most current C++ implementatoins with a literal reference type.

int myFunction(int &myReferenceType);

Dennis
Tuesday, June 01, 2004

Nope, this is not homework. But although I think I understand the indirection thing of pointers, the syntax makes me roll my eyes in rage.

n00b
Tuesday, June 01, 2004

Look up the 'right-left rule' and read up on what pointers actually are.

i.e.

const int *ptr    //is  'a pointer to a constant int'

so, what is the pointer pointing to?  Nothing!

int x = 7;
int *ptr = x;

printf("%i",*ptr)  //should print 7, look up 'dereferencing'

I am a C newb too, it took me a long time to get pointers and I  still make a lot of mistakes with arguments that take things like 'a pointer to an array of struct FOO'

Sassy
Tuesday, June 01, 2004

>> "printf("%d\n", *v);  // should print 2" <<

This last printf in the last main function should be:

printf("%d\n", i);  // should print 2

Anon
Tuesday, June 01, 2004

Sassy,

"int x = 7;
int *ptr = x;

printf("%i",*ptr)  //should print 7, look up 'dereferencing'"

No, *ptr will give you an undefined value (whatever integer values exist at bytes 7-11. Actually it'll likely trigger a general protection fault and crash your app). It should be

int *ptr = &x;

However I don't use that in a tutorial sense because declaring the value and assigning it in one line can be confusing the "noobs" when you're trying to explain dereferencing at the same time as pointer assignment, yet to people new to it the declaration looks like a dereference. Much hilarity ensues.

int *ptr;
ptr = &x;

Much clearer.

Dennis Forbes
Tuesday, June 01, 2004


I think the original poster is just poking fun at my post here.

http://discuss.fogcreek.com/joelonsoftware/default.asp?cmd=show&ixPost=146457&ixReplies=59

He doesn't mean to ask any question. It's just a carefully contrived pun on my post.

Estudiantin
Tuesday, June 01, 2004

hey, that guy's right! Told you I was a newb ;-)

Sassy
Tuesday, June 01, 2004

Estudiantin:

Absolutely not.
No way.

All the answers are welcome, all comments will be read and encarved in the stone of my mind.

I am not making fun of anyone.

noob
Tuesday, June 01, 2004


Alright then, noob. I take back my accusation.

Estudiantin
Tuesday, June 01, 2004

Ok, now for extra fun, how do you write that without using * or & or int?

that's not a troll, *this* is a troll
Tuesday, June 01, 2004

Ok, now for extra fun, how do you write that without using * or & or int?
--

void do_something( unsigned short value[] )
{
  value[0] = 8 >> 16;
  value[1] = 8;
}

main()
{
  unsigned short value[2] = {0, 7};
  do_something(value);
  printf("%i\n, value[0] << 16 + value[1]");
}

Anonymouche
Tuesday, June 01, 2004

Bah it would have been easier with a long type:

void do_something( unsigned long value[] )
{
  value[0] = 8;
}

main()
{
  unsigned long value[1] = {7};
  do_something(value);
  printf("%i\n, value[0]");
}

Anonymouche
Tuesday, June 01, 2004

Okay, now do it without any {s or }s.

Alyosha`
Tuesday, June 01, 2004

Look, ma! no {}'s!

RP
Tuesday, June 01, 2004

noob, this might help:

http://pweb.netcom.com/~tjensen/ptr/pointers.htm

The thing that helped me make sense of pointers is that (spurred by the above tutorial) instead of thinking of it as passing by value vs. passing by reference, realizing that C _always_ passes by value.  But sometimes instead of an int (8) or a float (3.679), that value is a memory address (08C7B955) which can be held in a pointer.

When you pass an int or a float to a function, what you're passing is the value that variable holds, which is then picked up by the function parameter.  Likewise, when you pass a pointer, you're passing the address that pointer holds, which is picked up by a pointer in the function.

Here's a function:

void addOne(int a, int * p)
{
  a++;
  p++;
  (*p)++;  // Using parentheses for clarity
}

Whatever you pass in for "a" won't change because of the a++, because "a" is only a copy of the original variable.  Likewise, whatever pointer you pass in for "p" won't change either (i.e. it'll still contain the same address and will point to the same variable) because "p" is only a copy of the original pointer.  However, if you dereference "p", as in the third line, you still get to the same target variable because you're still using the same address; the address gets copied but the variable whose address it is is still the original.  Only the third line will have a permanent effect.

(Of course calling what the pointer contains an "address" oversimplifies, but I think it makes it easier to grasp conceptually.)

Another thing that helps is doing OOP, since objects and object reference variables work similarly to variables and pointers.

Kyralessa
Tuesday, June 01, 2004

The parenthesis aren't just for clarity: They completely change the meaning of that line.

However it should be noted that your example is a bit muddy: the p++ incremented the pointer by 4 bytes (the size of an int), and thus on the third line you aren't modifying the value pointed to, you're modifying the integer value "above it" in memory.

Dennis Forbes
Tuesday, June 01, 2004

You're right; I should have used a separate pointer or something.

Forget what I said and just read the tutorial.  :)

Kyralessa (who's not a C programmer)
Tuesday, June 01, 2004

In order to do pass a variable by reference as the OP wanted, the following will also do and is perhaps clearer.

main(){
  int value;
  value = 7;
  do_something(&value);
  printf("%i\n", value);
}

void do_something( int *value ){
  *value = 8;
}

however,  you do still need to understand pointers.  Incidentally, in C++ you can pass by reference like this:

int main(){
  int value;
  value = 7;
  do_something(value);
  cout << value;
}

void do_something( int &value ){
  value = 8;
}

The unfortunate thing here is that declaring a parameter as pass by reference in C++ uses the same operator and causes lots of confusion for beginners.

Stephen Depooter
Wednesday, June 02, 2004

The two bits of verbiage useful in understanding pointer and address syntax are:

"Addressof" and "Contents"

The & is the 'addressof' operator it takes the address of whatever it is joined to and returns that rather than the contents.

The '*' when used in syntactical code, as opposed to declarations means 'Contents' and returns the content of whatever it is joined to, regardless of the type of whatever it is joined to.

So if you want to pass the address of any variable, which is what by reference means, you use the addressof operator.  Unless the variable you are passing is itself a pointer variable in which case the variable  is the addressof whatever it is pointing at.

int *i;
int j;
*i = &j;

printf("Printing the same number twice %d, %d\n",i,&j);

So, in the above example, i is a pointer to an integer and j is a variable that holds an integer.  &j gets the addressof the variable and pushes it on the stack and i is the addressof (one hopes), an integer value.

Simon Lucy
Wednesday, June 02, 2004

Now, please help me here.

I want to read a string from the user and store it in a struct. What is the difference between declaring the struct field like this:
    char *name;
and like this:
    char name[51];
?

noob
Wednesday, June 02, 2004

char *name;

Is simply a pointer to some char sized memory.  It will occupy just the size of the pointer in your struct.  Using an array will allocate that amount of memory within your struct.

You can, to a degree, treat an array name as a pointer to the array, just passing its name would be equivalent to passing a pointer to the space.  Older compilers used to do the same if you passed the zeroth element of the array, name[0].  Don't use that syntax though its not guaranteed to work.

If you use the pointer, char *name, then you'll have to allocate space as in:

name = malloc(51);

You also have to check the return value from malloc to make sure its not equal to NULL, in the unlikely but possible case of running out of memory.

The important thing to realise is that the memory you have allocated is pointed at by *name but is NOT included in your struct.  The actual memory space is somewhere on the heap, you care not where, because your pointer stores its location.

However, you can't store on some disk the pointer and expect the next time you run the program to get back the same data,  The validity of the pointer and the address is true only for so long as that memory is allocated.  When your program goes away, so does the allocation (even if the physical memory isn't overwritten).

That allocation does not go away, however, if you exit or leave that function.  Allocated memory is within the scope of the running application (bar complications such as thread safe processing which we'll leave out of this), up until the time it is freed.

Even though the allocation of memory and its contents won't go away because your function scope changes, the variable you're using to point at it and store its address may do.

If you declare a struct within a function its scope will end when that function's scope ends.  If your char *name is within that struct then its own address becomes meaningless once that function  is exited.  You may pass the pointer to other functions within the life of the function but you can't treat it as a static variable.

I'd recommend reading The C Programming Language by Kernighan and Ritchie, all meaningful statements on C are there.

Simon Lucy
Wednesday, June 02, 2004

Actually I'm working on this post as I read the K&R book. But sometimes I need someone holding my hand, and since there's nobody around where I'm at.... :)

Please, bear with me. You're all being extremelly helpfull.

noob
Wednesday, June 02, 2004

By the way, is it legal to have char *name[50]  ? Or do I have to exclusively use char *name or char name[50]? And what's the difference between the 3 of them?

noob
Wednesday, June 02, 2004

char *name[50] is an array of 50 pointers to char.

char *name; Is a pointer to char.

char name[50]; Is an array of char.

Simon Lucy
Wednesday, June 02, 2004

So, having char *name[50] is the equivalent of having 50x char *name, right?

RP
Wednesday, June 02, 2004

How can you have 50 times the same variable?

noob
Wednesday, June 02, 2004

"int *i;
int j;
*i = &j;"

Okay...tell me that you people are just trying to ensure job security by screwing up all of the prospective programmers...

Dennis Forbes
Wednesday, June 02, 2004

Please.
I really need to get this sintax clear. Posting random fragments of code is only going to make me more confused.

noob
Wednesday, June 02, 2004

noob, with all those comments, you still don't know?
OK, I'll give it a shot.

There are two entities here -- an 'int', and a 'pointer to an int'.  I'll put how to say this in quotes.

int MyValue;              // "Int MyValue"
int * AddrOfMyValue; //  "Integer Pointer AddrOfMyValue"

AddrOfMyValue = & MyValue; 
// "AddrOfMyValue is given the address of MyValue"
// or, "AddrOfMyValue holds the ADDRESS of MyValue"
// or, "AddrOfMyValue is a POINTER TO MyValue"

An 'int' has to be somewhere in the machine.  The first 'int MyValue' actually allocates memory for the integer.  The second 'int *' entry merely allocates a holder for an address to an integer.  The assignment statement then gives the Pointer variable the address of the actual int.

So, 'C' gives us several operators to work with these two entities -- the thing, and the address of (pointer to) the thing.

MyValue = 5;            // make the value of MyValue == 5
MyPtr = & MyValue;  // Give MyPtr the address of MyValue
*MyPtr = 6;              // "The Contents of MyPtr" (ie MyValue) is now equal to 6.  aka 'DeReference' operator.


Now, in 'C', if you want to pass something, have it changed in the subroutine, and get the altered thing back, you can only pass an address of the thing.

So:

main()
{
  int MyValue;  // " int MyValue"
  int *value;    //  " an Integer Pointer value"

  value = & MyValue;
  MyValue = 7;
  // (OR, *value = 7;) // "Contents Of 'value' set to 7"
  add(value);  // Here, we are passing an ADDRESS, as we should.
  printf("%i\n", *value);  // Here, we must 'dereference the pointer' again, to get 'the contents', an actual value.
}

void do_something( int *value )
{  // Here, we are receiving an ADDRESS, as we should
  *value = 8; // "The CONTENTS of value is set to 8"
                    // Since 'value' is an ADDRESS, we must
                    // 'DeReference' it to get to the actual memory
                    // where values can be stored.
 
}

In a declaration:
"void do_something(int * value)"

the use of '*' is different.  In a declaration, the 'int * value' is said:
"do_something is passed an ADDRESS OF an int", or "do_something is passed a POINTER TO an int".

In the body of the function:

int * MyPtr;

the '*' operator indicates a 'Pointer To' the thing, aka 'the address of' the thing.

*MyPtr = 5;  // the '*' operator here indicates 'The Contents'.

This does assume that the 'MyPtr' has been set to a valid address of an int to stick the 5 into.  If not, the default is zero, and trying to stick an int into the zero location of your computer will usually cause your operating system to complain, and your code to crash.

AllanL5
Wednesday, June 02, 2004

>By the way, is it legal to have char *name[50]  ? Or do
> I have to exclusively use char *name or char name[50]?
> And what's the difference between the 3 of them?

I need to define 'null' here.  'null' is the default state of a just defined pointer -- and it is zero.  This means a newly defined pointer points to the zeroth location of your computer -- until you point it somewhere else.

Having said that, again, this is a question of space allocated to hold the data. 

char MyName[50];
// This allocates 50 'char' bytes of space, and makes 'name' a POINTER TO the zeroth location of that space(aka a 'C' string).  All this is done through the 'C' array handling semantics. 

char * MyPtr;  // This merely allocates a null pointer.  It is expected you'll point this to a 'string' eventually.

char * name[50];  // This actually allocates 50 null pointers to a 'char *' thing, all of which are initially null. 

AllanL5
Wednesday, June 02, 2004

lol
Thanks to you all for the help. I'm going to close this thread. What I really need is an idiots guide to pointers. I'm studying this all by myself and I still don't know a couple of things. But, oh yes, this thread has opened my eyes.

noob
Wednesday, June 02, 2004

About half of the help provided in this threat has been incorrect.

.
Wednesday, June 02, 2004

Is that why you called it threat and not thread? ;)

Come on guys. Saying half the thread is wrong isn't helping me at all, only making me more confuse. I don't want to insult you, but you really should post the correct answers instead of just saying "it's wrong".

noob
Wednesday, June 02, 2004

I made one error in an example.

It should have been i = &j;

That's what comes of changing an example on the fly to make it clearer.

Simon Lucy
Wednesday, June 02, 2004

"What I really need is an idiots guide to pointers."

Did you read the tutorial I linked to?

Kyralessa
Wednesday, June 02, 2004

Going through it right now. Chapter 2 finally explained to me why pointers and arrays are so close to each other.

noob
Wednesday, June 02, 2004

I don't do C. In Perl references are pretty darn simple and in PHP they are convoluted. But never, never (except in one instance where I berated myself) would I modify arguments passed to a function. I use those arguments to do something or get something and then I either modify a KNOWN global variable or I return a reference to a value. Am I wrong here? I do recall someone asked me something just like this in an interview and I'm thinking ... man ... I'd just never ever do that and I did not pursue the job at that place because they asked me this question.

me
Wednesday, June 02, 2004

There are two reasons why you'd ever want to use pass-by-reference in C.  One, you're manipulating a string; there's no other way to access it except as a char*.  Two, you're returning multiple values.  In perl, this is easy; you can return an array.  In C, you would need to typedef a new struct for the return value.

Alyosha`
Wednesday, June 02, 2004

Not being a master in pointers myself, I found that using this description of the righ-left rule helped me out a lot.

http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html

RP
Wednesday, June 02, 2004

There is a third reason why you'd want to use a pass-by-reference: To avoid the copy overhead of passing some large data item like a structure by value.

Ian
Wednesday, June 02, 2004

Wow. I really thought Joel was exaggerating when he wrote about how hard it can be to "get" pointers.

But after reading this thread, I concede. Joel is absolutely right.

And a note for noob: There really have been a whole lot of mistakes and pieces of mis-information in this thread. Much of it has been a bit like the blind leading the blind.

The soundest advice I can offer (maybe too late) is buy or borrow a good tutorial-style book and work through it doing the examples.

Ian
Wednesday, June 02, 2004

"'null' is the default state of a just defined pointer -- and it is zero.  This means a newly defined pointer points to the zeroth location of your computer -- until you point it somewhere else."

This is incorrect and such an assumption is one of the most common sources for bugs difficult to track.

__ in C variables are not initialized until they have been assigned a value explicitly __ Until then they point at a random place in memory.

Regards,

chance

chance
Friday, June 04, 2004

Chance, according to the standard, integer variables in C are automatically initialized to 0, and pointer variables are initialized to NULL.

RP
Friday, June 04, 2004

Sure, NOW pointers are initialized to zero.  One of the gotcha's of 'C' has always been 'What Version (and when was it made) are you Using?'

K&R 'C', the original 1978 version of the language, was in fact silent on this.

As of 1985, I have a reference book that states that a newly created pointer could have any value.  I don't know WHEN the behavior was specified that a newly created pointer will be set to null by the 'C' environment.

I don't depend on this behavior, by the way.  In fact, I try to depend as little as possible on 'side-effect' issues -- like whether a newly created pointer variable is initialized or not.

AllanL5
Friday, June 04, 2004

In which standard are they initialised?

comp.lang.c FAQ: Q1.30:

"What can I safely assume about the initial values of variables which are not explicitly initialized?

Variables with automatic duration start out containing garbage, unless they are explicitly initialized.References: K&R1 Sec. 4.9 pp. 82-4
K&R2 Sec. 4.9 pp. 85-86
ANSI Sec. 3.5.7, Sec. 4.10.3.1, Sec. 4.10.5.3
ISO Sec. 6.5.7, Sec. 7.10.3.1, Sec. 7.10.5.3
H&S Sec. 4.2.8 pp. 72-3, Sec. 4.6 pp. 92-3, Sec. 4.6.2 pp. 94-5, Sec. 4.6.3 p. 96, Sec. 16.1 p. 386"

That looks like a lot of standards say that... is this in C9X?

Katie Lucas
Friday, June 04, 2004

*  Recent Topics

*  Fog Creek Home