Please help optimize (and standarize) this code...

G

gtippery

Newbie-ish questions - I've been away from C for a _long_ time.

It seems to me that there ought to be easier (or at least shorter) ways
to do what this does. It does compile & run for me (with PowerC, a
16-bit DOS compiler); if there are nonstandard or "accidentally-works"
aspects, please let me know.

{This is the sort of situation where if I knew *what* to Google for, I
wouldn't *need* to... <grin>}

Code follows:

---------------------- snip ------------------------
#include <stdio.h>
#include <stdlib.h> /* for itoa */
#include <string.h> /* for memcpy(), strcpy(), strcat() */

#define NAMLEN 8
#define EXTLEN 3
#define MAX 512
#define MAXCOL 1

typedef struct {
char name[NAMLEN];
char ext[EXTLEN];
} NameExt;

NameExt NameArray[MAX+1];
int NameCnt=0;

main()
{
//...
char fmtstr[32], digits[12];
int i;
char NAM[NAMLEN+1], EXT[EXTLEN+1];
int colnum = 0;

//...
/* Fill the array with (test) names & extensions,
left-justified & space-filled (no (char)0's).
In the real app (...'d code), there's findfirst(),
findnext(), sorting, etc.*/

memcpy(&NameArray[0], "One 1 ", 11);
memcpy(&NameArray[1], "TwoTwoTw222", 11);
memcpy(&NameArray[2], "333 003", 11);
NameCnt = 3;
//...

/* Print names & extensions in multiple columns */

// Construct format string like "%8s%c%3s"
strcpy(fmtstr, "%");
strcat(fmtstr, itoa(NAMLEN, digits, 10));
strcat(fmtstr, "s%c%");
strcat(fmtstr, itoa(EXTLEN, digits, 10));
strcat(fmtstr, "s");

// Print according to the specified format
for(i=0; i<NameCnt; i++) {
memcpy(NAM, NameArray.name, NAMLEN);
NAM[NAMLEN] = (char)0;
memcpy(EXT, NameArray.ext, EXTLEN);
EXT[EXTLEN] = (char)0;
printf(fmtstr, NAM, '.', EXT);

// Handle columnizing here
if(colnum++ < MAXCOL) {
printf(" ");
} else {
printf("\n");
colnum = 0;
}
}
printf("\n");
//...
}
---------------------- snip ------------------------

Particularly, are there easier ways to:
(1) initialize the NameArray records;
(2) specify the format string, assuming I want to stick with constants
for the field widths; and
(3) printf the NameArray array-of-char fields without explicitly
converting them to strings?

Also, is there a way to get e.g.,
sizeof NameExt.Ext
(which doesn't work) at compile-time?

TIA!
 
J

Jens.Toerring

gtippery said:
Newbie-ish questions - I've been away from C for a _long_ time.
It seems to me that there ought to be easier (or at least shorter) ways
to do what this does. It does compile & run for me (with PowerC, a
16-bit DOS compiler); if there are nonstandard or "accidentally-works"
aspects, please let me know.
{This is the sort of situation where if I knew *what* to Google for, I
wouldn't *need* to... <grin>}
Code follows:
---------------------- snip ------------------------
#include <stdio.h>
#include <stdlib.h> /* for itoa */

Sorry, but itoa() is an extension that you won't always find in
#include <string.h> /* for memcpy(), strcpy(), strcat() */
#define NAMLEN 8
#define EXTLEN 3
#define MAX 512
#define MAXCOL 1
typedef struct {
char name[NAMLEN];
char ext[EXTLEN];
} NameExt;
NameExt NameArray[MAX+1];
int NameCnt=0;

If you want to be sure that this also works with a C99 compiler you
must at least tell the compiler the return type of main() (which
must be int), since the old default assumption, that a function
without a declared return type will return int does not hold anymore.
Also telling the compiler the number of arguments is reasonable, so
you better make that either

int main( void )
or
int main( int argc, char *argv[ ] )
{
//...
char fmtstr[32], digits[12];
int i;
char NAM[NAMLEN+1], EXT[EXTLEN+1];
int colnum = 0;
//...
/* Fill the array with (test) names & extensions,
left-justified & space-filled (no (char)0's).
In the real app (...'d code), there's findfirst(),
findnext(), sorting, etc.*/
memcpy(&NameArray[0], "One 1 ", 11);

This obviously assumes that the members of the NameExt structure are
packed, i.e. that the 'ext' member starts immediately after the 'name'
member without any padding in between. While you probably often will
get away with that it's not guaranteed to work since the compiler is
free to insert as many padding bytes between (and after) the members
as it likes. And if it does your scheme won't work anymore. So you
better make that

memcpy( &NameArray[0].name, "One ", NAMLEN );
memcpy( &NameArray[0].ext, "1 ", EXTLEN );
memcpy(&NameArray[1], "TwoTwoTw222", 11);
memcpy(&NameArray[2], "333 003", 11);
NameCnt = 3;
//...
/* Print names & extensions in multiple columns */
// Construct format string like "%8s%c%3s"
strcpy(fmtstr, "%");
strcat(fmtstr, itoa(NAMLEN, digits, 10));

itoa() isn't a standard C function, so this will fail on systems
where there's no ito().
strcat(fmtstr, "s%c%");
strcat(fmtstr, itoa(EXTLEN, digits, 10));
strcat(fmtstr, "s");

Why don't you use sprintf() to make up the format string?

sprintf( fmtstr, "%%%ds%%c%%%ds" NAMLEN, EXTLEN );
// Print according to the specified format
for(i=0; i<NameCnt; i++) {
memcpy(NAM, NameArray.name, NAMLEN);
NAM[NAMLEN] = (char)0;


"(char)0" is probably better written as "'\0'", at least then all
C programmers with a bit of experience will know what you mean.
memcpy(EXT, NameArray.ext, EXTLEN);
EXT[EXTLEN] = (char)0;
printf(fmtstr, NAM, '.', EXT);

// Handle columnizing here
if(colnum++ < MAXCOL) {
printf(" ");
} else {
printf("\n");
colnum = 0;
}
}
printf("\n");
//...
}
---------------------- snip ------------------------
Particularly, are there easier ways to:
(1) initialize the NameArray records;

Perhaps, but that will depend on what you want to initialize the array
elements with in the real case. Since I don't know what findfirst() and
findnext() return (they aren't standard C functions) it's impossible to
say if there's a simpler way to assign values to the structure members.
(2) specify the format string, assuming I want to stick with constants
for the field widths; and
(3) printf the NameArray array-of-char fields without explicitly
converting them to strings?

You actually don't have to do that since you specify the length of them,
so even without the terminating '\0' characters it will work correctly.
You can safely do

printf( fmtstr, NameArry[ 0 ].name, '.', NameArray[ 0 ].ext );

when 'fmtstr' is e.g. "%8s%c%3s".

BTW, an alternative would be to use

printf( "%*s%c%*s", NAMLEN, NameArray[ 0 ].name, '.',
EXTLEN, NameArray[ 0 ].ext );
or
printf( "%*s%c%*s", sizeof NameArray->name, NameArray[ 0 ].name, '.',
sizeof NameArray->ext, NameArray[ 0 ].ext );
Also, is there a way to get e.g.,
sizeof NameExt.Ext
(which doesn't work) at compile-time?

Yes, if you give sizeof the name of a real instance of the NameExt
struct, i.e.

sizeof NameArray[ 0 ].ext
or
sizeof NameArray->ext
Regards, Jens
 
G

gtippery

Thanks for replying.

Sorry, but itoa() is an extension that you won't always find in
<stdlib.h>.

Yeah, I had to dig a bit to even find it, in my compiler's library -
I guess it was "depreciated" even in 1993...

....
you better make that either

int main( void )
or
int main( int argc, char *argv[ ] )

Yup. I did that deliberately to provoke someone to answer.
<grin> That's gotta be either #1 or #2 on the 'most frequent"
list...

/* Fill the array with (test) names & extensions,
left-justified & space-filled (no (char)0's).
In the real app (...'d code), there's findfirst(),
findnext(), sorting, etc.*/
memcpy(&NameArray[0], "One 1 ", 11);


This obviously assumes that the members of the NameExt structure are
packed, i.e. that the 'ext' member starts immediately after the 'name'
member without any padding in between. While you probably often will
get away with that it's not guaranteed to work since the compiler is
free to insert as many padding bytes between (and after) the members
as it likes. And if it does your scheme won't work anymore. So you
better make that

memcpy( &NameArray[0].name, "One ", NAMLEN );
memcpy( &NameArray[0].ext, "1 ", EXTLEN );

A good point, and exactly the sort of thing I wanted to know.

itoa() isn't a standard C function, so this will fail on systems
where there's no itoa().

Do you suggest sprintf(), or is there a standard, specific
integer-to-characters function? BTW, do you know if itoa()
[or something equivalent] is not standardized because character
sets aren't, or is there some other reason?

Why don't you use sprintf() to make up the format string?

sprintf( fmtstr, "%%%ds%%c%%%ds" NAMLEN, EXTLEN );

Argh! Because it didn't occur to me. I _did_ consider using
sprintf() to format the entire output line, but not just the
format string.

// Print according to the specified format
for(i=0; i<NameCnt; i++) {
memcpy(NAM, NameArray.name, NAMLEN);
NAM[NAMLEN] = (char)0;



"(char)0" is probably better written as "'\0'", at least then all
C programmers with a bit of experience will know what you mean.


I _knew_ there was a simpler way than (char)0, but while my
compiler accepts charvar = 0x00 and charvar = \0, neither looked
"explicit" enought. I guess I'd forgotten you could use escaped
characters in '' constants like you can in "" constants.


[Your corrected version requoted here for reference:]
memcpy( &NameArray[0].name, "One ", NAMLEN );
memcpy( &NameArray[0].ext, "1 ", EXTLEN );


Perhaps, but that will depend on what you want to initialize the array
elements with in the real case.

Well, for illustration purposes, is there a better way to do
what it _does_ do (setting an array-of-char field of a
structure to a text constant) than using memcpy()?

Since I don't know what findfirst() and
findnext() return (they aren't standard C functions)

They're MS-DOS functions; in my library they return a pointer to
a structure full of binary values for file size, dates, etc.

What's the standard-C way to get the contents of a directory? I
have a few C reference books, but they date from the 1980's.
(And I wasn't around them when I originally posted.)
(2) specify the format string, assuming I want to stick with constants
for the field widths; and
(3) printf the NameArray array-of-char fields without explicitly
converting them to strings?


You actually don't have to do that since you specify the length of them,
so even without the terminating '\0' characters it will work correctly.
You can safely do

printf( fmtstr, NameArry[ 0 ].name, '.', NameArray[ 0 ].ext );

when 'fmtstr' is e.g. "%8s%c%3s".

I attempted something like that - I originally wrote "%8c%c%3c",
but then realized that wouldn't do what I wanted, and

printf("%c%c%c%c%c%c%c%c%c%c%c%c",
NameArray.name[0],
NameArray.name[1],
...
NameArray.ext[2]);
was just "over the top". <grin> C doesn't have anything like
FORTRAN's "implicit DO" does it?

I wasn't sure "%8s" was safe, 'though I can't remember now why I
thought it might not be.

BTW, an alternative would be to use

printf( "%*s%c%*s", NAMLEN, NameArray[ 0 ].name, '.',
EXTLEN, NameArray[ 0 ].ext );
or
printf( "%*s%c%*s", sizeof NameArray->name, NameArray[ 0 ].name, '.',
sizeof NameArray->ext, NameArray[ 0 ].ext );


Ah, "%*", _that's_ what I couldn't think of.

Also, is there a way to get e.g.,
sizeof NameExt.Ext
(which doesn't work) at compile-time?


Yes, if you give sizeof the name of a real instance of the NameExt
struct, i.e.

sizeof NameArray[ 0 ].ext
or
sizeof NameArray->ext

OK, I need to have that explained a bit more. Why does
sizeof(int) work? It's not a "real instance". Is that the
sizeof paren/no paren thing?

Why does NameArray->ext work, without a specific array index?

And I'm definitely confused by NameArray.ext, *NameArray.ext [or
is that *(NameArray).ext?], and NameArray->ext. Could you kindly
explain the differences? I know an array reference is (almost?)
a pointer, but I don't know for structs.

Thanks.
 
J

Jens.Toerring

Yeah, I had to dig a bit to even find it, in my compiler's library -
I guess it was "depreciated" even in 1993...

It wasn't deprecated because it never was part of the set of functions
the standard requires.
...
you better make that either

int main( void )
or
int main( int argc, char *argv[ ] )
Yup. I did that deliberately to provoke someone to answer.
<grin> That's gotta be either #1 or #2 on the 'most frequent"
list...

You don't seem to have too good an opinion about clc;-)
Do you suggest sprintf(), or is there a standard, specific
integer-to-characters function? BTW, do you know if itoa()
[or something equivalent] is not standardized because character
sets aren't, or is there some other reason?

I don't know what itoa() would be good for since it only reproduces
a functionality you already have with sprintf(). I guess someone
invented it for symmetry reasons because there's atoi(), But atoi()
is a rather useless function anyway and should be avoided in favour
of strtol().
[Your corrected version requoted here for reference:]
memcpy( &NameArray[0].name, "One ", NAMLEN );
memcpy( &NameArray[0].ext, "1 ", EXTLEN );
Perhaps, but that will depend on what you want to initialize the
array elements with in the real case.
Well, for illustration purposes, is there a better way to do
what it _does_ do (setting an array-of-char field of a
structure to a text constant) than using memcpy()?

I don't see any better way here.
They're MS-DOS functions; in my library they return a pointer to
a structure full of binary values for file size, dates, etc.
What's the standard-C way to get the contents of a directory? I
have a few C reference books, but they date from the 1980's.
(And I wasn't around them when I originally posted.)

There aren't any standard C functions, you must use the extensions
for your OS - the word "directory" doesn't even appear anywhere in
the C standard.
Also, is there a way to get e.g.,
sizeof NameExt.Ext
(which doesn't work) at compile-time?


Yes, if you give sizeof the name of a real instance of the NameExt
struct, i.e.

sizeof NameArray[ 0 ].ext
or
sizeof NameArray->ext
OK, I need to have that explained a bit more. Why does
sizeof(int) work? It's not a "real instance". Is that the
sizeof paren/no paren thing?

Yes, it is. Without the parentheses you can only use names of
variables. If you want to use types they must be enclosed in
parenthesis.
Why does NameArray->ext work, without a specific array index?

Because NameArray is automatically taken here to be a pointer to
the first element of the NameArray array (like when you pass an
array to a function).
And I'm definitely confused by NameArray.ext, *NameArray.ext [or
is that *(NameArray).ext?], and NameArray->ext.

When you have an array of structures like

struct xyz {
int x;
char y;
}

struct xyz a[ 10 ];

then 'a' "decays" in situations where it's used as a value into a
pointer to the first element of the array. Thus 'a->y' is the same
as 'a[0].y' (as is '(*a).y').
I know an array reference is (almost?) a pointer, but I don't know
for structs.

It doesn't matter if you have an array of ints or structures or unions
or function pointers or whatever, it's always the same. When the name
of an array is used in a situation where it's used as if had a value
(like in 'a->y') then it automatically is converted to a pointer to
the first element of the array, no matter what kind of elements the
array has. See also what Chris Torek calls "The Rule":

http://web.torek.net/torek/c/index.html

Regards, Jens
 
G

gtippery

It wasn't deprecated because it never was part of the set of functions
the standard requires.

I meant "depreciated" by the distributor of the library; hence the
quotes.

I don't know what itoa() would be good for since it only reproduces
a functionality you already have with sprintf(). I guess someone
invented it for symmetry reasons because there's atoi(), But atoi()
is a rather useless function anyway and should be avoided in favour
of strtol().

Most of the C library "reproduces a functionality you already have",
but hopefully in a more convenient form. itoa()'s more convenient in
an
expression.


(Requoting for context:)

#define NAMLEN 8
#define EXTLEN 3
#define MAX 512

typedef struct {
char name[NAMLEN];
char ext[EXTLEN];
} NameExt;

NameExt NameArray[MAX+1];
int NameCnt=0;
Yes, if you give sizeof the name of a real instance of the NameExt
struct, i.e.
sizeof NameArray[ 0 ].ext
or
sizeof NameArray->ext

OK, I need to have that explained a bit more. Why does
sizeof(int) work? It's not a "real instance". Is that the
sizeof paren/no paren thing?


Yes, it is. Without the parentheses you can only use names of
variables. If you want to use types they must be enclosed in
parenthesis.


But 'sizeof(NameExt.Ext)' doesn't work either. Is NameExt.Ext a
type, or what is it? It's certainly not a variable.
And I'm definitely confused by NameArray.ext, *NameArray.ext [or
is that *(NameArray).ext?], and NameArray->ext.


When you have an array of structures like

struct xyz {
int x;
char y;
}

struct xyz a[ 10 ];

then 'a' "decays" in situations where it's used as a value into a
pointer to the first element of the array. Thus 'a->y' is the same
as 'a[0].y' (as is '(*a).y').

What (if anything) would 'a.y' be?

It doesn't matter if you have an array of ints or structures or unions
or function pointers or whatever, it's always the same. When the name
of an array is used in a situation where it's used as if had a value
(like in 'a->y') then it automatically is converted to a pointer to
the first element of the array, no matter what kind of elements the
array has. See also what Chris Torek calls "The Rule":

http://web.torek.net/torek/c/index.html

I didn't mean "for an array of structs", I meant "for structs", i.e.,
in your example, is a reference to xyz (ever?) a pointer? From reading
Torek's essay you pointed to, it appears it is not.

Speaking of that essay, I'm still trying to digest it. Part of my
problem is difficulty understanding C declarations, but I'm working
on it.
 
J

Jens.Toerring

(Requoting for context:)
#define NAMLEN 8
#define EXTLEN 3
#define MAX 512
typedef struct {
char name[NAMLEN];
char ext[EXTLEN];
} NameExt;
NameExt NameArray[MAX+1];
int NameCnt=0;
Yes, if you give sizeof the name of a real instance of the NameExt
struct, i.e.
sizeof NameArray[ 0 ].ext
or
sizeof NameArray->ext
OK, I need to have that explained a bit more. Why does
sizeof(int) work? It's not a "real instance". Is that the
sizeof paren/no paren thing?

Yes, it is. Without the parentheses you can only use names of
variables. If you want to use types they must be enclosed in
parenthesis.

But 'sizeof(NameExt.Ext)' doesn't work either. Is NameExt.Ext a
type, or what is it? It's certainly not a variable.

There are two things here. First of all there's nothing called
"NameExt.Ext" at all, I guess you mean "NameExt.ext" but since
you use the wrong spelling consistendly I am wondering. Second,
and more important, "NameExt.ext" is neither a variable nor a
type. The names of the elements of a structure aren't types but
they are also not variables. So neither 'sizeof NameExt.ext' nor
'sizeof(NameExt.ext)' can work. Therefore you're here forced to
use a real instance of the structure to get at the size of its
elements - at least I don't see any other way at the moment.
When you have an array of structures like

struct xyz {
int x;
char y;
}

struct xyz a[ 10 ];

then 'a' "decays" in situations where it's used as a value into a
pointer to the first element of the array. Thus 'a->y' is the same
as 'a[0].y' (as is '(*a).y').
What (if anything) would 'a.y' be?

That wouldn't make sense since 'a' isn't a structure (or union)
but, when used in value context like here, a pointer to the first
element of the array. And then the dot can't be used since a dot
is only allowed after a structure (or union) variable name.
I didn't mean "for an array of structs", I meant "for structs", i.e.,
in your example, is a reference to xyz (ever?) a pointer? From reading
Torek's essay you pointed to, it appears it is not.

Yes, in contrast to arrays, structures are "first class citizens" in C,
so if you have the name of a structure variable without any adornments
it means the whole structure and it's not converted to a pointer to
the structure. So for something like

struct xyz b;
struct xyz a[10];
some_func( b, a );

'b' gets passed by value, i.e. a copy of the structure is passed to
the function as with the basic types like ints etc., while for the
array 'a' only a pointer to the array is what the function receives.
And you can copy structures as a whole like in

struct xyz b = { 42, 'a' };
struct xyz c;
c = b;

which is something you also can't do with arrays.

Regards, Jens
 
C

Chris Torek

[Here is one of those older articles I was saving for a followup,
found via references: lines in newer postings.]

You actually don't have to do that since you specify the length of them,
so even without the terminating '\0' characters it will work correctly.
You can safely do

printf( fmtstr, NameArry[ 0 ].name, '.', NameArray[ 0 ].ext );

when 'fmtstr' is e.g. "%8s%c%3s".

This is almost, but not quite, right: you need "%.8s" to tell
printf() that the corresponding "char *" value points to (the first
element of) an array of size 8. "%8s" means "points to a \0-terminated
string as usual, but print it in a field at least 8 characters
wide":

printf("%s.%s\n", "file", "c");

prints "file.c\n", but:

printf("%8s.%3s\n", "file", "c");

prints " file. c\n". To get the blank-padding on the left, use
the '-' flag:

printf("%-8s.%-3s\n", "file", "c");

prints "file .c \n".

Of course, you can combine these, e.g.:

printf("%-8.8s%c%-3.3s", name, '.', ext);

would work.
BTW, an alternative would be to use

printf( "%*s%c%*s", NAMLEN, NameArray[ 0 ].name, '.',
EXTLEN, NameArray[ 0 ].ext );
or
printf( "%*s%c%*s", sizeof NameArray->name, NameArray[ 0 ].name, '.',
sizeof NameArray->ext, NameArray[ 0 ].ext );

Nit: "sizeof" produces a size_t value, but "%*" (and "%.*") require
an int. Since printf() is variadic, a cast is appropriate here:

printf("%.*s%c%.*s",
(int)sizeof NameArray->name, NameArray[0].name, '.',
(int)sizeof NameArray->ext, NameArray[0].ext);

or:

printf("%-*.*s%c%-*.*s",
(int)sizeof NameArray->name, (int)sizeof NameArray->name,
NameArray[0].name, '.',
(int)sizeof NameArray->ext, (int)sizeof NameArray->ext,
NameArray[0].ext);

for instance. (I prefer the version with the NAMLEN and EXTLEN
"#define"s, myself. Fewer casts make me happier. :) )
 
D

Dave Thompson

<snip other comments I concur with>
and add one minor one: double-slash comments are not standard in C90;
they are in C99 and C++, and a pretty common extension in C90.

Aside from Jens' suggestion of sprintf (fmtstr, "%%%ds", NAMLEN) etc.,
IF your lengths are #define'd as only an unsigned decimal literal --
not anything else like say #define NAMLEN (128/16) -- you can
stringize them into a constant string:
#define XSTR(x) XSTR2(x) /* indirection to expand macro as arg */
#define XSTR2(x) #x
char fmtstr [] = "%" XSTR(NAMLEN) "s"; /* etc. */
(3) printf the NameArray array-of-char fields without explicitly
converting them to strings?

You actually don't have to do that since you specify the length of them,
so even without the terminating '\0' characters it will work correctly.
You can safely do

printf( fmtstr, NameArry[ 0 ].name, '.', NameArray[ 0 ].ext );

when 'fmtstr' is e.g. "%8s%c%3s".
Wrong. %Ns specifies a _minimum_ width but the argument still must be
null-terminated. %.Ns note the dot handles a nonterminated char array.
BTW, an alternative would be to use

printf( "%*s%c%*s", NAMLEN, NameArray[ 0 ].name, '.',
EXTLEN, NameArray[ 0 ].ext );
or
printf( "%*s%c%*s", sizeof NameArray->name, NameArray[ 0 ].name, '.',
sizeof NameArray->ext, NameArray[ 0 ].ext );
Agree the first, but in the second the type of sizeof foo is size_t
not int, and may not work correctly as a vararg. Cast it.

Also I would write both the sizeof operand and the by-value argument
as array.name for clarity -- -> is equvalent to [0]. but when
reading it wastes time to verify it was used correctly in this case.

- David.Thompson1 at worldnet.att.net
 
G

gtippery

I think you guys are scaring me. I'm half-expecting Brian or Dennis to
chime in next with a correction to the correction to the corrections,
and it appears I'm going to have to expand my vocabulary as well as my
C skills.

BTW, thanks for not asking, but yes, I *can* spell "standardize".
<grin> Can I change the header without breaking the thread?
 
G

gtippery

Dave said:
On 21 Feb 2005 12:27:37 GMT, (e-mail address removed)-berlin.de wrote:
....
Aside from Jens' suggestion of sprintf (fmtstr, "%%%ds", NAMLEN) etc.,
IF your lengths are #define'd as only an unsigned decimal literal --
not anything else like say #define NAMLEN (128/16) -- you can
stringize them into a constant string:
#define XSTR(x) XSTR2(x) /* indirection to expand macro as arg */
#define XSTR2(x) #x
char fmtstr [] = "%" XSTR(NAMLEN) "s"; /* etc. */

I see how this is supposed to work, but not how it gets there with the
indirection. Could you show me step by step how that expands, or
possibly direct me to a good explanation of stringizing and pasting in
macros? I've read about the "#" but didn't understand it.

printf( "%*s%c%*s", sizeof NameArray->name, NameArray[ 0 ].name, '.',
sizeof NameArray->ext, NameArray[ 0 ].ext );
Agree the first, but in the second the type of sizeof foo is size_t
not int, and may not work correctly as a vararg. Cast it.

Also I would write both the sizeof operand and the by-value argument
as array.name for clarity -- -> is equvalent to [0]. but when
reading it wastes time to verify it was used correctly in this case.


By "correctly" do you just mean that it refers to the same thing as the
by-value argument, or some other sort of correctness?
 
G

gtippery

With thanks to Jens, Chris, and David, here's the revised version.
Besides recoding to the standard and simplifying, I attempted to
make capitalization more consistent and MAXCOL a bit more obvious.
I think it looks a lot better now. Does anything else need changing?


#include <stdio.h>
#include <string.h> /* for memcpy() */


#define NAMLEN 8
#define EXTLEN 3
#define MAX 512
#define MAXCOL 2


typedef struct {
char name[NAMLEN];
char ext[EXTLEN];
} NameExt;


NameExt nameArray[MAX+1];
int nameCnt=0;


int main(void)
{

int i;
int colnum = 0;


/* Fill the array with (test) names & extensions,
left-justified & space-filled (no '\0' characters). */

memcpy( &nameArray[0].name, "One ", NAMLEN );
memcpy( &nameArray[0].ext, "1 ", EXTLEN );

memcpy( &nameArray[1].name, "TwoTwoTw", NAMLEN );
memcpy( &nameArray[1].ext, "222", EXTLEN );

memcpy( &nameArray[2].name, "333 ", NAMLEN );
memcpy( &nameArray[2].ext, "003", EXTLEN );

nameCnt = 3;


/* Print names & extensions in multiple columns */

for(i=0; i<nameCnt; i++) {
printf("%-*.*s.%-*.*s",
NAMLEN, NAMLEN, nameArray.name,
EXTLEN, EXTLEN, nameArray.ext);

/* Handle columnizing here */
if(++colnum < MAXCOL) {
printf(" ");
} else {
printf("\n");
colnum = 0;
}
}
printf("\n");

}


Two questions:

1) Should I use a cast to (size_t) on the third parameter of the
memcopy()'s? Does memcpy()'s prototype take care of that?

2) Would

"%-*.*s" "." "%-*.*s"

be easier to read than

"%-*.*s.%-*.*s"

as the printf() format string?
 
M

Michael Mair

gtippery said:
Dave said:
On 21 Feb 2005 12:27:37 GMT, (e-mail address removed)-berlin.de wrote:

...

Aside from Jens' suggestion of sprintf (fmtstr, "%%%ds", NAMLEN)
etc.,

IF your lengths are #define'd as only an unsigned decimal literal --
not anything else like say #define NAMLEN (128/16) -- you can
stringize them into a constant string:
#define XSTR(x) XSTR2(x) /* indirection to expand macro as arg */
#define XSTR2(x) #x
char fmtstr [] = "%" XSTR(NAMLEN) "s"; /* etc. */


I see how this is supposed to work, but not how it gets there with the
indirection. Could you show me step by step how that expands, or
possibly direct me to a good explanation of stringizing and pasting in
macros? I've read about the "#" but didn't understand it.

#x puts quotation marks around whatever x is:

XSTR2(NAMLEN) gives you "NAMLEN".
With #define NAMLEN 42,
XSTR(NAMLEN) gives you XSTR2(42), i.e. "42"
fmtstr thus becomes "%" "42" "s", equivalent to "%42s"
printf( "%*s%c%*s", sizeof NameArray->name, NameArray[ 0

].name, '.',
sizeof NameArray->ext, NameArray[ 0 ].ext
);

Agree the first, but in the second the type of sizeof foo is size_t
not int, and may not work correctly as a vararg. Cast it.

Also I would write both the sizeof operand and the by-value argument
as array.name for clarity -- -> is equvalent to [0]. but when
reading it wastes time to verify it was used correctly in this case.


By "correctly" do you just mean that it refers to the same thing as the
by-value argument, or some other sort of correctness?


IMO, you are referring to the second "correctly".
If you see
"*s", (int) sizeof array.name, array.name
you know that the field width and the string fit together whereas
"*s", (int) sizeof array->name, array.name
makes you think for a moment whether this is correct.


Cheers
Michael
 
M

Mark F. Haigh

gtippery said:
Newbie-ish questions - I've been away from C for a _long_ time.

It seems to me that there ought to be easier (or at least shorter) ways
to do what this does.

<large snip>

Well, here's something shorter and more similar to what you'd encounter
in practice. For something like this, not many people are going to
#define the size of the members of struct NameExt, since you can get at
them with sizeof. Also, many times people are going to use a sentinel
value to signal the end of the array rather than maintain a separate
variable to do so.

"(i + 1) % MAXCOL" is marginally idiomatic for producing columned
output in situations where MAXCOL is a power of two or "small enough"
for the compiler to play fancy tricks with.


---snip---
#include <stdio.h>

#define MAXCOL 2

struct NameExt
{
char name[9];
char ext[4];
};


int main(void)
{
/* Note sentinel at end */
struct NameExt list[] = {
{ "BillyBob", "123" },
{ "Steve", "456" },
{ "Tom", "789" },
{ "Joe", "012" },
{ "Sal", "345" },
{ "", "" },
};

/* Print the array out */
size_t i;
for(i = 0; *list.name; i++)
printf("%-*s%-*s%s",
sizeof(list.name), list.name,
sizeof(list.ext), list.ext,
(i + 1) % MAXCOL ? "" : "\n");

return 0;
}
---snip---

[mark@icepick ~]$ gcc -Wall -O2 -ansi -pedantic foo.c -o foo
[mark@icepick ~]$ ./foo
BillyBob 123 Steve 456
Tom 789 Joe 012
Sal 345
[mark@icepick ~]$


Mark F. Haigh
(e-mail address removed)
 
G

gtippery

Mark said:
Well, here's something shorter and more similar to what you'd encounter
in practice. For something like this, not many people are going to
#define the size of the members of struct NameExt, since you can get at
them with sizeof. Also, many times people are going to use a sentinel
value to signal the end of the array rather than maintain a separate
variable to do so.

I don't see any particular advantage either way on either of these
changes:
-- See an earlier posting for another opinion on #define's vs.
casting sizeof's to int's for printf; and there may be other places
that need to use the values.
-- A sentinel's easier if you're using pointers (or doing
comparisons), but what's its advantage if you're indexing? And that's
assuming the count's not needed for anything else, of course.
"(i + 1) % MAXCOL" is marginally idiomatic for producing columned
output in situations where MAXCOL is a power of two or "small enough"
for the compiler to play fancy tricks with.

What kind of "tricks" did you have in mind that the compiler might do
with, say, 5 columns per line? Isn't it either going to have to divide
or to maintain an internal modulo counter (like I'm doing explicitly)?

I used a separate column counter have the column # directly available
for other purposes (not shown) and debugging. Also, there are
additional fields in each column in the working program, so putting the
column logic separately seemed easier. I _will_ see if I can use the
%s / ? combo.

The rest of your code doesn't quite do the same thing as mine - the
"input" data's formatted differently and so's the output.

If you just wanted to show something similar, that's fine, and it was
interesting. But the reason I didn't use an array of strings in the
first place is that in the real program, the data's coming from an
existing packed data structure in memory (returned by the OS), and
would have to be converted to strings to use your sentinel and printing
techniques; so as far as I can see I'd need the memcpy()'s anyway.

Then to get the right output, it needs the changes I show below to
allow for the fact that the structure members now each include '\0'.
Also, the array's larger now (by two bytes per element), but that won't
actually be a problem in my case.
---snip---
#include <stdio.h>

#define MAXCOL 2

struct NameExt
{
char name[9];
char ext[4];
};


int main(void)
{
/* Note sentinel at end */
struct NameExt list[] = {
{ "BillyBob", "123" },
{ "Steve", "456" },
{ "Tom", "789" },
{ "Joe", "012" },
{ "Sal", "345" },
{ "", "" },
};

/* Print the array out */
size_t i;

What's the reason for using size_t instead of just int? I don't see
anywhere you use i as a size.
for(i = 0; *list.name; i++)


*list.name is the same as list.name[0], right? You're checking
if the first char is zero?
printf("%-*s%-*s%s",

printf("%-*s.%-*s%s",
sizeof(list.name), list.name,
sizeof(list.ext), list.ext,


(int)sizeof(list.name) - 1, list.name,
(int)sizeof(list.ext) - 1, list.ext,
(i + 1) % MAXCOL ? "" : "\n");

(i + 1) % MAXCOL ? " " : "\n");
return 0;

I've been meaning to ask about that. Older references seem to imply
that main() defaults to returning 0, and I haven't seen anything newer
that says differently, but I wouldn't be surprised. I downloaded a
copy of a "late draft" of the spec, but it was a .bz and I couldn't
open it. 8(
}
---snip---

[mark@icepick ~]$ gcc -Wall -O2 -ansi -pedantic foo.c -o foo
[mark@icepick ~]$ ./foo
BillyBob 123 Steve 456
Tom 789 Joe 012
Sal 345
[mark@icepick ~]$

Should look like this:

BillyBob.123 Steve .456
Tom .789 Joe .012
Sal .345
 
G

gtippery

Michael Mair wrote:
....
#x puts quotation marks around whatever x is:

XSTR2(NAMLEN) gives you "NAMLEN".
With #define NAMLEN 42,
XSTR(NAMLEN) gives you XSTR2(42), i.e. "42"
fmtstr thus becomes "%" "42" "s", equivalent to "%42s"

OK, thanks. Is the indirection necessary to avoid getting a literal
"#" instead?

....
Agree the first, but in the second the type of sizeof foo is size_t
not int, and may not work correctly as a vararg. Cast it.

Also I would write both the sizeof operand and the by-value argument
as array.name for clarity -- -> is equvalent to [0]. but when
reading it wastes time to verify it was used correctly in this
case.

By "correctly" do you just mean that it refers to the same thing as the
by-value argument, or some other sort of correctness?


IMO, you are referring to the second "correctly".


Yes. Sorry for the ambiguity.
If you see
"*s", (int) sizeof array.name, array.name
you know that the field width and the string fit together whereas
"*s", (int) sizeof array->name, array.name
makes you think for a moment whether this is correct.


Thanks, that's what I thought.
 
M

Mark F. Haigh

gtippery said:
Mark F. Haigh wrote:

If you just wanted to show something similar, that's fine, and it was
interesting. But the reason I didn't use an array of strings in the
first place is that in the real program, the data's coming from an
existing packed data structure in memory (returned by the OS), and
would have to be converted to strings to use your sentinel and printing
techniques; so as far as I can see I'd need the memcpy()'s anyway.

And how was I supposed to know this? State this up front next time.
I'd thought it was for some kind of class project (first name / 3 digit
telephone extension), and that you had poor taste in data structures
and compilers.

I'll take another look when I get some time. Disregard the previous
post, and note:

1. Yes, you should cast size_t to int if it's going to be going
through the default argument promotions. I shouldn't have posted so
hastily right before bedtime (on some days, my only time for c.l.c).
Shame on me.

2. There are all kinds of dirty tricks and pre-canned "divide by x"
code sequences for compiler writers. Look at "Hacker's Delight" by
Henry S. Warren Jr. for more info on some interesting tricks (magic
numbers, etc).

3. What's the biggest number of elements that your array can have?
Check your platform docs. It may be more than int can handle, but less
than or equal to what size_t can handle.

4. Always put a return statement in main. It's necessary in C89, and
good style in C99.


Mark F. Haigh
(e-mail address removed)
 
C

CBFalconer

gtippery said:
.... snip ...

I've been meaning to ask about that. Older references seem to imply
that main() defaults to returning 0, and I haven't seen anything newer
that says differently, but I wouldn't be surprised. I downloaded a
copy of a "late draft" of the spec, but it was a .bz and I couldn't
open it. 8(

Sounds like you got it from me. You just have to feed it to bzip2
to expand it. Execute "bzip2 -dc n869_txt.bz2" and n869.txt will
magically appear. However you got it over a 56k modem in about 2
minutes instead of about 9 minutes, due to the compressions. A zip
version would have taken about 3 minutes.

Google for bzip2, and keep it available. You can find it for most
systems.
 
G

gtippery

CBFalconer said:
Sounds like you got it from me.

Yup. Lots of other interesting stuff you have there, btw. I'm going
to dig throught that Borland/ISO-input thing when I can.
You just have to feed it to bzip2
to expand it. Execute "bzip2 -dc n869_txt.bz2" and n869.txt will
magically appear. However you got it over a 56k modem in about 2
minutes instead of about 9 minutes, due to the compressions. A zip
version would have taken about 3 minutes.

Well, actually I got it in about 5 seconds (cable modem). Guess it
saved me, oh, maybe 1 or 2 seconds vs. zip... <grin>
 
M

Michael Mair

gtippery said:
Michael Mair wrote:
...


OK, thanks. Is the indirection necessary to avoid getting a literal
"#" instead?

[For the record, I quote the definitions of XSTR and XSTR2:
#define XSTR(x) XSTR2(x) /* indirection to expand macro as arg */
#define XSTR2(x) #x
]

Nope. Have a look at the direct call to XSTR2:
XSTR2(42) gives you "42", as expected
but
XSTR2(SOME_MACRO_EXPRESSION) gives you "SOME_MACRO_EXPRESSION"
instead of whatever SOME_MACRO_EXPRESSION expands to. In the
above case, in order to get the 42 "out of" NAMLEN, we need the
indirection. XSTR(NAMLEN) expands NAMLEN (to 42) before
XSTR2(expansion of NAMLEN) is applied. Thus, XSTR(NAMLEN) gives
you "42".


Cheers
Michael
 
G

gtippery

Mark said:
And how was I supposed to know this? State this up front next time.

The "real app" is mentioned in the original post (in a comment).
Anyway, I wasn't objecting, just explaining.
I'd thought it was for some kind of class project (first name / 3 digit
telephone extension), and that you had poor taste in data structures
and compilers.

Well, "de gustibus non est disputandum", and I've no reason to think
mine's particularly good. The compiler choice is partially dictated by
the target system, which runs PC-DOS 6. I've also got Borland's
compiler, but it seemed like overkill. PowerC, as far as I've seen to
date, has all the C89 features except localization (if that's strictly
C89). It may not be "picky" enough -- I need to check the options in
the docs.
....
I'll take another look when I get some time. Disregard the previous
post, and note:

1. Yes, you should cast size_t to int if it's going to be going
through the default argument promotions.

I learned this from an earlier post in this same thread, myself.
2. There are all kinds of dirty tricks and pre-canned "divide by x"
code sequences for compiler writers. Look at "Hacker's Delight" by
Henry S. Warren Jr. for more info on some interesting tricks (magic
numbers, etc).

Noted.

3. What's the biggest number of elements that your array can have?
Check your platform docs. It may be more than int can handle, but less
than or equal to what size_t can handle.

The platform limit is 64KB for any one item (i8086). The array would
be the limiting factor in this case (at something over 5,000 elements),
but it's never going to get that big - the maximum dimension is 512.

If size_t isn't actually an int (of some standard type), isn't that
going to be a problem in the for()? I thought the index variable had
to be an integer or enumeration.
4. Always put a return statement in main. It's necessary in C89, and
good style in C99.
....

Thanks.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,521
Members
44,995
Latest member
PinupduzSap

Latest Threads

Top