Home > Archive > Unix Programming > March 2007 > char *** help
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
|
|
| Frank Cusack 2007-03-26, 10:07 pm |
| Why doesn't this code work?
$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#define NELEM 2
int
main(int argc, char *argv[])
{
char **val[NELEM];
int i;
for (i = 0; i < NELEM; ++i) {
val[i] = malloc(sizeof(*val));
*val[i] = malloc(sizeof(**val) * 3);
// val[i][0] left uninitialized
val[i][1] = NULL;
val[i][2] = NULL;
printf("%p\n", val[0][2]);
}
}
$ gcc test.c
$ ./a.out
0
1
$
It "should" print 0 twice. I am using this to call a function which
takes an argv-like (char **) argument. I want to create an array of
char **'s so I can run through them in a loop.
thanks
-frank
| |
| Eric Sosman 2007-03-26, 10:07 pm |
| Frank Cusack wrote:
> Why doesn't this code work?
>
> $ cat test.c
> #include <stdio.h>
> #include <stdlib.h>
>
> #define NELEM 2
>
> int
> main(int argc, char *argv[])
> {
> char **val[NELEM];
> int i;
>
> for (i = 0; i < NELEM; ++i) {
> val[i] = malloc(sizeof(*val));
Allocates memory for one pointer, and sets val[i] to
point to it. Fine so far.
(Well, almost fine. Note that val[i] and the operand
of sizeof have the same indirection level, whereas normally
they'd be one level apart: The size ought to be that of the
thing pointed at, not that of the pointer. Since all data
pointers have the same size on many machines -- maybe on all
Unix machines -- the mistake is probably harmless. Still, the
fact that it was made suggests some confusion ... The same
probably-harmless error is repeated below, too.)
> *val[i] = malloc(sizeof(**val) * 3);
First time through the loop, allocates memory for three
pointers and sets *val[0] to point to it. Second time through,
allocates memory and sets *val[1] to-- hey, wait a minute!
There's no memory allocated for *val[1] itself, because the
earlier allocation only grabbed memory for one pointer, not
for two! The assignment stomps on unallocated memory, with
unpredictable consequences.
When you get beyond about two levels of indirection, it
may be helpful to get a sheet of paper and a pencil and draw
a diagram of the state of affairs. Draw it as things stand
as of the Big Bang, and then work your way through the program
line by line, updating the diagram as you go. In your case,
you'd start with a two-cell array representing val. At the
first malloc() call, with i==0, you'd draw an arrow from the
[0] element of val pointing to a one-cell newly-allocated area.
At the next malloc() call, you'd draw an arrow from that area
to a three-cell area, and so on. The second time you hit the
second malloc(), the diagram will show you what's wrong.
IMHO, two indirection levels is about as far as you should
go without bringing up some supporting troops. One way to
get some relief from mental overburden is to drag in a few
typedefs, mostly to give your mind something to grab on to.
For example, you might write
typedef char* String;
String *val[NELEM];
I am not a big fan of using typedef to cloak pointer types,
but when the levels proliferate it can be a useful crutch.
--
Eric Sosman
esosman@acm-dot-org.invalid
| |
| Rainer Weikusat 2007-03-27, 4:19 am |
| Frank Cusack <fcusack@fcusack.com> writes:
> Why doesn't this code work?
>
> $ cat test.c
> #include <stdio.h>
> #include <stdlib.h>
>
> #define NELEM 2
>
> int
> main(int argc, char *argv[])
> {
> char **val[NELEM];
> int i;
>
> for (i = 0; i < NELEM; ++i) {
> val[i] = malloc(sizeof(*val));
> *val[i] = malloc(sizeof(**val) * 3);
> // val[i][0] left uninitialized
> val[i][1] = NULL;
> val[i][2] = NULL;
> printf("%p\n", val[0][2]);
> }
> }
> $ gcc test.c
> $ ./a.out
> 0
> 1
> $
>
> It "should" print 0 twice.
It does on the Linux machine I have here.
| |
| Frank Cusack 2007-03-27, 10:08 pm |
| On Tue, 27 Mar 2007 11:08:44 +0200 Rainer Weikusat <rainer.weikusat@sncag.com> wrote:
> Frank Cusack <fcusack@fcusack.com> writes:
....[color=darkred]
>
> It does on the Linux machine I have here.
I bet you have an x86_64 machine?
| |
| Wayne C. Morris 2007-03-27, 10:08 pm |
| In article <m2slbrmpic.fsf@sucksless.local>,
Frank Cusack <fcusack@fcusack.com> wrote:
> Why doesn't this code work?
[snip]
> char **val[NELEM];
> int i;
>
> for (i = 0; i < NELEM; ++i) {
> val[i] = malloc(sizeof(*val));
val is declared as an array, not a pointer. While sizeof(*val) will
probably give the correct value, it'd be clearer to use either
sizeof(char**) or sizeof(val[0]).
> *val[i] = malloc(sizeof(**val) * 3);
Likewise, use either sizeof(char*) or sizeof(*val[0]).
> // val[i][0] left uninitialized
That comment is incorrect. You've just initialized val[i][0] in the
previous statement -- it's synonymous with *val[0].
> val[i][1] = NULL;
> val[i][2] = NULL;
val[i] points to memory which is only big enough to hold one pointer.
val[i][1] and val[i][2] are beyond the end of the allocated memory, so the
results are unpredictable.
What you probably want is:
(*val[i])[1] = NULL;
(*val[i])[2] = NULL;
| |
| Wayne C. Morris 2007-03-27, 10:08 pm |
| In article <O8adnQBwwqXbHZXbnZ2dnUVZ_s-rnZ2d@comcast.com>,
Eric Sosman <esosman@acm-dot-org.invalid> wrote:
>
> Allocates memory for one pointer, and sets val[i] to
> point to it. Fine so far.
[snip]
>
> First time through the loop, allocates memory for three
> pointers and sets *val[0] to point to it. Second time through,
> allocates memory and sets *val[1] to-- hey, wait a minute!
> There's no memory allocated for *val[1] itself
Wrong. It was allocated in the previous statement. You forgot that
subscripting takes precedence over indirection, so the code in his loop is
equivalent to:
char **p;
p = val[i] = malloc(sizeof(*val));
*p = malloc(sizeof(**val) * 3);
| |
| Eric Sosman 2007-03-27, 10:08 pm |
| Wayne C. Morris wrote On 03/27/07 12:26,:
> In article <m2slbrmpic.fsf@sucksless.local>,
> Frank Cusack <fcusack@fcusack.com> wrote:
>
>
>
>
> [snip]
>
>
>
> val is declared as an array, not a pointer. While sizeof(*val) will
> probably give the correct value, it'd be clearer to use either
> sizeof(char**) or sizeof(val[0]).
ITYM sizeof(*val[0])
>
>
>
> Likewise, use either sizeof(char*) or sizeof(*val[0]).
ITYM sizeof(**val[0])
In general, you want
ptr = malloc(sizeof(thing_pointed_at));
(maybe with a "times N" to allocate several, and maybe with
a cast if you need to placate a C++ compiler). The easiest
way to get the size of the thing_pointed_at is to repeat
the name of ptr itself, with a leading asterisk:
ptr = malloc(sizeof *ptr);
If `ptr' is something fancier than an unadorned variable,
just do a straightforward textual substitution, almost as
if you were expanding a macro:
f()->foo[i].glorp = malloc(sizeof *f()->foo[i].glorp);
The only time this incantation breaks down is when the
type of ptr does not match the type of thing_pointed_at:
Maybe ptr is a void*, or maybe it's an int* pointing at the
initial int element of a complicated struct. If that's the
sort of thing you're up to, the incantation won't work --
but rather than abandon the incantation, you might want to
reconsider your reasons for type-punning.
--
Eric.Sosman@sun.com
| |
| Eric Sosman 2007-03-27, 10:08 pm |
| Wayne C. Morris wrote On 03/27/07 13:18,:
> In article <O8adnQBwwqXbHZXbnZ2dnUVZ_s-rnZ2d@comcast.com>,
> Eric Sosman <esosman@acm-dot-org.invalid> wrote:
>
>
>
> [snip]
>
>
>
> Wrong. It was allocated in the previous statement. You forgot that
> subscripting takes precedence over indirection, [...]
Oh, ratzafratz. You're right: I misinterpreted the l.h.s.
as (*val)[i], whereas it's actually *(val[i]). (This must be
why I tend to use either * or [] but not both together -- too
easy to make dumb misteaks.)
--
Eric.Sosman@sun.com
| |
| Wayne C. Morris 2007-03-28, 4:18 am |
| In article <1175018470.519790@news1nwk>, Eric Sosman <Eric.Sosman@sun.com>
wrote:
> Wayne C. Morris wrote On 03/27/07 12:26,:
>
> ITYM sizeof(*val[0])
Oops, you're right. I gave an alternate way of writing what the OP had,
and forgot to check whether it was appropriate.
On the other hand, it looked like the OP wanted the second malloc to
allocate enough memory to store 3 pointers; if so, the sizeof's were
correct and the definition of val was wrong.
As you said in your previous post, drawing a diagram would help to figure
out what's really desired, and using typedef's where appropriate would make
the code easier to comprehend.
| |
| Rainer Weikusat 2007-03-28, 8:15 am |
| "Wayne C. Morris" <wayne.morris@this.is.invalid> writes:
> In article <m2slbrmpic.fsf@sucksless.local>,
> Frank Cusack <fcusack@fcusack.com> wrote:
>
>
> [snip]
>
> val is declared as an array, not a pointer. While sizeof(*val) will
> probably give the correct value, it'd be clearer to use either
> sizeof(char**) or sizeof(val[0]).
Except for three specific exceptions listed in 6.3.2.1|2, an
expression that has the type 'array of type' is always converted to
the type 'pointer to type'. One of the operands of [] is required to
have a pointer type, not 'array of type'. This means the val in *val
is a pointer and the val in val[0] is a pointer, too.
|
|
|
|
|