Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Interpreting command lines.

4 views
Skip to first unread message

frank

unread,
Jan 20, 2010, 2:27:07 PM1/20/10
to
I was hoping to follow the development of 2-d resizable arrays in _C
Unleashed_. The material is difficult for me, and doing it on a
relatively new to me platform has been a series of pratfalls. It's
chapter 11, and I'm looking at 11-18. The readme.txt has the following
suggestions for command lines:

Unix/Linux/DJGPP:

gcc -Wall -ansi -pedantic -c -o foo.o foo.c [-lm]

Microsoft C (any 16-bit version including VC1.0 and 1.5):

cl -AL -W4 -Za -Zi -Od -c foo.c
...

I think the [-lm] indicates something optional. Am I correct to think
that if I don't have a foo.c, then I won't compile my own foo.o?

Am I also correct to think that compiling a program that has main() in
it as above is a stupid thing to do?
--
frank

Keith Thompson

unread,
Jan 20, 2010, 3:12:53 PM1/20/10
to
frank <fr...@example.invalid> writes:
> I was hoping to follow the development of 2-d resizable arrays in _C
> Unleashed_. The material is difficult for me, and doing it on a
> relatively new to me platform has been a series of pratfalls. It's
> chapter 11, and I'm looking at 11-18. The readme.txt has the
> following suggestions for command lines:
>
> Unix/Linux/DJGPP:
>
> gcc -Wall -ansi -pedantic -c -o foo.o foo.c [-lm]
>
> Microsoft C (any 16-bit version including VC1.0 and 1.5):
>
> cl -AL -W4 -Za -Zi -Od -c foo.c
> ...
>
> I think the [-lm] indicates something optional.

Yes, the square brackets in this context mean that it's optional.
The "-lm" option, on some compilers (including most Unix compilers)
tells the linker to use the math library. It's not necessary if
you're not using any functions defined in the math library (which
functions those are is implementation-specific, but it's typically
close to what's declare in <math.h>), or if the implementation is
smart enough not to need the option. The reasons for this are
historical; at one time, implementations weren't clever enough
to figure out on their on whether the math library is needed,
and omitting it by default could save significant time and space.
These are mostly irrelevant for modern systems, but the behavior
lives on.

> Am I correct to think
> that if I don't have a foo.c, then I won't compile my own foo.o?

I don't know; that depends on what you mean by that. The example
certainly assumes that you have a "foo.c" file. "foo.c" is the
input to the compiler; "foo.o" is the output.

Can you rephrase the question?

> Am I also correct to think that compiling a program that has main() in
> it as above is a stupid thing to do?

No, why on Earth would you think so?

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

frank

unread,
Jan 20, 2010, 3:31:57 PM1/20/10
to

Is it an accurate statement that gcc is C99-compliant?


>
>> Am I correct to think
>> that if I don't have a foo.c, then I won't compile my own foo.o?
>
> I don't know; that depends on what you mean by that. The example
> certainly assumes that you have a "foo.c" file. "foo.c" is the
> input to the compiler; "foo.o" is the output.
>
> Can you rephrase the question?

I'm almost too embarrassed to admit this. Matter of fact, I am too
embarrassed; suffice it to say that this part of the question involved a
misunderstanding of what Heathfield wrote in 2000 as opposed to what I
compiled last night. I cleared this one up by
ls -l


>
>> Am I also correct to think that compiling a program that has main() in
>> it as above is a stupid thing to do?
>
> No, why on Earth would you think so?
>

Because I can't get gcc to #include a home-rolled* header in the same
place as is the source for main.
--
frank
*home-rolled somewhere in Britain

Keith Thompson

unread,
Jan 20, 2010, 3:54:17 PM1/20/10
to

First of all, the "-lm" option has nothing to do with C99 compliance.

Second, no, gcc is not entirely C99-compliant, though it's close. See
<http://gcc.gnu.org/c99status.html> for the current status. Follow
the links to see the status for the version you're using. Remember
that library issues are independent of gcc.

[...]

>>> Am I also correct to think that compiling a program that has main() in
>>> it as above is a stupid thing to do?
>>
>> No, why on Earth would you think so?
>>
>
> Because I can't get gcc to #include a home-rolled* header in the same
> place as is the source for main.

It's nearly impossible to tell what the problem is without seeing code.
My wild guess is that you're using
#include <myheader.h>
when you should be using
#include "myheader.h"

frank

unread,
Jan 20, 2010, 6:15:43 PM1/20/10
to

That's a good page; I bookmarked it. I'm not all that well informed
about the hiccups in differing vendors' C99 implementations. I'm
surprised one doesn't hear more about this effort in clc.


> It's nearly impossible to tell what the problem is without seeing code.
> My wild guess is that you're using
> #include <myheader.h>
> when you should be using
> #include "myheader.h"

That's not it. BTW, in #include "myheader.h", MUST a compiler include a
myheader.h in the same directory?

The first command line folds over in an unfortunate way (there is only a
space between -Wextra and t1.c):

dan@dan-desktop:~/source/unleashed/ch11$ gcc -std=c99 -Wall -Wextra
t1.c -o out/tmp/ccQ5ko8j.o: In function `ReadFile':
t1.c:(.text+0x7e): undefined reference to `AllocStrArray'
t1.c:(.text+0x147): undefined reference to `AddRowsToStrArray'
t1.c:(.text+0x187): undefined reference to `ResizeOneString'
t1.c:(.text+0x203): undefined reference to `AddRowsToStrArray'
t1.c:(.text+0x246): undefined reference to `FreeStrArray'
t1.c:(.text+0x269): undefined reference to `ConsolidateStrArray'
/tmp/ccQ5ko8j.o: In function `main':
t1.c:(.text+0x322): undefined reference to `FreeStrArray'
collect2: ld returned 1 exit status
dan@dan-desktop:~/source/unleashed/ch11$ cat t1.c
/* c11_018.c - read a text file into memory
*
* C11_018 - dynamic allocation of arrays
*
* Copyright (C) 2000 Richard Heathfield
* Eton Computer Systems Ltd
* Macmillan Computer Publishing
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, write
* to the Free Software Foundation, Inc., 675 Mass Ave,
* Cambridge, MA 02139, USA.
*
* Richard Heathfield may be contacted by email at:
* bin...@eton.powernet.co.uk
*
*/

#include <stdio.h>
#include <string.h>

#include "strarr.h"

#define DEFAULT_LINE_LEN 64
#define LINES_PER_ALLOC 16
#define ERR_ROWS_NOT_ADDED 1
#define ERR_STRING_NOT_RESIZED 2
#define ERR_FILE_OPEN_FAILED 3
#define ERR_ALLOC_FAILED 4

int ReadFile(char *Filename,
char ***Array,
int *NumRows)
{
char Buffer[DEFAULT_LINE_LEN] = {0};
char *NewLine = NULL;
FILE *fp;
int Error = 0;
int Row = 0;
size_t NumBlocks;

*NumRows = 0;

*Array = AllocStrArray(LINES_PER_ALLOC,
DEFAULT_LINE_LEN);
if(NULL != *Array)
{
fp = fopen(Filename, "r");
if(fp != NULL)
{
*NumRows = LINES_PER_ALLOC;
NumBlocks = 1;

/* fgets will give us no more than sizeof Buffer
* bytes, including zero terminator and newline
* if one is present within that number of bytes.
* Therefore we need to cater for longer lines.
* To do this, we call fgets again (and again
* and again) until we encounter a newline.
*/
while(0 == Error &&
NULL != fgets(Buffer, sizeof Buffer, fp))
{
NewLine = strchr(Buffer, '\n');
if(NewLine != NULL)
{
*NewLine = '\0';
}
/* This strcat relies on the AllocStrArray()
* function initialising rows to empty strings.
*/
strcat((*Array)[Row], Buffer);
if(NewLine != NULL)
{
/* There was a newline, so the
* next line is a new one.
*/
NumBlocks = 1;
++Row;
if(Row >= *NumRows)
{
/* Add another LINES_PER_ALLOC lines.
* If it didn't work, give up.
*/
if(0 == AddRowsToStrArray(Array,
*NumRows,
LINES_PER_ALLOC,
DEFAULT_LINE_LEN))
{
Error = ERR_ROWS_NOT_ADDED;
}
else
{
*NumRows += LINES_PER_ALLOC;
}
}
}
else
{
++NumBlocks;
/* Make room for some more data on this line */
if(0 ==
ResizeOneString(*Array,
Row,
NumBlocks * DEFAULT_LINE_LEN))
{
Error = ERR_STRING_NOT_RESIZED;
}
}
}
fclose(fp);
if(0 == Error && *NumRows > Row)
{
if(0 == AddRowsToStrArray(Array,
*NumRows,
Row - *NumRows,
0))
{
Error = ERR_ALLOC_FAILED;
}
*NumRows = Row;
}
}
else
{
Error = ERR_FILE_OPEN_FAILED; /* Can't open file */
}
}
else
{
Error = ERR_ALLOC_FAILED; /* Can't allocate memory */
}
if(Error != 0)
{
/* If the original allocation failed,
* *Array will be NULL. FreeStrArray()
* correctly handles this possibility.
*/
FreeStrArray(*Array, *NumRows);
*NumRows = 0;
}
else
{
ConsolidateStrArray(*Array, *NumRows);
}

return Error;
}

int main(int argc, char **argv)
{
char **array = NULL;

int rows;
int thisrow;
int error;

if(argc > 1)
{
error = ReadFile(argv[1], &array, &rows);
switch(error)
{
case 0:
for(thisrow = rows - 1; thisrow >= 0; thisrow--)
{
printf("%s\n", array[thisrow]);
}

FreeStrArray(array, rows);
break;
case ERR_STRING_NOT_RESIZED:
case ERR_ALLOC_FAILED:
case ERR_ROWS_NOT_ADDED:
puts("Insufficient memory.");
break;
case ERR_FILE_OPEN_FAILED:
printf("Couldn't open %s for reading\n", argv[1]);
break;
default:
printf("Unknown error! Code %d.\n", error);
break;
}
}
else
{
puts("Please specify the text file name.");
}

return 0;
}
/* end of c11_018.c */

/* gcc -Wall -ansi -pedantic -o out t1.c
gcc -std=c99 -Wall -Wextra t1.c -o out */
dan@dan-desktop:~/source/unleashed/ch11$ cat strarr.h
/* strarr.h - header for string array demo
*
* STRARR - dynamic allocation of arrays of strings
*
* Copyright (C) 2000 Richard Heathfield
* Eton Computer Systems Ltd
* Macmillan Computer Publishing
*
* This program is free software; you can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software
* Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General
* Public License along with this program; if not, write
* to the Free Software Foundation, Inc., 675 Mass Ave,
* Cambridge, MA 02139, USA.
*
* Richard Heathfield may be contacted by email at:
* bin...@eton.powernet.co.uk
*
*/

#ifndef STRARR_H__
#define STRARR_H__

void FreeStrArray(char **Array, size_t NumRows);
char **AllocStrArray(size_t NumRows, size_t Width);
int ResizeOneString(char **Array,
size_t Row,
size_t NewSize);
int AddRowsToStrArray(char ***ArrayPtr,
size_t OldNumRows,
int NumRowsToAdd,
size_t InitWidth);
int ConsolidateStrArray(char **ArrayPtr,
size_t NumRows);

#endif
dan@dan-desktop:~/source/unleashed/ch11$

I apologize for the product of the length of this post times whatever
idiocy my misunderstanding must represent.
--
frank


frank

unread,
Jan 20, 2010, 6:40:18 PM1/20/10
to
frank wrote:

> I apologize for the product of the length of this post times whatever
> idiocy my misunderstanding must represent.

I thought I'd try something different with the files and now see that I
was to do as follows. As a first test, I thought I would read the file
that I had been leaving out, which is strarr.c. The good news is that I
have output. The bad news is that it's exactly backwards linewise:

dan@dan-desktop:~/source/unleashed/ch11$ gcc -Wall -Wextra -c -o
string.o strarr.c


dan@dan-desktop:~/source/unleashed/ch11$ gcc -std=c99 -Wall -Wextra

string.o t1.c -o out
dan@dan-desktop:~/source/unleashed/ch11$ ./out strarr.c
gcc -Wall -Wextra -c -o string.o strarr.c */
/*
/* end of strarr.c */

}
return NumFailures;
}
}
++NumFailures;
{
if(0 == ResizeOneString(ArrayPtr, Row, Len))
Len = 1 + strlen(ArrayPtr[Row]);
assert(ArrayPtr[Row] != NULL);
*/
* assert that this is the case.
* row pointer will ever be NULL, so we should
/* If the library has been correctly used, no
{
for(Row = 0; Row < NumRows; Row++)

int NumFailures = 0;
size_t Len;
size_t Row;
{
size_t NumRows)
int ConsolidateStrArray(char **ArrayPtr,

}
return Success;
}
Success = 0;
{
else
}
}
}
Success = 0;
{
else
}
(*ArrayPtr)[Row][0] = '\0';
{
if((*ArrayPtr)[Row] != NULL)
(*ArrayPtr)[Row] = malloc(InitWidth);
{
Row++)
Success && Row < OldRows + NumRowsToAdd;
for(Row = OldRows;

*ArrayPtr = p;
{
if(p != NULL)

sizeof(**ArrayPtr));
(OldRows + NumRowsToAdd) *
p = realloc(*ArrayPtr,

}
}
free((*ArrayPtr)[Row]);
{
Row--)
Row >= OldRows + NumRowsToAdd;
for(Row = OldRows - 1;
{
if(NumRowsToAdd < 0)
OldRows = (int)OldNumRows;

int OldRows;
int Row;
int Success = 1;
char **p;
{
size_t InitWidth)
int NumRowsToAdd,
size_t OldNumRows,
int AddRowsToStrArray(char ***ArrayPtr,

}
return Success;

}
Success = 0;
{
else
}
Array[Row] = p;
{
if(p != NULL)
p = realloc(Array[Row], NewSize);

assert(Array != NULL);

int Success = 1;
char *p;
{
size_t NewSize)
size_t Row,
int ResizeOneString(char **Array,

}
return Array;

}
}
}
Array = NULL;
FreeStrArray(Array, NumRows);
{
if(1 != Success)
*/
* we should clean up.
/* If any inner allocation failed,
}
}
Array[Row][0] = '\0';
*/
* we might as well do it.
* Thing and can never be The Wrong Thing, so
* op which will almost invariably be The Right
/* Making this into an empty string is a quick
{
else
}
Success = 0;
{
if(NULL == Array[Row])
Array[Row] = malloc(Width * sizeof *Array[Row]);
{
for(Row = 0; Row < NumRows; Row++)
{
if(Array != NULL)
Array = malloc(NumRows * sizeof *Array);
{
if(NumRows > 0 && Width > 0)
*/
* in testing, we'll check for it here.
/* Just in case the zero allocation is NOT caught

assert(Width > 0);
assert(NumRows > 0);
*/
* represents a logic error.
/* allocating 0 bytes is not a great idea, and

int Success = 1;
size_t Row;
char **Array = NULL;
{


char **AllocStrArray(size_t NumRows, size_t Width)

}
}
free(Array);
}
}
free(Array[Row]);
{
if(Array[Row] != NULL)
{
for(Row = 0; Row < NumRows; Row++)
{
if(Array != NULL)

size_t Row;
{


void FreeStrArray(char **Array, size_t NumRows)

#include "strarr.h"

#include <assert.h>
#include <string.h>
#include <stdlib.h>

*/
*
* bin...@eton.powernet.co.uk


* Richard Heathfield may be contacted by email at:
*
* Cambridge, MA 02139, USA.

* to the Free Software Foundation, Inc., 675 Mass Ave,
* Public License along with this program; if not, write

* You should have received a copy of the GNU General
*
* for more details.

* PARTICULAR PURPOSE. See the GNU General Public License
* implied warranty of MERCHANTABILITY or FITNESS FOR A

* be useful, but WITHOUT ANY WARRANTY; without even the
* This program is distributed in the hope that it will
*

* (at your option) any later version.
* Foundation; either version 2 of the License, or

* Public License as published by the Free Software
* and/or modify it under the terms of the GNU General

* This program is free software; you can redistribute it
*
* Macmillan Computer Publishing

* Eton Computer Systems Ltd
* Copyright (C) 2000 Richard Heathfield
*

* STRARR - dynamic allocation of arrays

*
/* strarr.c - resizeable string array
dan@dan-desktop:~/source/unleashed/ch11$

Tja.
--
frank

Keith Thompson

unread,
Jan 20, 2010, 6:38:40 PM1/20/10
to
frank <fr...@example.invalid> writes:
> Keith Thompson wrote:
[...]

>> It's nearly impossible to tell what the problem is without seeing code.
>> My wild guess is that you're using
>> #include <myheader.h>
>> when you should be using
>> #include "myheader.h"
>
> That's not it. BTW, in #include "myheader.h", MUST a compiler include
> a myheader.h in the same directory?

#include <myheader.h> searches for the header in an
implementation-defined sequence of places.

#include "myheader.h" searches for the header file in another
implementation-defined sequence of places; if that fails, it then
proceeds as if you has written #include <myheader.h>.

See the C standard for a more accurate description.

So no, there's no requirement in the standard that #include
"myheader.h" searches in the current directory -- but gcc does, and
that's not your problem.

> The first command line folds over in an unfortunate way (there is only
> a space between -Wextra and t1.c):
>
> dan@dan-desktop:~/source/unleashed/ch11$ gcc -std=c99 -Wall -Wextra
> t1.c -o out/tmp/ccQ5ko8j.o: In function `ReadFile':
> t1.c:(.text+0x7e): undefined reference to `AllocStrArray'
> t1.c:(.text+0x147): undefined reference to `AddRowsToStrArray'
> t1.c:(.text+0x187): undefined reference to `ResizeOneString'
> t1.c:(.text+0x203): undefined reference to `AddRowsToStrArray'
> t1.c:(.text+0x246): undefined reference to `FreeStrArray'
> t1.c:(.text+0x269): undefined reference to `ConsolidateStrArray'
> /tmp/ccQ5ko8j.o: In function `main':
> t1.c:(.text+0x322): undefined reference to `FreeStrArray'
> collect2: ld returned 1 exit status

You said gcc was having problems with a #include. It's not. These
are linker errors.

[big snip]


> /* end of c11_018.c */
>
> /* gcc -Wall -ansi -pedantic -o out t1.c
> gcc -std=c99 -Wall -Wextra t1.c -o out */

You're writing the object file for t1.c to "out". The convention is
to write the object file for t1.c to t1.o -- and this is what gcc
does by default with the "-c" option.

[...]

Ok, so t1.c contains a couple of functions, one of which is main, and
strarr.h contains declarations (*not* definitions) for several other
functions. You don't have (or haven't shown us) anything that
*defines* those functions. And those are the functions the linker is
complaining about.

For an application like this, I'd expect t1.c to contain the main
program, strarr.h to contain function declarations, and strarr.c to
contain function definitions. Both strarr.c and t1.c would have
#include "strarr.h"
One way to create the executable would be:

gcc -c strarr.c # Creates strarr.o
gcc -c t1.c # Creates t1.o
gcc strarr.o t1.o -o t1 # Invokes the linker, creates t1, the executable

A more abbreviated method would be:

gcc strarr.c t1.c -o t1

Most other compilers are likely to offer similar methods, but perhaps
with different options.

frank

unread,
Jan 31, 2010, 2:53:39 AM1/31/10
to
On Jan 20, 4:38 pm, Keith Thompson <ks...@mib.org> wrote:

> Ok, so t1.c contains a couple of functions, one of which is main, and
> strarr.h contains declarations (*not* definitions) for several other
> functions.  You don't have (or haven't shown us) anything that
> *defines* those functions.  And those are the functions the linker is
> complaining about.

I had always struggled with the difference between declaration and
definition. Apparently the definition is where the code is.

--
frank

Richard Heathfield

unread,
Jan 31, 2010, 3:24:04 AM1/31/10
to
frank wrote:
>
> I had always struggled with the difference between declaration and
> definition. Apparently the definition is where the code is.

All definitions are declarations. Not all declarations are definitions.

A declaration is merely a promise ("I declare to you") that an object
(or function) exists. You make the promise, and the compiler trusts that
promise. Do not mislead the compiler by breaking your promise.

A definition is a reservation of storage (for an object) or a precise
description of behaviour (for a function).

The confusion (which you share with many people) may stem from the fact
that a definition - a bringing into existence - satisfies the compiler's
occasional requirement for a declaration of existence; that is, "here it
is" is considered as good as "I promise that it's somewhere".

All definitions are declarations. Not all declarations are definitions.


#include <stdio.h>

int z; /* definition: storage is reserved */
extern int y; /* declaration: just a promise of existence */
extern int x = 6; /* definition: by giving it a value, you force the
compiler to take it as a definition rather than a mere declaration */

int foo(void); /* declaration - no function body */

int main(void)
{ /* definition - function body coming right up */
printf("%d * d\n", z + y + x * foo());
return 0;
} /* end of definition */

The compiler has no alternative but to trust you for the definitions of
foo() and y. (The linker will swear at you if you break your promise.)


Here is another translation unit:

int y = 42; /* definition */

int foo(void) /* definition - function body coming right up */
{
int j; /* definition */
j = 117;
return j;
}

If this is compiled and linked with the other object file, all will be well.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
"Usenet is a strange place" - dmr 29 July 1999
Sig line vacant - apply within

Rich Webb

unread,
Jan 31, 2010, 4:16:43 AM1/31/10
to
On Sat, 30 Jan 2010 23:53:39 -0800 (PST), frank <abqha...@gmail.com>
wrote:

A memory aid is to note that (not counting the "tion" suffix), only
"definition" has an 'i'. That's the Roman numeral for "one" and there
can be only one definition, while there may be multiple declarations.

--
Rich Webb Norfolk, VA

frank

unread,
Feb 1, 2010, 7:20:57 PM2/1/10
to
Richard Heathfield wrote:

> If this is compiled and linked with the other object file, all will be
> well.
>

dan@dan-desktop:~/source/unleashed/ch11$ cat r1.c


#include <stdio.h>

int z; /* definition: storage is reserved */
extern int y; /* declaration: just a promise of existence */
extern int x = 6; /* definition: by giving it a value, you force the
compiler to take it as a definition rather than a mere declaration */

int foo(void); /* declaration - no function body */

int main(void)
{ /* definition - function body coming right up */

printf("%d * %d\n", z + y + x * foo());


return 0;
} /* end of definition */

//gcc -D_GNU_SOURCE -Wall -Wextra r1.c -o out
dan@dan-desktop:~/source/unleashed/ch11$ gcc tu1.o -D_GNU_SOURCE -Wall
-Wextra r1.c -o out
r1.c:9: warning: �x� initialized and declared �extern�
dan@dan-desktop:~/source/unleashed/ch11$ ls
C11_001.c C11_014.c clistmn.c kenny1.c sllist.h string.o
C11_002.c c11_015.c deque.c kenny1.c~ sllistmn.c t1.c
C11_003.c C11_016.c deque.h moi1.c stack.c t1.c~
C11_004.c c11_017.c dequemn.c moi1.c~ stack.h t2.c
C11_005.c c11_018.c dllist.c out stackmn.c t2.c~
C11_006.c c11_019.c dllisteg.c queue.c strarr2.c text1.txt
C11_007.c c11_020.c dllist.h queue.h strarr2.c~ tu1.c
C11_008.c c11_021.c dllistmn.c queuemn.c strarr2.h tu1.o
C11_009.c c11_022.c foo.o r1.c strarr2.h~
C11_010.c cityloc.txt heap.c r1.c~ strarr.c
C11_011.c clist.c heap.h rd3.c strarr.c~
C11_012.c clist.h heapmn.c readme.txt strarr.h
C11_013.c clistmn2.c k sllist.c string2.o
dan@dan-desktop:~/source/unleashed/ch11$ ./out
48 * 117
dan@dan-desktop:~/source/unleashed/ch11$ cat tu1.c


int y = 42; /* definition */

int foo(void) /* definition - function body coming right up */
{
int j; /* definition */
j = 117;
return j;
}

// gcc -c tu1.c
dan@dan-desktop:~/source/unleashed/ch11$

I think this is what you are getting at. I had to change your printf a
bit. There's a lot to think about here.

Is z guaranteed to be zero?

Oh, r1.c is now:

dan@dan-desktop:~/source/unleashed/ch11$ cat r1.c


#include <stdio.h>

int z; /* definition: storage is reserved */
extern int y; /* declaration: just a promise of existence */
extern int x = 6; /* definition: by giving it a value, you force the
compiler to take it as a definition rather than a mere declaration */

int foo(void); /* declaration - no function body */

int main(void)
{ /* definition - function body coming right up */

printf("%d * %d\n", z + y + x , foo());


return 0;
} /* end of definition */

//gcc tu1.o -D_GNU_SOURCE -Wall -Wextra r1.c -o out
dan@dan-desktop:~/source/unleashed/ch11$

--
frank

frank

unread,
Feb 1, 2010, 7:24:51 PM2/1/10
to

Thx, Rich.

--

Phred Phungus

unread,
Feb 3, 2010, 11:26:05 PM2/3/10
to
frank wrote:

> Is z guaranteed to be zero?

dan@dan-desktop:~/source/unleashed/ch11$ gcc tu1.o -D_GNU_SOURCE -Wall
-Wextra r2.c -o out
r2.c:9: warning: �x� initialized and declared �extern�
dan@dan-desktop:~/source/unleashed/ch11$ ./out
z y and x are 0 42 6
foo is 117
dan@dan-desktop:~/source/unleashed/ch11$ cat r2.c


#include <stdio.h>

int z; /* definition: storage is reserved */
extern int y; /* declaration: just a promise of existence */
extern int x = 6; /* definition: by giving it a value, you force the
compiler to take it as a definition rather than a mere declaration */

int foo(void); /* declaration - no function body */

int main(void)
{ /* definition - function body coming right up */

printf("z y and x are %d %d %d\n", z , y, x );
printf(" foo is %d\n", foo());


return 0;
} /* end of definition */

// gcc tu1.o -D_GNU_SOURCE -Wall -Wextra r2.c -o out
dan@dan-desktop:~/source/unleashed/ch11$ cat tu1.c


int y = 42; /* definition */

int foo(void) /* definition - function body coming right up */
{
int j; /* definition */
j = 117;
return j;
}

// gcc -c tu1.c
dan@dan-desktop:~/source/unleashed/ch11$


That's a nice-looking program, Richard, thanks.
--

Ben Bacarisse

unread,
Feb 4, 2010, 8:44:40 AM2/4/10
to
[re-ordered for convenience]
Phred Phungus <Ph...@example.invalid> writes:
> frank wrote:
<snip>

> #include <stdio.h>
>
> int z; /* definition: storage is reserved */
> extern int y; /* declaration: just a promise of existence */
> extern int x = 6; /* definition: by giving it a value, you force the
> compiler to take it as a definition rather than a mere declaration */
<snip>

>> Is z guaranteed to be zero?

In this case, yes. You might think that z is zero because of
6.7.8 p10 that gives the initial value of objects that are not
explicitly initialised, but that is technically not so.

int z; (at file scope) is not an external definition. It is a
tentative definition and, if there is no external definition for that
identifier in the translation unit, the behaviour is as if an explicit
initialiser of 0 had been written. I.e. the initial value is given by
section 6.9.2 p2 with some more technical details about the composite
type.

The upshot is that you don't know the value until the end of the
translation unit since there may be a subsequent int z = 42; which is
not an error as it would be if both were in the same block scope.

<snip>
--
Ben.

Phred Phungus

unread,
Feb 5, 2010, 1:42:35 AM2/5/10
to
Ben Bacarisse wrote:
> [re-ordered for convenience]
> Phred Phungus <Ph...@example.invalid> writes:
>> frank wrote:
> <snip>
>> #include <stdio.h>
>>
>> int z; /* definition: storage is reserved */
>> extern int y; /* declaration: just a promise of existence */
>> extern int x = 6; /* definition: by giving it a value, you force the
>> compiler to take it as a definition rather than a mere declaration */
> <snip>
>
>>> Is z guaranteed to be zero?
>
> In this case, yes. You might think that z is zero because of
> 6.7.8 p10 that gives the initial value of objects that are not
> explicitly initialised, but that is technically not so.


If an object that has automatic storage duration is not initialized
explicitly, its value is
indeterminate. If an object that has static storage duration is not
initialized explicitly,
then:
� if it has pointer type, it is initialized to a null pointer;
� if it has arithmetic type, it is initialized to (positive or unsigned)
zero;
� if it is an aggregate, every member is initialized (recursively)
according to these rules;
� if it is a union, the first named member is initialized (recursively)
according to these
rules.

>
> int z; (at file scope) is not an external definition. It is a
> tentative definition and, if there is no external definition for that
> identifier in the translation unit, the behaviour is as if an explicit
> initialiser of 0 had been written. I.e. the initial value is given by
> section 6.9.2 p2 with some more technical details about the composite
> type.
>
> The upshot is that you don't know the value until the end of the
> translation unit since there may be a subsequent int z = 42; which is
> not an error as it would be if both were in the same block scope.

I'm not quite following you yet, Ben. Do I understand correctly that
while the value of z is indeterminate at onset of execution, by the time
it gets to my printf it must, as an arithmetic type, be zero?
--
Phred

Ben Bacarisse

unread,
Feb 5, 2010, 7:22:36 AM2/5/10
to
Phred Phungus <Ph...@example.invalid> writes:

> Ben Bacarisse wrote:
>> [re-ordered for convenience] Phred Phungus <Ph...@example.invalid>
>> writes:
>>> frank wrote:
>> <snip>
>>> #include <stdio.h>
>>>
>>> int z; /* definition: storage is reserved */
>>> extern int y; /* declaration: just a promise of existence */
>>> extern int x = 6; /* definition: by giving it a value, you force the
>>> compiler to take it as a definition rather than a mere declaration */
>> <snip>
>>
>>>> Is z guaranteed to be zero?
>>
>> In this case, yes. You might think that z is zero because of
>> 6.7.8 p10 that gives the initial value of objects that are not
>> explicitly initialised, but that is technically not so.
>
>
> If an object that has automatic storage duration is not initialized
> explicitly, its value is
> indeterminate. If an object that has static storage duration is not
> initialized explicitly,
> then:

> — if it has pointer type, it is initialized to a null pointer;
> — if it has arithmetic type, it is initialized to (positive or
> unsigned) zero;
> — if it is an aggregate, every member is initialized (recursively)
> according to these rules;
> — if it is a union, the first named member is initialized


> (recursively) according to these
> rules.

Yes, that's 6.7.8 p10.

>> int z; (at file scope) is not an external definition. It is a
>> tentative definition and, if there is no external definition for that
>> identifier in the translation unit, the behaviour is as if an explicit
>> initialiser of 0 had been written. I.e. the initial value is given by
>> section 6.9.2 p2 with some more technical details about the composite
>> type.
>>
>> The upshot is that you don't know the value until the end of the
>> translation unit since there may be a subsequent int z = 42; which is
>> not an error as it would be if both were in the same block scope.
>
> I'm not quite following you yet, Ben. Do I understand correctly that
> while the value of z is indeterminate at onset of execution, by the
> time it gets to my printf it must, as an arithmetic type, be zero?

Yes, though I don't like talking about time. Its initial value is
zero (or null) for whatever reason.

If you are reading a C program and you see this:

#include <stdio.h>
int z;

int main(void) { printf("%d\n", z); return 0; }

Can you conclude, from what you have seen so far, what will be
printed? No, you can't, because int z; is /not/ an external
definition of z. It is simply a /tentative definition/ and, after
main, we might find:

int z = 42;

which /is/ an external definition of z.

My other point was that if there is no such external definition
(i.e. there are only one or more tentative definitions) then the
initial value for z is not given by 6.7.8 p10; it is given by 6.9.2
p2. That value is zero, so you can't tell the difference (at least I
don't think you can tell) but, technically, the normal rules do not
apply here.

This second point is a small technical one of very little value, but
the first is more significant. One day, years from now, you might
stumble across it and get a warm glow from not being baffled by it.

--
Ben.

Phred Phungus

unread,
Feb 5, 2010, 7:58:20 AM2/5/10
to

dan@dan-desktop:~/source/unleashed/ch11$ gcc -c tu2.c
dan@dan-desktop:~/source/unleashed/ch11$ ls
ac1.c C11_014.c deque.h ll1.o sllist.c string.o
ac1.c~ c11_015.c dequemn.c moi1.c sllist.c~ t1.c
C11_001.c C11_016.c dllist.c moi1.c~ sllist.h t1.c~
C11_002.c c11_017.c dllisteg.c moi2.c sllistmn.c t2.c
C11_003.c c11_018.c dllist.h moi2.c~ stack.c t2.c~
C11_004.c c11_019.c dllistmn.c out stack.h t3.c
C11_005.c c11_020.c foo.o queue.c stackmn.c text1.txt
C11_006.c c11_021.c heap.c queue.h strarr2.c tu1.c
C11_007.c c11_022.c heap.h queuemn.c strarr2.c~ tu1.o
C11_008.c cityloc.txt heapmn.c r1.c strarr2.h tu2.c
C11_009.c clist.c k r1.c~ strarr2.h~ tu2.o
C11_010.c clist.h kenny1.c r2.c strarr.c
C11_011.c clistmn2.c kenny1.c~ r2.c~ strarr.c~
C11_012.c clistmn.c ll1.c rd3.c strarr.h
C11_013.c deque.c ll1.c~ readme.txt string2.o
dan@dan-desktop:~/source/unleashed/ch11$ gcc tu1.o tu2.o -D_GNU_SOURCE

-Wall -Wextra r2.c -o out

r2.c:9: warning: ‘x’ initialized and declared ‘extern’
dan@dan-desktop:~/source/unleashed/ch11$ ./out
z y and x are 42 42 6
foo is 117
dan@dan-desktop:~/source/unleashed/ch11$ cat tu2.c


int z = 42;

// gcc -c tu2.c
dan@dan-desktop:~/source/unleashed/ch11$

and there it is ....

>
> My other point was that if there is no such external definition
> (i.e. there are only one or more tentative definitions) then the
> initial value for z is not given by 6.7.8 p10; it is given by 6.9.2
> p2. That value is zero, so you can't tell the difference (at least I
> don't think you can tell) but, technically, the normal rules do not
> apply here.

ok


>
> This second point is a small technical one of very little value, but
> the first is more significant. One day, years from now, you might
> stumble across it and get a warm glow from not being baffled by it.
>

I enjoy this reward of continuing education.

Ben Bacarisse

unread,
Feb 5, 2010, 9:03:53 AM2/5/10
to
Phred Phungus <Ph...@example.invalid> writes:
<snip>

> dan@dan-desktop:~/source/unleashed/ch11$ gcc -c tu2.c
<snip>

> dan@dan-desktop:~/source/unleashed/ch11$ gcc tu1.o tu2.o -D_GNU_SOURCE
> -Wall -Wextra r2.c -o out
> r2.c:9: warning: ‘x’ initialized and declared ‘extern’
> dan@dan-desktop:~/source/unleashed/ch11$ ./out
> z y and x are 42 42 6
> foo is 117
> dan@dan-desktop:~/source/unleashed/ch11$ cat tu2.c
>
>
> int z = 42;
>
> // gcc -c tu2.c
> dan@dan-desktop:~/source/unleashed/ch11$
>
> and there it is ....

You don't show the other two translation units but if they are the
same as they were when you last posted them, then what you have now is
a program with undefined behaviour so, again, I am not sure what your
point is. If it is that gcc is not honouring the spirit of 6.9.2 p2
then I agree.

However, since the behaviour is undefined, gcc is permitted to do
anything it likes and can merge the two definitions of z which is what
it does by default. If you add -fno-common to your compile line you
get told of the problem.

<snip>
--
Ben.

0 new messages