"Mastering C Pointers"....

R

Roose

So, can anyone explain to me why everyone is so insistent on not
top-posting, when they don't care about the long sigs?

I mean, an explanation other than hypocrisy.
 
A

Alan Connor

I've made many such statements like that. Go back and read the posts. Note
the use of the word "pretend", and "I _know_ this is not true on many
machines, but the idea is to give him a general idea without getting bogged
down in details."


I agree it would be "well advised." However, his real goal is to learn C.
You can easily get discouraged with the details of assembly language,
especially with the tools most people have available. That is why I suggest
it is nice to learn a simple model.

I'm yakking with some folks on the asm groups.
 
R

Roose

For example, Roose said "a pointer is an integer". This is not
true. A pointer *might* be an integer, and it might be something
else, like two distinct integers, or something else entirely.

How about, "At least some bits of the binary representation of a pointer can
be interpreted as an integer." I didn't say anything about casting pointers
to integers.

Of course, that sentence is much less clear at the expense of a detail. It
doesn't get across the basic point as well.

And I didn't say (or didn't mean to say), that it _is_ an integer, just on
the hardware level. I specifically said a pointer is a C language
abstraction.
I understand that you might not see why it matters one way or
the other, but there is a good reason to be precisely correct
right from the start: it prevents you from making lazy assumptions
that might not bite you now, but that probably will bite you
later.

Have you ever heard of "speculative generality"?
I don't know why you are learning C, but lets imagine that you
might someday want to program professionally using C. If so, you
might find it professionally embarrassing when your code crashes
the airplane, cash register, medical device, file server, robot,
etc. because you assumed that a pointer is "just an integer",
as Roose urged you to.

This is a straw man. Any program, of critical nature or not, is tested, and
these types of errors don't even take testers to figure out. The code
simply won't work when you try it for the first time. If you want to make
an error-free program for one device, it is probably safer to find out the
specifics of your hardware, and program to that. Rather than coding in 100%
"standard ANSI C" and hoping the compiler does the right thing. If not,
then it will at least save you a lot of time.
Perhaps you think that's just hypothetical, that you'll worry
about the details when you know C better. Well that might work,
but on the other hand, I have seen things like that happen
(well, nothing as dramatic as a plane crash) because the
programmer didn't *really* know how C worked.

Again, this pretty much has nothing to do with the C language, and is more
of a QA question.
 
S

Sheldon Simms

And all the people that ARE, are doing nothing but confuse me.

Ok, let me try to tell you something about pointers that is (hopefully)
precisely correct, but won't confuse you.

A pointer is a kind of variable that can "point to" some object.

But what does "point to" mean in this case? It means that it is
possible to access the object by using the pointer to see where
it is.

Let me try to show how it works with an example. For example, let me
declare an integer variable:

int x; /* an integer named x */

Becase of the declaration there is an object named x that has a value
that is an integer. We can change the value of the object named x and
we can retrieve the value of the object named x:

x = 3; /* change the value of the integer named x to 3 */
printf("x == %d\n", x); /* print the value of the integer named x */

You might wonder why I'm being verbose and talking about changing "the
value of the object named x" instead of "changing x". I'm doing that
because it is possible to manipulate the object without calling it x.
The integer in memory can have many names, only one of which is "x",
although "x" could be considered the "main" or "canonical" name of
the object. For example I can also refer to the object with a pointer:

int * p; /* a pointer named p to int */
p = &x; /* make p "point to" the object named x */

Now px refers to the same object as x and I can manipulate the value
of that object by using the name "p".

*p = 5;
printf("x == %d\n", x);
printf("*p == %d\n", *p);

This will produce the output:

x == 5
*p == 5

And you can see that the value of the object named x has been altered
by using p. the object that p points to is the same object that is
named x. This can be said more succinctly "p points to x". However,
p is not the same as x. p is also the name of an object. That object
has a type (pointer to int), and a value of some kind. The value of
p "refers to" the object named x, but how it does that is of no
concern to us, all that is important is that we can manipulate that
value in certain ways which I will get to in a minute.

To try to make this clear before I talk more about the value of p,
here's a diagram that attempts to show the objects named p and x
in memory

+------------+
| (value)---|------+
+------------+ | +--------------+
+----->| 5 |
p: pointer to int +--------------+
x: int

There is an object named p, and an object named x. The object named x
has the value 5 and the object named p has a value that points to x.

Now roose said "pointers are integers". This is wrong, but it's close
to the truth. It's close to the truth because pointers act like integers
in some ways. For example, we can add an integer to them. If we add an
integer to a pointer, we get a pointer that points to the "next thing",
where thing is whatever pointer points to. To illustrate this I'll
make another diagram.

p p+1 p+2 p+3
| | | |
V V V V
+------------+------------+------------+------------+
| | | | |
+------------+------------+------------+------------+
x: int

If the pointer p points to the integer x, then p+1 points to the integer
after x, and p+2 points to the integer after that, and so on. C allows
the programmer to view memory this way. If you have a pointer to some
object, then the value of that pointer plus one is a pointer to another
object of the same type that comes right after the first one.

However, if you try this:

int x;
int * p;
p = &x;
p = p + 1;
printf("the integer after x is %d\n", *p);

Your program will probably crash. This is because there actually isn't an
object after x. We know where the object after x would be if it existed,
but since it doesn't exist, we can't access it. This is an example of
undefined behavior. The C language does not guarantee any particular
behavior and, in fact, allows pretty much anything to happen as a
result. This problem can be solved by declaring an array of int:

int x[8];

Now we know that there are 8 integers all lined up right after each other.
You may know that you can access these integers by using array notation
like this:

int i;
int x[8];

for (i = 0; i < 8; ++i)
printf("x[%d] == %d", i, x);

But you can also use pointers to do the same thing, like this:

int * p;
int x[8];

p = &x[0];
printf("x[0] == %d\n", *p);
printf("x[1] == %d\n", *(p+1));
...
printf("x[7] == %d\n", *(p+7));

And, as you may expect, you can do this more easily with a for-loop:

int * p;
int x[8];

p = &x[0];
for (i = 0; i < 8; ++i)
printf("x[%d] == %d", i, *(p+i));

Now we come to a somewhat tricky subject, which is the conversion of
arrays to pointers. This isn't really that hard to understand, but it
seems to get talked about a lot in comp.lang.c.

In most cases, if you use the name of an array like a pointer, it acts
like a pointer. There are sometimes when this is not true, so you have
to be quite careful about using this fact until you know when it is true
and when it isn't.

To get back to our example, we can change the code like this:

int * p;
int x[8];

p = x; /* same as p = &x[0] */

We have used x as a pointer and it acts like a pointer. This does *not*
copy the entire array anywhere. All it does is make p point at the first
element of x. From this point we can continue as before:

for (i = 0; i < 8; ++i)
printf("x[%d] == %d", i, *(p+i));

and everything will work the same way.
You might wonder however, if the name of an array sometimes acts like a
pointer, can we use it to replace the variable p altogether in this code?
The answer is yes:

int x[8];
for (i = 0; i < 8; ++i)
printf("x[%d] == %d", i, *(x+i)); /* using x as a pointer */

In fact, not only can you do this to access the elements of the array,
this is THE ONLY WAY to do so. This is a very important fact about C.
You are probably thinking, hey, we just used x to access elements of
the array named x, that's another way! But actually it's not another
way. That's because:

Definition:
the meaning of x is *(x+i)

Because this is true, not only does x[0] access the first element of
the array named x, 0[x] does too! This may seem strange, but look at
the definition above and then consider:

0[x] means *(0+x) equals *(x+0) means x[0]


In mathematics a+b equals b+a when one is adding integers, and the same
is true in C when you add a pointer and an integer together. In fact we
can rewrite the example loop as follows:

int x[8];
for (i = 0; i < 8; ++i)
printf("x[%d] == %d", i, i[x]); /* perverse, but valid */

Now I will tell you one last thing about pointers today: you can subtract
one pointer from another, as long as they have the same type and are
pointing to different elements of the same array. The result of doing this
tells you how many elements ahead of one pointer the other one is.
Consider the following:

int x[4];
int * p = x; /* using x like a pointer */
int * q = x+2; /* x+2 points at the thing "2 after" the first */

These declarations set up the following situation:

p q
| |
V V
+------------+------------+------------+------------+
| | | | |
+------------+------------+------------+------------+
x: 4-element array of int

You can see that p is zero "elements ahead" of itself, and q is two
"elements ahead" of p. You can find this out in a C program like
this:

int difference = q - p;
printf("q is %d elements ahead of p\n", difference);

which will print

q is 2 elements ahead of p

Now I want to remind you, that this only works when both p and q
are pointing to elements of the same array. If the code looked like
this:

int x, y;
int * p = &x;
int * q = &y;
int difference = q - p;
printf("q is %d elements ahead of p\n", difference);

Then there's no guaranteeing what will happen. p and q both point to
objects of type int, but they are not elements of a common array.
The behavior of this program is undefined. We may find out how many
integer objects can fit in the space between wherever the compiler puts
the object named x and wherever the compiler puts the object named y, but
the C compiler is not required to do this, it could format our hard disk
instead. -- harsh, to be sure, but allowed by the C standard.

To finish up, I will present a function using some of these concepts.
The function will take one argument, a string, and will return the length
of the string. A string in C is an array of char containing values
representing characters. The array has at least as many elements as
characters in the string, plus one. The element of the array after the
last character in the string has the value 0, to mark the end of the
string. A character with the value 0 is called a NUL. In C, you can
just write it like a normal zero (i.e, 0), but many people choose to
show that it is a character by writing it inside ' quotes like this:
'\0'. The backslash is necessary to make this a character with value
zero, as opposed to the character which is used to print a zero on
your screen, which is written '0'.

A string is usually manipulated in C by using a pointer to the
first element of the array. A diagram should make this clear.

+------------+
| (value)---|------+
+------------+ | +----+----+----+----+----+---+
+----->| 72 | 69 | 76 | 76 | 79 | 0 |
p: pointer to char +----+----+----+----+----+---+
72=H 69=E 76=L 76=L 79=O

Here is a string containing the text "HELLO". Each object of type
char in the array has a numeric value. Each number corresponds to
some character. The mapping from number to character can be done in many
different ways, and that fact can be very important when writing programs
for international audiences. The mapping used above is a very common
mapping though, used in the US and Europe most of the time these days.

In any case, we use a pointer to char to access our string, which is
an array of elements of type char. The function to be presented will not
declare the array itself, it will assume that the array has already been
declared, but that does not change the fact that the array *must* be
declared somewhere. We can only use a pointer that points to some object
that really exists in memory, as I said way up above.

The function to be presented doesn't care what the values in the elements
of the array are, it will just look for the value 0, which is the last
element of the string. So here's the function, at first, line-by-line.

unsigned int
/* the length of a string can't be negative, so return unsigned char. */

string_length (const char * string)
/* we will not change the values in the string, so declare the pointer
as a pointer to const(ant) char. when this function is called,
string will point at the first element of a char array that contains
a string */
{
const char * index;
/* we will use this pointer to find the zero */

index = string;
/* string points at the first element, now index does too */

while (*index != '\0')
/* while index isn't pointing at the terminating NUL */
{
index = index + 1;
/* make index point at the next character */
}

/* when execution reaches this point, index points at the
character with value zero, that is, at the NUL. That
means that index is pointing at the end of the string. */

return index - string;
/* compute how many characters ahead of string index is, and
return that value as the length of the string. */
}

The way the length of the string is computed means that "the length of the
string" means "the number of non-zero characters in the string". You
should be able to follow the code, now. To convince you that it is correct,
the last diagrams...

At the beginning:

string index
| |
V V
+----+----+----+----+----+---+
| 72 | 69 | 76 | 76 | 79 | 0 |
+----+----+----+----+----+---+
72=H 69=E 76=L 76=L 79=O

*index is 72, not 0, so add one to index:

string index
| |
V V
+----+----+----+----+----+---+
| 72 | 69 | 76 | 76 | 79 | 0 |
+----+----+----+----+----+---+
72=H 69=E 76=L 76=L 79=O

Keep going like that until *index == 0:

string index
| |
V V
+----+----+----+----+----+---+
| 72 | 69 | 76 | 76 | 79 | 0 |
+----+----+----+----+----+---+
72=H 69=E 76=L 76=L 79=O

Now return the number of elements that index is ahead of string:

string index
| |
V 5 4 3 2 1 V
+----+----+----+----+----+---+
| 72 | 69 | 76 | 76 | 79 | 0 |
+----+----+----+----+----+---+
72=H 69=E 76=L 76=L 79=O

index - string == 5, which is the number of letters in the string
"HELLO".

Here the code again, without comments, along with a main function that
uses it:

#include <stdio.h>

unsigned int string_length (const char * string)
{
const char * index;
index = string;

while (*index != '\0')
{
index = index + 1;
}

return index - string;
}

int main (void)
{
unsigned int length;
length = string_length("hello");
printf("the length is %u\n", length);
return 0;
}

-Sheldon
 
A

Alan Connor

Ok, let me try to tell you something about pointers that is (hopefully)
precisely correct, but won't confuse you.

An amazing post. Not one I can just skim and offer an intelligent reply to.

So I've saved it to my C docs and will read it until I get it.

After reading the first 1/3 carefully, it seems to me that a pointer is very
much like a symlink.

In your debt, Sheldon. Expect some more questions directly from this tutorial
in the future.
 
R

Richard Heathfield

Roose said:
So, can anyone explain to me why everyone is so insistent on not
top-posting, when they don't care about the long sigs?

But we /do/ care about long sig blocks, and I for one have complained
several times about them on this newsgroup. At least one person has
shortened their sig block as a result of my complaint (and, in so doing,
shows a maturity and wisdom that some here would do well to emulate).

Nevertheless, it is true that some news services appear to add their own
trailer text to each article posted, which (IMHO) is a Bad Thing, but some
people are stuck with their news service and it seems unfair to
discriminate against such people by going on and on about something that is
outside their power to change.
 
R

Richard Heathfield

Roose said:
Richard Heathfield said:
[top-posting fixed]
Richard wrote:
I only post replies to you when it is necessary to correct your errors.
If you don't write articles, you won't make errors, so it won't be
necessary to correct them.

I think you've stumbled across a big time-saver. Well done.

Just answer the questions.

You appear to be a very bright light, a couple of pairs of manacles, a
bloodstained wooden chair, a rope, a soundproof room, a rubber hose, a
piece of lead piping, an electric shock device, and a whole bucketful of
broken teeth short of a torture chamber. Until you get those deficiencies
sorted out, I'll answer the questions /I/ choose to answer.
They shouldn't be that hard for you. That is,
if you're going to keep nitpicking -- otherwise feel free to ignore
_everything_ I post.

I do feel free to ignore everything you post, but I also feel free to read
everything you post. And, of course, I feel free to correct such errors of
yours as I may notice from time to time. Freedom is a wonderful thing, is
it not?
 
R

Richard Heathfield

Alan Connor wrote:

We'll see. But trolling gets anyone killfiled for a while. I don't care
if it's being done by Dennis Ritchie.

Ah, the irony! The exquisite irony!
 
R

Richard Heathfield

Sheldon said:
Ok, let me try to tell you something about pointers that is (hopefully)
precisely correct, but won't confuse you.

A pointer is a kind of variable that can "point to" some object.

This isn't precisely correct. Sorry to dash your hopes.

A pointer need not be variable. For example, there exists such a thing as a
null pointer constant. Also, consider:

int main(void)
{
int i;
int *p = &i; /* &i is not a variable, but it is a pointer */
return 0;
}

Of course, Alan won't learn this, at least not yet. His problem, not mine.
 
R

Roose

You appear to be a very bright light, a couple of pairs of manacles, a
bloodstained wooden chair, a rope, a soundproof room, a rubber hose, a
piece of lead piping, an electric shock device, and a whole bucketful of
broken teeth short of a torture chamber. Until you get those deficiencies
sorted out, I'll answer the questions /I/ choose to answer.

Are you suggesting that I've tortured you? I didn't realize that forcing
all your inadequacies and defects of personality into the light -- oh god
the light -- would be so painful. So I understand the reluctance to answer
simple, straightforward questions.
I do feel free to ignore everything you post, but I also feel free to read
everything you post. And, of course, I feel free to correct such errors of
yours as I may notice from time to time. Freedom is a wonderful thing, is
it not?

Let me suggest that if you answer them in a _polite_ manner, then I will
feel free to let them stand, unanswered, for the rest of the group to learn
from. Otherwise, I will respond in kind when the points are irrelevant to
the larger question, and/or acknowledge their correctness when they are
relevant (as I have already done in more than one case).
 
R

Richard Heathfield

Roose said:
How about, "At least some bits of the binary representation of a pointer
can
be interpreted as an integer."

That's trivially true, because /all/ the bits of the object representation
(what you call the binary representation) of /any/ value can be interpreted
as an integer:

void PrintObjectRepresentation(FILE *fp, void *p, size_t len)
{
unsigned char *objrep = p;
while(len--)
{
printf(fp, " %X", *objrep++);
}
putc('\n', fp);
}
I didn't say anything about casting
pointers to integers.

Perhaps you should have done. The Standard says:

"Conversions that involve pointers (other than as permitted by the
constraints of $3.3.16.1) shall be specified by means of an explicit cast;
they have implementation-defined aspects: A pointer may be converted to an
integral type. The size of integer required and the result are
implementation-defined. If the space provided is not long enough, the
behavior is undefined. An arbitrary integer may be converted to a pointer.
The result is implementation-defined./37/ A pointer to an object or
incomplete type may be converted to a pointer to a different object type or
a different incomplete type. The resulting pointer might not be valid if
it is improperly aligned for the type pointed to."
Of course, that sentence is much less clear at the expense of a detail.
It doesn't get across the basic point as well.

If the basic point is /wrong/, it's not helpful.

<snip>
 
R

Richard Heathfield

Roose wrote:

It's because you think I actually something
valuable to say, that competes with your jack-off C standard knowledge,
that you must repeatedly, vehemently insist that I'm a troll.

Not so. If you have something valuable to say, please say it. We are all
waiting (with varying degrees of patience).

But we haven't seen it yet, as far as I can tell.

<snip>
 
R

Roose

Richard Heathfield said:
No. Learn To Read (tm).

Just engaging in some light wordplay, children's games if you will. You're
familiar with those. Remember that other thread?
 
R

Randy Howard

Chris, I enjoyed reading your post and there is definitely interesting
information here. But I would contend that if someone doesn't yet have a
good understanding of pointers, he won't have the foggiest idea what you're
talking about. : )

Roose, if you want to be a moderator of a newsgroup, why do you not go
start one and see how many sheep wish to join your flock?
 
S

Sheldon Simms

This isn't precisely correct. Sorry to dash your hopes.

No problem.
A pointer need not be variable. For example, there exists such a thing as a
null pointer constant. Also, consider:

int main(void)
{
int i;
int *p = &i; /* &i is not a variable, but it is a pointer */
return 0;
}

All true. I'll try to update to be precisely correct.

-Sheldon
 
A

Alan Connor

Are you suggesting that I've tortured you? I didn't realize that forcing
all your inadequacies and defects of personality into the light -- oh god
the light -- would be so painful. So I understand the reluctance to answer
simple, straightforward questions.


Let me suggest that if you answer them in a _polite_ manner, then I will
feel free to let them stand, unanswered, for the rest of the group to learn
from. Otherwise, I will respond in kind when the points are irrelevant to
the larger question, and/or acknowledge their correctness when they are
relevant (as I have already done in more than one case).

This is very sophisticated duelling, and somewhat entertaing, but I'd much
rather you were each expounding upon the fundamentals of C in your individual
and unique styles.

But I am curious, Roose, as to why you continue feeding the troll? Trolls
don't care about reason or truth or the subject at hand. They pretend to be
in order to create and win a game of dominance in which they can't increment
the value of SELF but must decrement the value of OTHER.
 

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

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top