arrays and K&R §5.10

  • Thread starter Merrill & Michele
  • Start date
M

Merrill & Michele

What follows is an adaptation of the second program in K&R §5.10. The
changes are to elucidate (validate) the difference (sameness) of char *[]
and char**. I cannot for the life of me understand why the output looks the
way it does, in particular, with all the symmetry in arguments, why one sees
apple[0] but not argv[0]. This program was designed to run from a command
line with one argument (two if you count the prog name). The .c file
follows. Afterwards I post a link to a screenshot of output.

#include <stdio.h>

int main(int argc, char **argv)
{
char **apple;

apple[0]="tja zero";
apple[1]="tja again";

while (--argc >0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");

printf("%s%s", *++apple, (argc > 1) ? " " : "");
printf("\n");
return (0);
}

http://home.comcast.net/~beckjensen/cstuff3.htm

++thanks. MPJ
 
C

Chris Dollin

Merrill said:
What follows is an adaptation of the second program in K&R §5.10. The
changes are to elucidate (validate) the difference (sameness) of char *[]
and char**. I cannot for the life of me understand why the output looks
the way it does, in particular, with all the symmetry in arguments, why
one sees
apple[0] but not argv[0]. This program was designed to run from a command
line with one argument (two if you count the prog name). The .c file
follows. Afterwards I post a link to a screenshot of output.

#include <stdio.h>

int main(int argc, char **argv)
{
char **apple;

apple[0]="tja zero";

`apple` is uninitialised. Assigning to `apple[0]` results in
undefined behaviour. The program is broken from here on.
apple[1]="tja again";

while (--argc >0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");

You do realise that the loop body finished here, ie, consisted
only of the single printf?
printf("\n");

printf("%s%s", *++apple, (argc > 1) ? " " : "");

Supposing that apple *were* properly initialised, you would skip
its first element (ie `apple[0]` aka `*apple`).
printf("\n");
return (0);

Unnecessary brackets.

Don't do that. Especially since jpegs are now an attack vector.
 
M

Merrill & Michele

What follows is an adaptation of the second program in K&R §5.10. The
changes are to elucidate (validate) the difference (sameness) of char *[]
and char**. I cannot for the life of me understand why the output looks the
way it does, in particular, with all the symmetry in arguments, why one sees
apple[0] but not argv[0]. This program was designed to run from a command
line with one argument (two if you count the prog name). The .c file
follows. Afterwards I post a link to a screenshot of output.

#include <stdio.h>

int main(int argc, char **argv)
{
char **apple;

apple[0]="tja zero";
apple[1]="tja again";

while (--argc >0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");

printf("%s%s", *++apple, (argc > 1) ? " " : "");
printf("\n");
return (0);
}

http://home.comcast.net/~beckjensen/cstuff3.htm

++thanks. MPJ

....sheepishly...
I figured out my problem one minute after I posted, namely, that I can't
read my own screen. I did, however, have secondary and tertiary questions
here. I have never written a program that did not specify how large a
matrix is to be. What does an ANSI-compliant compiler set aside in main
memory for apple[] in this code? Please don't tell me to read the standard.
That I read so much in that vein this weekend is the reason that I can't
read my screen now. MPJ
 
M

Mabden

Merrill & Michele said:
What follows is an adaptation of the second program in K&R §5.10. The
changes are to elucidate (validate) the difference (sameness) of char *[]
and char**. I cannot for the life of me understand why the output
looks
the
way it does, in particular, with all the symmetry in arguments, why
one
sees
apple[0] but not argv[0]. This program was designed to run from a command
line with one argument (two if you count the prog name). The .c file
follows. Afterwards I post a link to a screenshot of output.

#include <stdio.h>

int main(int argc, char **argv)
{
char **apple;

apple[0]="tja zero";
apple[1]="tja again";

while (--argc >0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");

printf("%s%s", *++apple, (argc > 1) ? " " : "");
printf("\n");
return (0);
}

http://home.comcast.net/~beckjensen/cstuff3.htm

++thanks. MPJ

...sheepishly...
I figured out my problem one minute after I posted, namely, that I can't
read my own screen. I did, however, have secondary and tertiary questions
here. I have never written a program that did not specify how large a
matrix is to be. What does an ANSI-compliant compiler set aside in main
memory for apple[] in this code? Please don't tell me to read the standard.
That I read so much in that vein this weekend is the reason that I can't
read my screen now. MPJ

I'm not sure what you are trying to prove, but you have a single pointer
(apple) and you are setting it using [0] and [1] as if you had an array.
A char pointer pointer can point to an array of pointers, but it is one
pointer, not an array of pointers. It points to arrays of char, but it
is one pointer - there is no apple[1].
 
J

Jens.Toerring

Merrill & Michele said:
What follows is an adaptation of the second program in K&R §5.10. The
changes are to elucidate (validate) the difference (sameness) of char *[]
and char**. I cannot for the life of me understand why the output looks
the way it does, in particular, with all the symmetry in arguments, why one
sees apple[0] but not argv[0]. This program was designed to run from a
command line with one argument (two if you count the prog name). The .c
file follows. Afterwards I post a link to a screenshot of output.

#include <stdio.h>

int main(int argc, char **argv)
{
char **apple;

apple[0]="tja zero";
apple[1]="tja again";

while (--argc >0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");

printf("%s%s", *++apple, (argc > 1) ? " " : "");
printf("\n");
return (0);
}

http://home.comcast.net/~beckjensen/cstuff3.htm

++thanks. MPJ
...sheepishly...
I figured out my problem one minute after I posted, namely, that I can't
read my own screen. I did, however, have secondary and tertiary questions
here. I have never written a program that did not specify how large a
matrix is to be. What does an ANSI-compliant compiler set aside in main
memory for apple[] in this code?

You don't have any "apple[]" in your program. 'apple' is a pointer to
a char pointer. It's _NOT_ an array, it's just a pointer and all the
compiler sets aside for you is enough space to store a single pointer
to a pointer to char. Not more and not less. To be able to use it
like an array you have to first allocate memory it points to.

Moreover, after the definition it points to some random location in
memory. But then you try to assign a value to what you suddenly start
treating as an array of char pointers, i.e. you try to push values into
the the random place it's pointing to. If you're unlucky, this might
even seem to work because what 'apple' is pointing to is random and so
by chance it could point to some location you can change and you then
happily write into some memory you don't own, possibly overwriting
some important other data. If you're lucky and it points to some
memory you're not allowed to change you get a segmentation fault and
then you at least know immediately that you made a bad mistake.

To use 'apple' in the way you try to do it you first have to obtain
some memory it's pointing to. For that you need malloc(). You would
e.g. do:

char **apple;

apple = malloc( 2 * sizeof *apple );
if ( apple == NULL ) {
fprintf( stderr, "Not enought memory.\n" );
exit( EXIT_FAILURE );
}

Thereby you now have the 'apple' pointer initialized to point to
2 char pointers and you now can proceed with your assignments:

apple[ 0 ] = "tja zero";
apple[ 1 ] = "tja again";

If you should later in your program realize that you need a larger
(or smaller) array of char pointers you could use realloc() to
change the size of the memory area 'apple' is pointing to.

It's considered good practise to clean up after yourself, so when
you don't need the memory 'apple' is pointing to anymore you should
call free() on it. To do that you must pass the same value you got
from malloc() to free() and therefore one usually would try to avoid
changing 'apple' at all. Since you don't know in advance how many
command line arguments there are going to be you don't know at the
end if 'apple' has been incremented twice or not at all, so you
aren't able to free() the memory you allocated. So you created what
is commonly called a memory leak - you lost the information about
memory you allocated and are unable to deallocate it, so, if you
would have a long program where the code would be called again and
again, the program would use more and more memory without really
needing it.
Regards, Jens
 
M

Merrill & Michele

"Merrill & Michele" wrote .of
char *[]
and char**. I cannot for the life of me understand why the output
looks
the
way it does, in particular, with all the symmetry in arguments, why
one
sees
apple[0] but not argv[0]. This program was designed to run from a command
line with one argument (two if you count the prog name). The .c file
follows. Afterwards I post a link to a screenshot of output.

#include <stdio.h>

int main(int argc, char **argv)
{
char **apple;

apple[0]="tja zero";
apple[1]="tja again";

while (--argc >0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");

printf("%s%s", *++apple, (argc > 1) ? " " : "");
printf("\n");
return (0);
}

http://home.comcast.net/~beckjensen/cstuff3.htm

++thanks. MPJ

...sheepishly...
>>What does an ANSI-compliant compiler set aside in
main
memory for apple[] in this code?

You don't have any "apple[]" in your program. 'apple' is a pointer to
a char pointer. It's _NOT_ an array, it's just a pointer and all the
compiler sets aside for you is enough space to store a single pointer
to a pointer to char. Not more and not less. To be able to use it
like an array you have to first allocate memory it points to.

Moreover, after the definition it points to some random location in
memory. But then you try to assign a value to what you suddenly start
treating as an array of char pointers, i.e. you try to push values into
the the random place it's pointing to. If you're unlucky, this might
even seem to work because what 'apple' is pointing to is random and so
by chance it could point to some location you can change and you then
happily write into some memory you don't own, possibly overwriting
some important other data. If you're lucky and it points to some
memory you're not allowed to change you get a segmentation fault and
then you at least know immediately that you made a bad mistake.

To use 'apple' in the way you try to do it you first have to obtain
some memory it's pointing to. For that you need malloc(). You would
e.g. do:

char **apple;

apple = malloc( 2 * sizeof *apple );
if ( apple == NULL ) {
fprintf( stderr, "Not enought memory.\n" );
exit( EXIT_FAILURE );
}

Thereby you now have the 'apple' pointer initialized to point to
2 char pointers and you now can proceed with your assignments:

apple[ 0 ] = "tja zero";
apple[ 1 ] = "tja again";

If you should later in your program realize that you need a larger
(or smaller) array of char pointers you could use realloc() to
change the size of the memory area 'apple' is pointing to.

It's considered good practise to clean up after yourself, so when
you don't need the memory 'apple' is pointing to anymore you should
call free() on it. To do that you must pass the same value you got
from malloc() to free() and therefore one usually would try to avoid
changing 'apple' at all. Since you don't know in advance how many
command line arguments there are going to be you don't know at the
end if 'apple' has been incremented twice or not at all, so you
aren't able to free() the memory you allocated. So you created what
is commonly called a memory leak - you lost the information about
memory you allocated and are unable to deallocate it, so, if you
would have a long program where the code would be called again and
again, the program would use more and more memory without really
needing it.
Regards, Jens

Thanks for replies: Mr. Dollin, Mabden, Dr. Toerring (I assume you
defended.)

Ouch. When I stumbled into undefined behavior, I was expecting Scott Nudds
to fly out of my hard drive. I was extraordinarily surprised that this
program compiled. Does it say something about my compiler or its settings
that it didn't tell me, "Dude you're stoned."

As for Mr. Dollin's comment on the screenshot:My .jpegs attack no one. They help other people to point out where I'm
being a complete ninny. MPJ
 
C

Chris Torek

<[email protected]>
[wrote -- message-ID missing, but from references: line it must have
[more snippage, including test for failure]

It is not absolutely *required* that one use malloc() here. For
instance, this will work just as well:

char **apple;
char *mem[2];

apple = mem;

Unless "apple" ever changes, though, there is little point to
introducing it as an auxiliary -- one might as well just use mem[0]
and mem[1] directly.

The big advantage to malloc() is that you can pass it a number that
is at least partly determined at runtime. The size "2" in the
above must be a constant in C89. (C99 does allow variable sizes,
at least for ordinary local arrays allocated at block entry,
producing "variable length arrays" or VLAs. The precise constraints
on VLAs are a little hard to describe.)

Ouch. When I stumbled into undefined behavior, I was expecting Scott Nudds
to fly out of my hard drive. I was extraordinarily surprised that this
program compiled. Does it say something about my compiler or its settings
that it didn't tell me, "Dude you're stoned."

Better compilers will generally print warnings for "obvious" cases
of undefined behavior (though perhaps only by specific request, as
is often true for gcc: -O and -Wuninitialized are both required
here, although -Wuninitialized can be requested under the general
"-Wall" group).

Undefined behavior is allowed to do *anything*, though, including
the most insidious of all: appear to work as intended, just until
it becomes important that it actually work.
As for Mr. Dollin's comment on the screenshot:
My .jpegs attack no one. They help other people to point out where I'm
being a complete ninny. MPJ

He is referring to Yet Another Bug in Microsoft-ware. One of the
MS-Windows dynamic libraries that decodes JPG files has Yet Another
Buffer Overflow in it (well, at least we cannot blame gets() for
this one :) ). This buffer overflow -- probably resulting from
C code with undefined behavior! -- allows attackers to run arbitrary
executable code on Microsoft Windows boxes whenever the unsuspecting
user views a JPG image.

Given the prevalence of JPGs, it is not clear what to do about this
in general, other than "not use Microsoft-ware". :) That particular
DLL has an MS-provided fix, but one must be aware that the fix only
fixes one copy; there may be other copies and/or other versions of
that DLL in use, and those will continue to have the bug. Thus,
if you use Windows, you may be taking a risk every time you view
a JPG image (unless you know for sure which program(s) will be
using a corrected DLL). If the image is stored on a compromised
Microsoft server, even a "known-pedigree" JPG image may have had
virus or worm code loaded into it after the fact.

In this particular case, however, the "thing to do" about it is to
use nothing but plain text in comp.lang.c.
 
J

Jens.Toerring

Merrill & Michele said:
Ouch. When I stumbled into undefined behavior, I was expecting Scott Nudds
to fly out of my hard drive. I was extraordinarily surprised that this
program compiled. Does it say something about my compiler or its settings
that it didn't tell me, "Dude you're stoned."

No. First of all, C gives you enough rope to hang yourself and
that's for a good reason: sometimes you need that much rope. And
then undefined behaviour isn't always something bad. Writing e.g.
to memory you don't own (i.e. never allocated) is sometimes even
necessary. If you're writing a device driver you may have to write
into certain memory locations you know registers of the device are
mapped to. Of course, the C standard can't know about that, so it
is undefined behaviour from a standard C point of view - but it's
(hopefully) very well defined what's going to happen by the the
documentation for the machine/device you're doing that on. So the
compiler has no reason to complain about it.

Actually, undefined behaviour just means what it says - the be-
haviour is not defined by the C standard. That doesn't mean that
Scott Nudds is going to do something indecent with your hard drive.
It's just one of the many (less likely) possibilities. What's im-
portant is to know what's defined and what's undefined so that one
can avoid the later unless one has to use it because one knows what
is going to happen (from some other information, not the C standard)
and there's no other good method to make that happen.

Regards, Jens
 
J

Jack Klein

What follows is an adaptation of the second program in K&R §5.10. The
changes are to elucidate (validate) the difference (sameness) of char *[]
and char**. I cannot for the life of me understand why the output looks the
way it does, in particular, with all the symmetry in arguments, why one sees
apple[0] but not argv[0]. This program was designed to run from a command
line with one argument (two if you count the prog name). The .c file
follows. Afterwards I post a link to a screenshot of output.

#include <stdio.h>

int main(int argc, char **argv)
{
char **apple;

apple[0]="tja zero";
apple[1]="tja again";

while (--argc >0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");

printf("%s%s", *++apple, (argc > 1) ? " " : "");
printf("\n");
return (0);
}

http://home.comcast.net/~beckjensen/cstuff3.htm

++thanks. MPJ

...sheepishly...
I figured out my problem one minute after I posted, namely, that I can't
read my own screen. I did, however, have secondary and tertiary questions
here. I have never written a program that did not specify how large a
matrix is to be. What does an ANSI-compliant compiler set aside in main
memory for apple[] in this code? Please don't tell me to read the standard.
That I read so much in that vein this weekend is the reason that I can't
read my screen now. MPJ

The term is 'array', not 'matrix'. There are some languages that have
native support for a matrix data type, but C is not among them. C
does not even really have multi-dimensional arrays, merely arrays of
arrays. The array in C is very strictly a linear type.

As for how much memory is allocated for apple[], there is no
'apple[]', there is a pointer named 'apple', which is NOT an array.

POINTER RULE 1: Defining a pointer in C DOES NOT create anything for
the pointer to point to, nor does it point the pointer at anything at
all.

POINTER RULE 2: Never forget pointer rule 1.
 
D

Dan Pop

In said:
The term is 'array', not 'matrix'. There are some languages that have
native support for a matrix data type, but C is not among them. C
does not even really have multi-dimensional arrays, merely arrays of
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
arrays. The array in C is very strictly a linear type.

Although I happen to agree with Jack on this one, the C standard itself
seems to suggest the opposite:

3 Successive subscript operators designate an element of a
multidimensional array object. If E is an n-dimensional array
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
(n >= 2) with dimensions i × j × . . . × k, then E (used as
other than an lvalue) is converted to a pointer to an (n -
1)-dimensional array with dimensions j × . . . × k. If the unary
* operator is applied to this pointer explicitly, or implicitly
as a result of subscripting, the result is the pointed-to (n -
1)-dimensional array, which itself is converted into a pointer if
used as other than an lvalue. It follows from this that arrays
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
are stored in row-major order (last subscript varies fastest).
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Dan
 

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

Similar Threads

K&R 1-24 15
Command Line Arguments 0
How to build a char[][] in k&r dialect. 7
p 145 K&R 14
Problematic code in K&R 1
K&R Exercise 1-21: entab 10
K&R hash table question 16
K$R xrcise 1-13 (histogram) 4

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,216
Latest member
topweb3twitterchannels

Latest Threads

Top