Like almost all of beginners I have problem understanding pointers.
In my experience, the usual underlying cause of problems with
understanding pointers is that they seem "special" and "magical".
The trick, then, is to realize: pointers are not special or
magical at all, in any way.
Consider an ordinary integer:
#include <stdio.h>
void silly(int x) {
x = 0;
}
int main(void) {
int i = 42;
silly(i);
printf("i is %d\n", i);
return 0;
}
The function silly() is indeed pretty silly (it does nothing useful,
setting x to 0 and then returning), but, after you get used to
by-value parameters, it is no longer surprising that it has no
effect on the variable "i" in main(). The function silly() gets
a *copy* of the value of i -- a copy of the 42, in other words --
and it changes the copy, not the original.
Pointers are not special! Pointer variables, like ordinary "int"
variables, hold values. If you change a *copy*, the original is
unchanged:
#include <stdio.h>
void silly_2(int *xp) {
xp = NULL;
}
int main(void) {
int i = 42;
int *ip = &i;
silly_2(ip);
printf("i is %d\n", i);
if (ip == &i)
printf("ip still points to i\n");
return 0;
}
Here, silly_2() gets a *copy* of the value in ip. It changes xp
-- the copy -- which affects neither ip itself, nor *ip.
On the other hand, we can make a less-silly function:
void less_silly(int *xp) {
*xp = 0;
}
and call that (instead of silly_2()) from main():
int main(void) {
int i = 42;
int *ip = &i;
less_silly(ip);
printf("i is %d\n", i);
if (ip == &i)
printf("ip still points to i\n");
return 0;
}
The call to less_silly() again gives out a *copy* of the value in
ip, but now less_silly() uses that copy (in xp) to find the place
the pointer points -- which is main()'s variable "i" -- and change
that. So less_silly() changes main()'s "i". Since less_silly()
has only a copy of the value in ip, it cannot change the value in
ip -- but it can change the value in *ip: the copy in *xp points
to main()'s "i", and the original in *ip also points to main()'s
"i".
Please, look at this piece of code, and please explain me why myswap
function doesn't work as it's supposed to do, whereas myswap2 is doing
exactly what I want it to do ...
Here you are running into something that *is* tricky, in C. While
pointers are simple (once you understand the gimmick, i.e., that
pointers are not special at all), *arrays* in C *are* special. And
the part that is most special of all is what happens when you declare
a function's formal parameter as an array type, as in myswap2()
below:
void myswap(char *pa, char *pb){
char *tmp;
tmp=pa;
pa=pb;
pb=tmp;
}
Here, myswap() does not work because it gets copies of the original
values (as always) and then changes the two copies. So pa and pb,
in myswap(), change; but those copies are then thrown away and the
changes vanish.
void myswap2(char *sw[]){
Here you get the Secret Extra-Special Feature of C.
The myswap2() function has one parameter, which is named "sw".
This part is straightforward and normal. It *looks* like sw2 has
type "array (of unknown size) of pointer to char", but this part
is *not* normal.
Whenever a formal parameter has type "array N of T", for any integer
constant N and type T, the constant N is thrown away and the formal
parameter's type is rewritten as "pointer to T". (The constant N
is thrown away, so it can even be omitted, as in this case.) This
is part of The Rule about arrays and pointers in C -- for more on
that, see, e.g., <
http://web.torek.net/torek/c/pa.html>.
In this case, it means that "sw" -- which *seems* to have type
"array ? of pointer to char" -- *really* has type "pointer to
pointer to char". In other words, the "true" type of myswap2()
is:
void myswap2(char **sw) {
char *tmp;
tmp=sw[0];
sw[0]=sw[1];
sw[1]=tmp;
}
So, now that we know that myswap2() really takes a "char **",
consider also what sw[0] "means". The subscript operator takes
one pointer, and one integer. It adds the two and then does
an indirection. The pointer here is "sw", and the integer is
zero. Adding 0 has no effect, so sw[0] "means" the same thing
as "*sw". Hence, the first three lines can be written as:
void myswap2(char **sw) {
char *tmp;
tmp = *sw;
*sw = ... the rest goes here ...
(With sw[1], things are a little less simple. Adding 1 *does* have
an effect, so this is the same as *(sw + 1). This relies on the
way arrays are laid out -- which is not surprising after all: it
just means that C's arrays rely on the way C's arrays work, so C's
arrays have to work for C's arrays to work. Well, of course they
do.)
If we stop relying on arrays, though, we can write a myswap3():
void myswap3(char **ppa, char **ppb) {
char *tmp;
tmp = *ppa;
*ppa = *ppb;
*ppb = tmp;
}
and now we can call myswap3() from main(), passing &a and &b,
or passing &A[0] and &A[1]:
int main(void){
char *a="1111", *b="2222", *A[]={"1111","2222"};
printf("1: %s, 2: %s\n", a,b); //stdout: 1: 1111, 2: 2222
myswap(a,b);
printf("1: %s, 2: %s\n", a,b); //stdout: 1: 1111, 2: 2222
//Not working :/
printf("2: %s, 2: %s\n", A[0],A[1]); //stdout: 1: 1111, 2: 2222
myswap2(A);
printf("2: %s, 2: %s\n", A[0],A[1]); //stdout: 1: 2222, 2: 1111
//Here it works!
return 0;
}
Note that passing "A" is, by The Rule, the same thing as passing
&A[0]. "The Rule" about arrays and pointers in C tells us that
the "value" of an array object like "A", whenever we try to use
its value anyway -- such as in a function call -- is just a pointer
to the first element of the array. So myswap2(A) "means" myswap2(&A[0])
-- these are just two ways to write the same thing.