Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #56952 > unrolled thread
| Started by | bilsch <king621@comcast.net> |
|---|---|
| First post | 2015-02-04 23:45 +0000 |
| Last post | 2015-02-05 09:47 +0000 |
| Articles | 20 on this page of 30 — 10 participants |
Back to article view | Back to comp.lang.c
what is data type 'int *' ? bilsch <king621@comcast.net> - 2015-02-04 23:45 +0000
Re: what is data type 'int *' ? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-02-05 00:09 +0000
Re: what is data type 'int *' ? glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-02-05 01:24 +0000
Re: what is data type 'int *' ? James Kuyper <jameskuyper@verizon.net> - 2015-02-05 07:00 -0500
Re: what is data type 'int *' ? glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-02-05 14:09 +0000
Re: what is data type 'int *' ? Robert Wessel <robertwessel2@yahoo.com> - 2015-02-05 13:08 -0600
Re: what is data type 'int *' ? glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-02-06 00:33 +0000
Re: what is data type 'int *' ? Robert Wessel <robertwessel2@yahoo.com> - 2015-02-05 19:30 -0600
Re: what is data type 'int *' ? glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-02-06 10:17 +0000
Re: what is data type 'int *' ? Malcolm McLean <malcolm.mclean5@btinternet.com> - 2015-02-07 13:21 -0800
Re: what is data type 'int *' ? Lew Pitcher <lew.pitcher@digitalfreehold.ca> - 2015-02-04 19:47 -0500
Re: what is data type 'int *' ? bilsch <king621@comcast.net> - 2015-02-05 09:40 +0000
Re: what is data type 'int *' ? Richard Heathfield <rjh@cpax.org.uk> - 2015-02-05 09:51 +0000
Re: what is data type 'int *' ? Keith Thompson <kst-u@mib.org> - 2015-02-05 08:04 -0800
Re: what is data type 'int *' ? glen herrmannsfeldt <gah@ugcs.caltech.edu> - 2015-02-06 00:42 +0000
Re: what is data type 'int *' ? Keith Thompson <kst-u@mib.org> - 2015-02-05 19:09 -0800
Re: what is data type 'int *' ? Kaz Kylheku <kaz@kylheku.com> - 2015-02-05 01:17 +0000
Re: what is data type 'int *' ? Richard Heathfield <rjh@cpax.org.uk> - 2015-02-05 03:04 +0000
Re: what is data type 'int *' ? Richard Heathfield <rjh@cpax.org.uk> - 2015-02-05 03:06 +0000
Re: what is data type 'int *' ? Richard Heathfield <rjh@cpax.org.uk> - 2015-02-05 03:06 +0000
Re: what is data type 'int *' ? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-02-05 10:20 +0000
Re: what is data type 'int *' ? Richard Heathfield <rjh@cpax.org.uk> - 2015-02-05 10:47 +0000
Re: what is data type 'int *' ? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-02-05 12:18 +0000
Re: what is data type 'int *' ? Richard Heathfield <rjh@cpax.org.uk> - 2015-02-05 12:40 +0000
Re: what is data type 'int *' ? Keith Thompson <kst-u@mib.org> - 2015-02-05 08:24 -0800
Re: what is data type 'int *' ? James Kuyper <jameskuyper@verizon.net> - 2015-02-05 11:42 -0500
Re: what is data type 'int *' ? Ben Bacarisse <ben.usenet@bsb.me.uk> - 2015-02-05 17:13 +0000
Re: what is data type 'int *' ? Keith Thompson <kst-u@mib.org> - 2015-02-05 08:43 -0800
Re: what is data type 'int *' ? bilsch <king621@comcast.net> - 2015-02-05 09:45 +0000
THANKS FOR THE INFO bilsch <king621@comcast.net> - 2015-02-05 09:47 +0000
Page 1 of 2 [1] 2 Next page →
| From | bilsch <king621@comcast.net> |
|---|---|
| Date | 2015-02-04 23:45 +0000 |
| Subject | what is data type 'int *' ? |
| Message-ID | <mauauc$p5v$1@dont-email.me> |
A Sams book on C programming has a program that demonstrates how pointers
work, similar to the listing below. I'm using Ubuntu and GNU gcc
compiler. For the last two printf lines the gcc compiler gives the error:
expects argument of type ‘int’, but argument 2 has type ‘int *’
Presumably, the compiler used with the book does not produce this error.
My question is what format specifier (%i) should I use for
type 'int *' ? I dont know of a format specifier for type ‘int *’.
Also, if the compiler used with the book doesnt produce the error, why is
gcc so finicky.
/* pointer.c */
#include <stdio.h>
/* declare and initialize variable of type int */
int var=1;
/* declare a pointer to int */
int *ptr;
int main()
{
ptr=&var;
/* access directly and indirectly */
printf("\ndirect access var= %i",var);
printf("\nindirect access var= %i",*ptr);
/* display the address of var two ways */
printf("\nthe address of var is %i\n",ptr);
printf("\nthe address of var is %i\n",&var);
}
[toc] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2015-02-05 00:09 +0000 |
| Message-ID | <87vbjhcj84.fsf@bsb.me.uk> |
| In reply to | #56952 |
bilsch <king621@comcast.net> writes:
> A Sams book on C programming has a program that demonstrates how pointers
> work, similar to the listing below. I'm using Ubuntu and GNU gcc
> compiler. For the last two printf lines the gcc compiler gives the error:
>
> expects argument of type ‘int’, but argument 2 has type ‘int *’
>
> Presumably, the compiler used with the book does not produce this
> error.
Or the author ignored it!
> My question is what format specifier (%i) should I use for
> type 'int *' ? I dont know of a format specifier for type ‘int *’.
There's no specifier for int * but there is one for void *: %p. Because
of the way functions like printf with variable numbers of arguments
work, you need to cast the pointer to void * if it is not already of
that type:
printf("\nthe address of var is %p\n", (void *)&var);
> Also, if the compiler used with the book doesnt produce the error, why is
> gcc so finicky.
gcc has a history of being cross-platform. It is stronger than some
others at pointing out things that are wrong but might just happen to
work on one system or another. It's a good thing. You sound as if you
think it's the wrong way to go.
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | glen herrmannsfeldt <gah@ugcs.caltech.edu> |
|---|---|
| Date | 2015-02-05 01:24 +0000 |
| Message-ID | <maugoi$g65$1@speranza.aioe.org> |
| In reply to | #56953 |
Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
> bilsch <king621@comcast.net> writes:
>> A Sams book on C programming has a program that demonstrates
>> how pointers work, similar to the listing below.
(snip)
>> expects argument of type ???int???, but argument 2 has type ???int *???
>> My question is what format specifier (%i) should I use for
>> type 'int *' ? I dont know of a format specifier for type ???int *???.
> There's no specifier for int * but there is one for void *: %p.
> Because of the way functions like printf with variable numbers
> of arguments work, you need to cast the pointer to void * if
> it is not already of that type:
> printf("\nthe address of var is %p\n", (void *)&var);
>> Also, if the compiler used with the book doesnt produce the error,
>> why is gcc so finicky.
Early C compilers stored pointers and int the same way, and it
was very common to ignore the difference. Functions with a pointer
argument that was then passed on to another function often declared
one as int. (That was before void*, though the specific pointer
type could have been used.)
Also, compilers for many years didn't look inside the format
string until run time.
If (int) and (int*) are the same size, and passed to functions
the same way, it usually works. As machines with pointer and int
of different size became more popular, there was more need for
the message.
Note that you can cast to (int) and print with %i or %x. The
conversion is system dependent, but sometimes people know what
it does on the specific system, and find it useful.
> gcc has a history of being cross-platform. It is stronger than some
> others at pointing out things that are wrong but might just happen to
> work on one system or another. It's a good thing. You sound as if you
> think it's the wrong way to go.
-- glen
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2015-02-05 07:00 -0500 |
| Message-ID | <mavlv6$ogf$1@dont-email.me> |
| In reply to | #56956 |
On 02/04/2015 08:24 PM, glen herrmannsfeldt wrote: ... > If (int) and (int*) are the same size, and passed to functions > the same way, it usually works. As machines with pointer and int > of different size became more popular, there was more need for > the message. > > Note that you can cast to (int) and print with %i or %x. The > conversion is system dependent, but sometimes people know what > it does on the specific system, and find it useful. It's not merely system dependent: "If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type." (6.3.2.3p6) Don't do that. The right way to print pointer values is with "%p" and a cast to void*. -- James Kuyper
[toc] | [prev] | [next] | [standalone]
| From | glen herrmannsfeldt <gah@ugcs.caltech.edu> |
|---|---|
| Date | 2015-02-05 14:09 +0000 |
| Message-ID | <mavti7$hgr$1@speranza.aioe.org> |
| In reply to | #56976 |
James Kuyper <jameskuyper@verizon.net> wrote: (snip, I wrote) >> If (int) and (int*) are the same size, and passed to functions >> the same way, it usually works. As machines with pointer and int >> of different size became more popular, there was more need for >> the message. >> Note that you can cast to (int) and print with %i or %x. The >> conversion is system dependent, but sometimes people know what >> it does on the specific system, and find it useful. > It's not merely system dependent: "If the result cannot be represented > in the integer type, the behavior is undefined. The result need not be > in the range of values of any integer type." (6.3.2.3p6) If one doesn't understand the underlying hardware, the way it stores addresses, and such, yes, one should not use it. But if one does, there might be some cases where it is better. For one, you have the choice of %x for hex, or %i for decimal. With %p you get what someone else decided was best. As well as I know it, that could be hex, decimal, or some combination. > Don't do that. The right way to print pointer values is > with "%p" and a cast to void*. -- glen
[toc] | [prev] | [next] | [standalone]
| From | Robert Wessel <robertwessel2@yahoo.com> |
|---|---|
| Date | 2015-02-05 13:08 -0600 |
| Message-ID | <cpf7dal792u50odpg86csmimkv35uob383@4ax.com> |
| In reply to | #56984 |
On Thu, 5 Feb 2015 14:09:11 +0000 (UTC), glen herrmannsfeldt <gah@ugcs.caltech.edu> wrote: >James Kuyper <jameskuyper@verizon.net> wrote: > >(snip, I wrote) >>> If (int) and (int*) are the same size, and passed to functions >>> the same way, it usually works. As machines with pointer and int >>> of different size became more popular, there was more need for >>> the message. > >>> Note that you can cast to (int) and print with %i or %x. The >>> conversion is system dependent, but sometimes people know what >>> it does on the specific system, and find it useful. > >> It's not merely system dependent: "If the result cannot be represented >> in the integer type, the behavior is undefined. The result need not be >> in the range of values of any integer type." (6.3.2.3p6) > >If one doesn't understand the underlying hardware, the way it >stores addresses, and such, yes, one should not use it. > >But if one does, there might be some cases where it is better. >For one, you have the choice of %x for hex, or %i for decimal. >With %p you get what someone else decided was best. As well as >I know it, that could be hex, decimal, or some combination. And on some x86-16 implementations, with far pointers %p would (as you would logically expect) generate something like "abcd:1234".
[toc] | [prev] | [next] | [standalone]
| From | glen herrmannsfeldt <gah@ugcs.caltech.edu> |
|---|---|
| Date | 2015-02-06 00:33 +0000 |
| Message-ID | <mb124p$ske$1@speranza.aioe.org> |
| In reply to | #57010 |
Robert Wessel <robertwessel2@yahoo.com> wrote: (snip regarding casting pointers to int, and printing them) (then I wrote) >>If one doesn't understand the underlying hardware, the way it >>stores addresses, and such, yes, one should not use it. >>But if one does, there might be some cases where it is better. >>For one, you have the choice of %x for hex, or %i for decimal. >>With %p you get what someone else decided was best. As well as >>I know it, that could be hex, decimal, or some combination. > And on some x86-16 implementations, with far pointers %p would (as > you would logically expect) generate something like "abcd:1234". I think I remember %P from the days of OS/2 1.x programming, and for large model it did print in selector:offset form. When was %p added to C? -- glen
[toc] | [prev] | [next] | [standalone]
| From | Robert Wessel <robertwessel2@yahoo.com> |
|---|---|
| Date | 2015-02-05 19:30 -0600 |
| Message-ID | <mr58datv7363nafvf6a34csf6aphqlbalm@4ax.com> |
| In reply to | #57036 |
On Fri, 6 Feb 2015 00:33:29 +0000 (UTC), glen herrmannsfeldt <gah@ugcs.caltech.edu> wrote: >Robert Wessel <robertwessel2@yahoo.com> wrote: > >(snip regarding casting pointers to int, and printing them) >(then I wrote) > >>>If one doesn't understand the underlying hardware, the way it >>>stores addresses, and such, yes, one should not use it. > >>>But if one does, there might be some cases where it is better. >>>For one, you have the choice of %x for hex, or %i for decimal. >>>With %p you get what someone else decided was best. As well as >>>I know it, that could be hex, decimal, or some combination. > >> And on some x86-16 implementations, with far pointers %p would (as >> you would logically expect) generate something like "abcd:1234". > >I think I remember %P from the days of OS/2 1.x programming, and >for large model it did print in selector:offset form. > >When was %p added to C? It was formally added in C89, and was not in K&R1. Although there were implementations supporting it before C89. Circa 1987 MSC 5.0 (IIRC, it was 5.1 that first supported OS/2) had %p.
[toc] | [prev] | [next] | [standalone]
| From | glen herrmannsfeldt <gah@ugcs.caltech.edu> |
|---|---|
| Date | 2015-02-06 10:17 +0000 |
| Message-ID | <mb24c8$pep$1@speranza.aioe.org> |
| In reply to | #57040 |
Robert Wessel <robertwessel2@yahoo.com> wrote: (snip, I wrote) >>I think I remember %P from the days of OS/2 1.x programming, and >>for large model it did print in selector:offset form. >>When was %p added to C? > It was formally added in C89, and was not in K&R1. Although there > were implementations supporting it before C89. Circa 1987 MSC 5.0 > (IIRC, it was 5.1 that first supported OS/2) had %p. OK, I went back to some programs I wrote for MSC 5.1 and 6.0. I had thought I remembered it as %P, but it seems that, yes, they are %p. I haven't used it so much since, though. OS/2 would report the address where it was executing and the register contents at the time when programs crashed. Printing out some pointers made it especially easy to figure out what it was doing. For most systems now, pointer values don't help so much in debugging. I do still remember using adb to find the address where a program died, though. -- glen
[toc] | [prev] | [next] | [standalone]
| From | Malcolm McLean <malcolm.mclean5@btinternet.com> |
|---|---|
| Date | 2015-02-07 13:21 -0800 |
| Message-ID | <0f69cd6d-7c25-4a6d-b79e-da487988f946@googlegroups.com> |
| In reply to | #57049 |
On Friday, February 6, 2015 at 10:17:59 AM UTC, glen herrmannsfeldt wrote: > > For most systems now, pointer values don't help so much in > debugging. I do still remember using adb to find the address > where a program died, though. > It can help in detecting memory leaks. If the values recycle that usually indicates that objects are being freed correctly, if they keep on incrementing, then often that indicates a leak of approximately the size of the increment.
[toc] | [prev] | [next] | [standalone]
| From | Lew Pitcher <lew.pitcher@digitalfreehold.ca> |
|---|---|
| Date | 2015-02-04 19:47 -0500 |
| Message-ID | <q4zAw.1325618$412.538957@fx30.iad> |
| In reply to | #56952 |
On Wednesday February 4 2015 18:45, in comp.lang.c, "bilsch"
<king621@comcast.net> wrote:
> A Sams book on C programming
How old is this book? Could it be pre-1989? If so, it likely discusses K&R
C, rather than ANSI or ISO C.
This is important, because K&R C had a lot fewer restrictions wrt pointer
types than ANSI/ISO C had. In "The C Programming Language" (Prentice-Hall,
1978) Kernighan and Richie made the point that, in common implementations
of K&R C, "on most machines a pointer may be assigned to an integer and
back again without changing it; no scaling or conversion takes place, and
no bits are lost". However, they note that "Regrettably, this has led to
the taking of liberties ...".
> has a program that demonstrates how pointers
> work, similar to the listing below. I'm using Ubuntu and GNU gcc
> compiler. For the last two printf lines the gcc compiler gives the error:
>
> expects argument of type ‘int’, but argument 2 has type ‘int *’
>
> Presumably, the compiler used with the book does not produce this error.
That was, apparently, common in the K&R days.
> My question is what format specifier (%i) should I use for
> type 'int *' ? I dont know of a format specifier for type ‘int *’.
As Ben B. says, there is /no/ printf() format specifier for type 'int *', in
K&R C or any of the ANSI/ISO C standards. The closest you get is the %p
format specifier introduced in ISO C 1990. %p takes a 'void *';
> Also, if the compiler used with the book doesnt produce the error, why is
> gcc so finicky.
>
>
> /* pointer.c */
>
> #include <stdio.h>
>
> /* declare and initialize variable of type int */
> int var=1;
> /* declare a pointer to int */
> int *ptr;
>
> int main()
> {
>
> ptr=&var;
>
> /* access directly and indirectly */
>
> printf("\ndirect access var= %i",var);
> printf("\nindirect access var= %i",*ptr);
>
> /* display the address of var two ways */
>
> printf("\nthe address of var is %i\n",ptr);
> printf("\nthe address of var is %i\n",&var);
> }
I suspect that the book addresses K&R C, as this would be valid K&R code
(under some compilers), and the C standards prior to 2011 required main()
to return an explicit int value (via the return statement or a call to one
of the exit() functions).
HTH
--
Lew Pitcher
"In Skills, We Trust"
PGP public key available upon request
[toc] | [prev] | [next] | [standalone]
| From | bilsch <king621@comcast.net> |
|---|---|
| Date | 2015-02-05 09:40 +0000 |
| Message-ID | <mavdq7$8od$1@dont-email.me> |
| In reply to | #56954 |
The book says written with ANSI standard in mind. Copyright 2003.
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-02-05 09:51 +0000 |
| Message-ID | <maveeq$sq3$1@dont-email.me> |
| In reply to | #56963 |
On 05/02/15 09:40, bilsch wrote: > The book says written with ANSI standard in mind. Copyright 2003. I think you can safely bin it. If you're unwilling to do that, at least take *everything* in it with a pinch of salt. Never assume that a C author knows what he's talking about. -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-02-05 08:04 -0800 |
| Message-ID | <lna90smjjt.fsf@nuthaus.mib.org> |
| In reply to | #56954 |
Lew Pitcher <lew.pitcher@digitalfreehold.ca> writes:
[...]
> I suspect that the book addresses K&R C, as this would be valid K&R code
> (under some compilers), and the C standards prior to 2011 required main()
> to return an explicit int value (via the return statement or a call to one
> of the exit() functions).
The change you're thinking of was made by C99.
Prior to C99, falling off the end of main returned an "undefined" result
to the environment. This wasn't forbidden, and might not cause any real
problems as long as you didn't do anything that depends on the program's
exit status.
In C99 and later, reaching the closing } is equivalent to `return 0;`.
(I'm not convinced that adding this special case was a good idea.)
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | glen herrmannsfeldt <gah@ugcs.caltech.edu> |
|---|---|
| Date | 2015-02-06 00:42 +0000 |
| Message-ID | <mb12ld$uh7$1@speranza.aioe.org> |
| In reply to | #56996 |
Keith Thompson <kst-u@mib.org> wrote: (snip on the return type and value of main) > The change you're thinking of was made by C99. > Prior to C99, falling off the end of main returned an "undefined" result > to the environment. This wasn't forbidden, and might not cause any real > problems as long as you didn't do anything that depends on the program's > exit status. Seems like I remember some figuring out that the return value was the value of the last expression, which would be left in the appropriate register. > In C99 and later, reaching the closing } is equivalent to `return 0;`. > (I'm not convinced that adding this special case was a good idea.) If something is done often enough, it seems that special case is convenient and not confusing. Though I could wonder why not have all functions that fall off the end return zero? Early (version -1) of Mathematica required [] on Quit, like all other function references. After I complained about this, it was made a special case. I think it was worth changing, though less consistent. -- glen
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2015-02-05 19:09 -0800 |
| Message-ID | <ln8ugbloql.fsf@nuthaus.mib.org> |
| In reply to | #57038 |
glen herrmannsfeldt <gah@ugcs.caltech.edu> writes:
> Keith Thompson <kst-u@mib.org> wrote:
> (snip on the return type and value of main)
>
>> The change you're thinking of was made by C99.
>
>> Prior to C99, falling off the end of main returned an "undefined" result
>> to the environment. This wasn't forbidden, and might not cause any real
>> problems as long as you didn't do anything that depends on the program's
>> exit status.
>
> Seems like I remember some figuring out that the return value was
> the value of the last expression, which would be left in the
> appropriate register.
That may well be true for some implementations. It's not tre in
general.
>> In C99 and later, reaching the closing } is equivalent to `return 0;`.
>> (I'm not convinced that adding this special case was a good idea.)
>
> If something is done often enough, it seems that special case is
> convenient and not confusing.
There's a case to be made for that; I don't feel very strongly about it
one way or the other.
> Though I could wonder why not have all functions that fall off the
> end return zero?
Not all functions return a type for which "zero" is meaningful.
> Early (version -1) of Mathematica required [] on Quit, like all
> other function references. After I complained about this, it was
> made a special case. I think it was worth changing, though less
> consistent.
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Kaz Kylheku <kaz@kylheku.com> |
|---|---|
| Date | 2015-02-05 01:17 +0000 |
| Message-ID | <20150204171116.522@kylheku.com> |
| In reply to | #56952 |
On 2015-02-04, bilsch <king621@comcast.net> wrote: > Also, if the compiler used with the book doesnt produce the error, why is > gcc so finicky. Note that GCC's error message is produced by actually scanning the contents of the printf format string literal, and matching between the conversion specifiers like %d and the types of the expressions. GCC is going above and beyond what is required by ISO C and what many other compilers provide. The C standard doesn't require translators to analyze printf format strings. In C, the processing of printf format strings is traditionally modeled as a run-time library feature that a compiler doesn't care about. Mismatches between printf format strings and arguments have been sources of bugs. Possiby even security holes. These bugs are diagnosable at compile time, since most format strings are static literals. The authors of your book didn't catch their bug because they used a compiler that doesn't do this validation between the format string and arguments. They also seem shockingly ignorant (for people claiming to be expert enough to write a book); everyone who is anyone in C programming knows about the %p conversion specifier for printing pointers. It's been in printf since the 1980's.
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-02-05 03:04 +0000 |
| Message-ID | <maumii$m0o$1@dont-email.me> |
| In reply to | #56952 |
On 04/02/15 23:45, bilsch wrote: > A Sams book on C programming has a program that demonstrates how pointers > work, similar to the listing below. I just spent an hour (or more) unpacking this question. The reply was - er - rather long. I sent it off just now, and discovered to my horror that I'd hit "reply" instead of "followup", so it's gone via email instead of via Usenet. Worse still, I can find no record of it locally. blisch - sorry about that. If you're still reading, could you email a copy back to me, please? Thanks. -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-02-05 03:06 +0000 |
| Message-ID | <maumnc$m0o$3@dont-email.me> |
| In reply to | #56957 |
On 05/02/15 03:04, Richard Heathfield wrote: > On 04/02/15 23:45, bilsch wrote: >> A Sams book on C programming has a program that demonstrates how pointers >> work, similar to the listing below. > > I just spent an hour (or more) unpacking this question. The reply was - > er - rather long. I sent it off just now, and discovered to my horror > that I'd hit "reply" instead of "followup", so it's gone via email > instead of via Usenet. Worse still, I can find no record of it locally. > > blisch - sorry about that. If you're still reading, could you email a > copy back to me, please? Thanks. Skip that. Found it! -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2015-02-05 03:06 +0000 |
| Message-ID | <maumml$m0o$2@dont-email.me> |
| In reply to | #56952 |
On 04/02/15 23:45, bilsch wrote:
> > A Sams book on C programming
You want to be careful of those.
> > has a program that demonstrates how pointers
> > work, similar to the listing below.
Reading through that code, I see that they've chosen to demonstrate how
pointers work in the same way that a zoo-keeper might explain an
elephant by saying "those are the legs, that's the trunk, there are the
big ears, and it's grey". All true, but you don't really feel,
afterwards, that you've /understood/ the elephant. Why the rather odd
gait? Why the trunk? What, in short, is an elephant /for/?
Same with pointers. The code you show doesn't look like it'll be much
help in understanding them. Never fear! Help is here. After a few weeks
of interminable debate here in comp.lang.c, you should understand them
just fine.
> > I'm using Ubuntu and GNU gcc
> > compiler.
Excellent choices.
> For the last two printf lines the gcc compiler gives the error:
> >
> > expects argument of type ‘int’, but argument 2 has type ‘int *’
Okay, we'll get there in due course.
> >
> > Presumably, the compiler used with the book does not produce this
error.
Don't count on it. Book code is notorious for not working as well as it
should, not working at all, or sometimes not even compiling.
> > My question is what format specifier (%i) should I use for
> > type 'int *' ? I dont know of a format specifier for type ‘int *’.
Ben covered this. There isn't one. Use %p for "pointer", and cast the
pointer to (void *), whatever its type (although beware of function
pointers). This is one of the EXTRAORDINARILY RARE times when you should
use a cast. If in doubt, NEVER cast, and then rely on comp.lang.c to
tell you when you should have done, until you learn what casting is for
and why you should hardly ever use it and why it is massively over-used
in the Real World and why this is a Bad Thing that will result in The
End Of All Things and We'll All Be Murdered In Our Beds. (Well, okay,
maybe not, but it's still not a good thing.)
> >
> > Also, if the compiler used with the book doesnt produce the error,
why is
> > gcc so finicky.
Finicky is good. Besides, you can't assume that book code is any good.
(Trust me, I know this.)
> > /* pointer.c */
> >
> > #include <stdio.h>
Okay, so we're pulling in all kinds of useful information for the
compiler - function prototypes for a fair chunk of the standard library,
type synonyms, a few handy #defines...
> >
> > /* declare and initialize variable of type int */
> > int var=1;
Good comment (despite the US spelling of "initialise"). This line
actually achieves no fewer than four things:
1) it's a declaration of var as an int;
2) it's a definition of var as a real live object;
3) it's an initialisation of var as having value 1;
4) it's a reminder to think at some point about code formatting. Don't
just adopt the layout style used by the book. Think it through and come
up with a style that suits your thinking. My own style seems to drive
other people nuts around here, but they tolerate it because they're Good
People...
The comment, as I said, is good. The following line is just rubbish.
What on earth is that object doing outside a function? Legal? Sure.
Sensible? Hell no!
> > /* declare a pointer to int */
> > int *ptr;
Not such a good comment. Complain to your author. This *is* indeed a
declaration of a pointer to int, but it is also a tentative definition.
Although you shouldn't worry too much about this at the moment, it is
something to bear in mind for the future. In the absence of any
subsequent (or indeed prior) non-tentative definitions, this will be the
definition of ptr. What's more, because it's at file scope, it has
static storage duration, and /that/ means that it will, in the absence
of explicit initialisation, take on the default static initialiser value
which, for pointers, is the null value - so it's like saying: int *ptr =
NULL;
Note that this is only true for objects with static storage duration. It
is not true for auto objects.
Note, too, that this object also belongs inside a function. The author
is trying to teach you bad habits, but you don't have to listen.
> >
> > int main()
Keith Thompson would argue that this is better:
int main(void)
If you want to know why, ask Keith. There is a point to it, but I can
never remember what it is, so it's probably not /that/ important, but
Keith may disagree on that, and he's a chap that decent chaps listen to
(even if they can't always remember exactly what he said!).
> > {
> >
> > ptr=&var;
Okay, let's take this apart a bit. Firstly, let's put in some SPACE!
ptr = &var;
Spacing doesn't define precedence, but it can help to clarify our intent.
The first thing to notice is that this is an assignment statement, not
an initialisation. Initialisation happens when you define an object
*and* give it a value, all in the same line of code. It's a terminology
thing, but it's good to get the terminology straight at an early stage.
Secondly, the types on either side of an assignment statement must
match. They need not always be exactly the same type, but the types must
be "compatible". In this case, there is no problem matching the types,
as we shall see.
&var means: "var is an object whose address I can legally access". You
can legally take the address of most objects. One obvious exception is
objects that you have asked to be stored in registers, which doesn't
apply here. Since "var" is of type "int", "&var" has type "int *". That
is, &var is a pointer to an int.
Since ptr also has the type "pointer to int", there is no problem
assigning the value "&var" to the object named "ptr". (There *is* a
problem doing it the other way around, because you can't change an
object's address via assignment: &var = ptr; is not legal.)
Now, before we go any further, let's try to find out how this elephant
ticks, and what it's actually for.
C is a "pass-by-value" language. Whenever we try to pass anything to a
function, it is the /value/ of the thing that is passed. In this case,
var has the value 1, so an attempt to pass var to a function will
actually have the effect of passing the value 1 to that function. The
function will never know about var itself. Any change to the value
passed will have no effect whatsoever on var.
<WARNING: analogy ahead. Analogies are illustrative, but they are not
allegories, and there is not a 100% match between analogy and reality>
Passing an object to a function in C is like giving a photograph of your
house to an arsonist. He can burn the photograph all he likes, but your
house will remain standing. And, since one house looks pretty much like
another, he can't use the information in the photograph to get to your
actual house, so he still can't burn it down.
But we can get around this apparent limitation by sending the address of
the object instead. To continue the analogy, imagine writing your house
address on a piece of paper. Take a photograph of the piece of paper,
and hand the photograph to the arsonist. Well, a photograph of a
hand-written address is just as useful as the original, right? So now
the arsonist can find your home, and now he can burn it down. Or maybe
he can mow your lawn, if he's in a good mood. A photo of an ADDRESS is
just as good as the original address.
And that's the principal reason (although not the only one) that
pointers are useful - they allow us to pass enough information about an
object into a function such that the function can change the value of
the object itself, not just the value of a copy of that object.
Pass-by-value with values:
#include <stdio.h>
void change(int foo)
{
foo = 6;
}
int main(void)
{
int bar = 0;
printf("bar: prior value = %d\n", bar);
change(bar);
printf("bar: new value = %d\n", bar);
return 0;
}
Output:
bar: prior value = 0
bar: new value = 0
Pointer version:
#include <stdio.h>
void change(int *foo)
{
*foo = 6;
}
int main(void)
{
int bar = 0;
printf("bar: prior value = %d\n", bar);
change(&bar);
printf("bar: new value = %d\n", bar);
return 0;
}
Output:
bar: prior value = 0
bar: new value = 6
> >
> > /* access directly and indirectly */
> >
> > printf("\ndirect access var= %i",var);
Yes, %i will do instead of %d here. But what a stupid place for a
newline! Not your fault - the author's fault. The \n should go at the
end of the format string. A \n should be used to indicate the end of a
line, not the beginning. And this isn't just pickiness. C specifies that
a newline is one of those points at which standard output is 'flushed',
meaning it is definitely sent to the output device and not just sitting
in a cache somewhere. So it's a good idea to put it at the end of the
line, not the beginning.
Note, too, the rather inconsistent use of spacing. Better:
printf("direct access var = %i\n", var);
Now, let's actually look at what is happening here.
printf is a function, like any other (all right, not /quite/ like any
other, because the way it's prototyped is a bit strange; but it's still
just a function). And, like most functions, it takes parameters.
Argument: something you pass to a function
Parameter: something a function receives
Here, we pass printf two arguments. They are:
"direct access var = %i\n"
and
var
in that lexical order.
C now *evaluates* these arguments.
The string "direct access var = %i\n" is in fact an array of characters.
When you evaluate an array, what you get (in C, at least!) is a pointer
to the first element in that array. That's enough information for printf
to be able to find the rest of the array, and it knows when to stop
because C strings are null-terminated ('\0' at the end, automatically
inserted into 'string literals' like this one, in almost all cases, and
the only exception is one that you shouldn't have to worry about for now).
var is evaluated as 1.
So what the compiler sends to printf is actually the address of the 'd'
of "direct access var = %i\n", and a 1.
The printf function uses your first argument (its first parameter) to
work out how much more information you are (claiming to be) sending to
it. The % is the giveaway. % means "heads-up, something happening here".
(That is why you have to be wary when your program outputs percentages.
To tell printf to print a % sign, you have to write %% rather than just
% on its own.)
The character(s) following the % sign tell printf what to do. %i means
"print an int", so printf looks in its parameter list for the next
parameter, treats it as an int, and displays the value on stdout, the
standard output device, normally your screen. And that's why printf
prints 1.
Interestingly, if you forget to send it var as well (and we've all done
it), printf won't care. It'll grab what it /thinks/ is the next
parameter (which in practice will be some random gobs of RAM), and print
/that/ as an int. Fortunately, many compilers are wise to this nowadays,
and will squeal at you at compilation time.
> > printf("\nindirect access var= %i",*ptr);
Apart from the terrible formatting, this line is fine. The string is
evaluated as before. *ptr is evaluated as: "the value of the object
pointed to by ptr". Since ptr points to var (i.e. holds the address of
var), *ptr means in this case "the value of var", i.e. 1. And so printf
actually receives 1 as its second parameter. It doesn't know anything at
all about ptr.
> >
> > /* display the address of var two ways */
What the author didn't tell you is that both these ways are wrong.
> > printf("\nthe address of var is %i\n",ptr);
> > printf("\nthe address of var is %i\n",&var);
ptr has type int *, but %i tells printf to expect an int.
In many cases, ptr and int have the same size (in bits), so printf
doesn't necessarily know or care that there's a problem here. But *we*
should care, because int * is not int, and int is not int *.
To print a pointer value, use %p. Since there are infinitely many
pointer types (for certain values of "infinitely"), it would be
impractical to have a different format specifier for every pointer type.
So the printf function offers just one (two if you count %s, which is
rather different in semantics): %p for void *.
In general, void * is a synonym for "any old object pointer type". But,
where variadic functions are concerned, it pays to be careful because
printf doesn't know in advance that you're sending a void * as your
second argument. In fact, the first and second times you called it, you
passed an int, not a void *. In other words, the type can change from
call to call. So printf can't tell until runtime what type to expect. So
it can't define its own parameter list in terms of void *, and that
means you can't expect an automatic conversion, so you have to supply
one explicitly, by means of a cast: (void *)ptr - hence we get:
printf("\nthe address of var is %p\n", (void *)ptr);
printf("\nthe address of var is %p\n", (void *)&var);
And there we have it.
I *hope* I have unpacked your question in sufficient detail that you can
now actually understand what is going on. But, if I haven't, please do
feel free to ask follow-up questions.
--
Richard Heathfield
Email: rjh at cpax dot org dot uk
"Usenet is a strange place" - dmr 29 July 1999
Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.c
csiph-web