format for declaring variables?

D

Daniel Rudy

Hello Group,

Consider the following code:


/* form 1 */
int main(void)
{
int i;
char *p;

/* do something */

return(0);
}



/* form 2 */
int main(void)
int i;
char *p;
{

/* do something */

return(0);
}

It seems alot of unix code uses the second form for initializing
variables. Alot of my code uses the first form. Is there any
operational difference between the two?




--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
R

Richard Bos

Daniel Rudy said:
/* form 1 */
int main(void)
{
int i;
char *p;

/* form 2 */
int main(void)
int i;
char *p;
{
return(0);
}

It seems alot of unix code uses the second form for initializing
variables.

No, you don't, and if you did it isn't C.
Alot of my code uses the first form. Is there any
operational difference between the two?

Yes. The former is C, the latter is not.

What you _may_ have seen is this:

int main()
int i;
char *p;
{

return(0);
}

Note the lack of a void keyword in the function header. This makes this
an old-style declaration, rather than a prototype. They're obsolescent,
and IMO should have been obsoleted in the 1999 C Standard. Very old code
may still be around which was written like this in 18-something; code
which still has to compile on antediluvial, pre-Standard compilers may
still need to be written like this; but no normal code should be written
like it today.

Richard
 
R

Richard Heathfield

[code reflowed for vertical compression!]

Daniel Rudy said:
Consider the following code:

/* form 1 */
int main(void) { int i; char *p; /* do something */ return(0); }

/* form 2 */
int main(void) int i; char *p; { /* do something */ return(0); }

It seems alot of unix code uses the second form for initializing
variables. Alot of my code uses the first form. Is there any
operational difference between the two?

The two forms are not equivalent, and indeed the second form is illegal.
What I think you're trying to represent, which *is* legal, is:

int main(i, p) int i; char **p; { return(0); }

and it is basically an archaeological curiosity. It is equivalent to:

int main(int i, char **p) { return(0); }

except that this more up-to-date ("function prototype") version
facilitates the compiler's type-checking - which is why it was
introduced. The older form is still legal for hysterical reasons.

Ignore the older version if you can. Better still, update code that uses
it to adopt the prototype style.

Incidentally, the parentheses around the 0 are redundant and pointless.
 
J

Jens Thoms Toerring

Daniel Rudy said:
/* form 1 */
int main(void)
{
int i;
char *p;
/* do something */

/* form 2 */
int main(void)
int i;
char *p;
{
/* do something */

It seems alot of unix code uses the second form for initializing
variables. Alot of my code uses the first form. Is there any
operational difference between the two?

It's not about initializing variables but function parameters. And
the second form is the correct form, the first form is from times
before standardization of C by ANSI/ISO. i.e. from before 1989/90.
It's only still supported for backward compatibility. And, actually,
the way you write it isn't correct, if I remember correctly it would
have to be

int main(i,p)
int i;
char *p;

making main() a function that takes an integer argument 'i' and a char
pointer argument 'p' which would be written nowadays

int main(int i, char *p)

(Both are, of course, not standard-compliant since main() takes
either no arguments or an int and a char** or an array of char*.)

Regards, Jens
 
D

Daniel Rudy

At about the time of 2/9/2007 1:25 AM, Daniel Rudy stated the following:
Hello Group,

snip


It seems alot of unix code uses the second form for initializing
variables. Alot of my code uses the first form. Is there any
operational difference between the two?

I was using main as an example to demonstrate the form....and it should
have been declaring and not initializing there too....

Anyways, I know that this IS platform specific, but here's what I saw,
copied verbatim from the source file:

int
kenv(td, uap)
struct thread *td;
struct kenv_args /* {
int what;
const char *name;
char *value;
int len;
} */ *uap;
{
char *name, *value;
size_t len, done, needed;
int error, i;

KASSERT(dynamic_kenv, ("kenv: dynamic_kenv = 0"));

error = 0;
if (uap->what == KENV_DUMP) {
#ifdef MAC
error = mac_check_kenv_dump(td->td_ucred);
if (error)
return (error);
#endif
done = needed = 0;
sx_slock(&kenv_lock);
for (i = 0; kenvp != NULL; i++) {


I cut it short because it's pretty long. But if you want to see for
yourself, FreeBSD 6.2-RELEASE /usr/src/sys/kern/kern_environment.c is
where this code fragment came from. The CVS tag on the file is dated
2005/10/09 which is pretty recent.


--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
Q

quarkLore

except that this more up-to-date ("function prototype") version
facilitates the compiler's type-checking - which is why it was
introduced. The older form is still legal for hysterical reasons.

Ignore the older version if you can. Better still, update code that uses
it to adopt the prototype style.

Following compiles with gcc (version 3.2.2). Even if I use strict
flags like -ansi -Wall -Werror without warning or error. So using this
you can potentially landup in a mess.

int myf(i)
int i;
{
/* do something */
return(0);
}

int main(void)
{
myf(10,10);
return 0;
}
 
D

Daniel Rudy

At about the time of 2/9/2007 1:43 AM, Jens Thoms Toerring stated the
following:
It's not about initializing variables but function parameters. And
the second form is the correct form, the first form is from times
before standardization of C by ANSI/ISO. i.e. from before 1989/90.
It's only still supported for backward compatibility.
And, actually,
the way you write it isn't correct, if I remember correctly it would
have to be

That's interesting, because on programs that don't take arguments, I
always do int main(void).
int main(i,p)
int i;
char *p;

Hmm...maybe I missed something.

making main() a function that takes an integer argument 'i' and a char
pointer argument 'p' which would be written nowadays

int main(int i, char *p)

(Both are, of course, not standard-compliant since main() takes
either no arguments or an int and a char** or an array of char*.)

Regards, Jens


--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
R

Richard Heathfield

Daniel Rudy said:

Anyways, I know that this IS platform specific,

Fortunately, that doesn't matter here (and anyway, you tried, right?).
but here's what I saw, copied verbatim from the source file:

int
kenv(td, uap)
struct thread *td;
struct kenv_args /* [...] */ *uap;
{

That's legal.

The modern form is:
int
kenv(struct thread *td, struct kenv_args *uap)
{

They have identical semantics for all intents and purposes, except that
the latter form allows stricter type-checking by the compiler.
 
R

Richard Heathfield

quarkLore said:
Following compiles with gcc (version 3.2.2). Even if I use strict
flags like -ansi -Wall -Werror without warning or error. So using this
you can potentially landup in a mess.

int myf(i)
int i;
{
/* do something */
return(0);
}

int main(void)
{
myf(10,10);
return 0;
}

Which is precisely why I suggested updating the code to use the
prototype style.
</confused>
 
C

Chris Dollin

Daniel said:
OT: Speaking of style, forms like this bug me:

if ((fc = open(filename)) < 0)
errx("file error %d\n", errno);

How hard is it to sit there and do this instead?

fc = open(filename);
if (fc < 0) errx("file error %d\n", errno);

It's just so much easier to read, less error prone (since there are
fewer nested () ), and it probably generates the exact same code at the
machine level.

But doesn't work as part of an else-if without putting up
extra scaffolding.

(Myself, I want to know why I can't put /declarations/ in
the conditions of an if [1,2].)

[1] Because then I don't have to introduce variables before
I know their values.

[2] Yes, it can be done cleanly, although I haven't thought
hard about it /for C/.
 
D

Daniel Rudy

At about the time of 2/9/2007 2:03 AM, Richard Heathfield stated the
following:
Daniel Rudy said:



Fortunately, that doesn't matter here (and anyway, you tried, right?).

Tried? Not sure what you mean there...
but here's what I saw, copied verbatim from the source file:

int
kenv(td, uap)
struct thread *td;
struct kenv_args /* [...] */ *uap;
{

That's legal.

Ok. That form threw me for a loop. I had no idea what was going on.
The modern form is:
int
kenv(struct thread *td, struct kenv_args *uap)
{

They have identical semantics for all intents and purposes, except that
the latter form allows stricter type-checking by the compiler.

I've only used the modern form. People using different styles like that
makes the code much harder to read and decipher. But for a project as
complicated as an operating system kernel (that's what that code
fragment is from), you have a bunch of different people working on
different parts and everybody has their own style.


OT: Speaking of style, forms like this bug me:

if ((fc = open(filename)) < 0)
errx("file error %d\n", errno);

How hard is it to sit there and do this instead?

fc = open(filename);
if (fc < 0) errx("file error %d\n", errno);

It's just so much easier to read, less error prone (since there are
fewer nested () ), and it probably generates the exact same code at the
machine level.


--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
R

Richard Heathfield

Daniel Rudy said:
At about the time of 2/9/2007 2:03 AM, Richard Heathfield stated the
following:

Tried? Not sure what you mean there...

I mean that you made an effort in your original article to present the
problem in a platform-independent way - which was good. Okay, it didn't
work out on this occasion, and it turns out it didn't matter anyway,
but you did make the effort.
 
K

Keith Thompson

Daniel Rudy said:
At about the time of 2/9/2007 1:43 AM, Jens Thoms Toerring stated the
following: [...]
And, actually,
the way you write it isn't correct, if I remember correctly it would
have to be

Um, you snipped what Jens said it would have to be. Oh, well.
That's interesting, because on programs that don't take arguments, I
always do int main(void).

Yes, that's valid.

There are two permitted forms for main:

int main(int argc, char *argv[]) { /* ... */ }

int main(void) { /* ... */ }

(or equivalent).

This:

int main(argc, argv)
int argc;
char *argv;
{
/* ... */
}

is an old-style declaration; it's still legal, but it shouldn't be
used in new code.

That's equivalent to the above old-style declarations (the parameters
don't have to be named "argc" and "argv", but they are by convention,
and there's no good reason to use different names) -- except that
"char *p;" should be "char **p;" or "char *p[];".

[...]
 
C

CBFalconer

Daniel said:
.... snip ...

OT: Speaking of style, forms like this bug me:

if ((fc = open(filename)) < 0)
errx("file error %d\n", errno);

How hard is it to sit there and do this instead?

fc = open(filename);
if (fc < 0) errx("file error %d\n", errno);

It's just so much easier to read, less error prone (since there
are fewer nested () ), and it probably generates the exact same
code at the machine level.

While I am annoyed by (not to mention the non-existent open):

fc = fopen(filename, etc);
if (!fc) erroraction();
dowhatever(fc);

when

if (!(fc = fopen(filename, etc)))
erroraction(filename);
else (
/* all is well */
dowhatever(fc);
}

will show me precisely what is required to get to dowhatever, or to
erroraction. I also know that I will not fall into dowhatever when
erroraction (somewhere else) returns. I also trivially know that
dowhatever has a valid opened fc value passed to it.

Note that the preferred method above can be wrapped in:

do {
filename = getfcspecification();
if (!(fc = fopen(filename, etc)))
erroraction(filename);
else (
/* all is well */
dowhatever(fc);
}
} while (!fc);
fclose(fc); /* knowing that fc is non-NULL */

It is clearer because the thing that is being tested is the success
of fopen, not the value of fc (which is incidental). It also leads
to single entry / single exit modules, and conserves vertical
space, all of which enhances code readability.

Think it over.

--
<http://www.cs.auckland.ac.nz/~pgut001/pubs/vista_cost.txt>
<http://www.securityfocus.com/columnists/423>

"A man who is right every time is not likely to do very much."
-- Francis Crick, co-discover of DNA
"There is nothing more amazing than stupidity in action."
-- Thomas Matthews
 
D

Daniel Rudy

At about the time of 2/9/2007 3:09 AM, Keith Thompson stated the following:
Daniel Rudy said:
At about the time of 2/9/2007 1:43 AM, Jens Thoms Toerring stated the
following: [...]
And, actually,
the way you write it isn't correct, if I remember correctly it would
have to be

Um, you snipped what Jens said it would have to be. Oh, well.

It was snipped because my example was in error. That's what I get for
copying something from memory. :)
That's interesting, because on programs that don't take arguments, I
always do int main(void).

Yes, that's valid.

There are two permitted forms for main:

int main(int argc, char *argv[]) { /* ... */ }

int main(void) { /* ... */ }

(or equivalent).

This:

int main(argc, argv)
int argc;
char *argv;
{
/* ... */
}

is an old-style declaration; it's still legal, but it shouldn't be
used in new code.

Which begs the question as to why they did it that way.
That's equivalent to the above old-style declarations (the parameters
don't have to be named "argc" and "argv", but they are by convention,
and there's no good reason to use different names) -- except that
"char *p;" should be "char **p;" or "char *p[];".

[...]

I've never used anything but this for programs that take command line
arguments:

int main(int argc, char **argv)

But you say that you can also do this?

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

Hmmm.... It seems that second form might be better suited for argument
processing then that first one. I've always had problems properly
interpreting what ** meant, although I know it's a pointer to a pointer.



--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
D

Daniel Rudy

At about the time of 2/9/2007 2:20 AM, Chris Dollin stated the following:
Daniel said:
OT: Speaking of style, forms like this bug me:

if ((fc = open(filename)) < 0)
errx("file error %d\n", errno);

How hard is it to sit there and do this instead?

fc = open(filename);
if (fc < 0) errx("file error %d\n", errno);

It's just so much easier to read, less error prone (since there are
fewer nested () ), and it probably generates the exact same code at the
machine level.

But doesn't work as part of an else-if without putting up
extra scaffolding.

(Myself, I want to know why I can't put /declarations/ in
the conditions of an if [1,2].)

[1] Because then I don't have to introduce variables before
I know their values.

[2] Yes, it can be done cleanly, although I haven't thought
hard about it /for C/.

I've seen code like this that compiles just fine under C with all
warnings enabled (or at least the ones that I have turned on):

int function(int i)
{
switch(i)
{
case 0:
{
int a;
/* do something */
return(0);
}
case 1:
{
char *p;
/* do something */
return(0);
}
case 2:
{
unsigned long lax;
/* do something */
return(0);
}
default:
return(1);
}
return(0);
}

And here's the actual code:

char * inet_ntop_host(const struct sockaddr *sa, socklen_t salen)
{
char portstr[8];
static char str[128];

switch(sa->sa_family)
{
case AF_INET:
{
struct sockaddr_in *sin = (struct sockaddr_in *) sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) ==
NULL) return(NULL);
if(ntohs(sin->sin_port) != 0)
{
snprintf(portstr, sizeof(portstr), ":%d",
ntohs(sin->sin_port));
strcat(str, portstr);
}
return(str);
}
#ifdef IPV6
case AF_INET6:
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
if (inet_ntop(AF_INET6, &sin6->sin6_addr, str, sizeof(str))
== NULL) return(NULL);
if(ntohs(sin6->sin6_port) != 0)
{
snprintf(portstr, sizeof(portstr), ":%d",
ntohs(sini6->sin6_port));
strcat(str, portstr);
}
return(str);
}
#endif
#ifdef AF_UNIX
case AF_UNIX:
{
struct sockaddr_un *unp = (struct sockaddr_un *) sa;
if (unp->sun_path[0] == 0) strcpy(str, "(no pathname bound)");
else snprintf(str, sizeof(str), "%s", unp->sun_path);
return(str);
}
#endif
default:
snprintf(str, sizeof(str), "sock_ntop_host: unknown AF_xxx:
%d, len %d", sa->sa_family, salen);
return(NULL);
break;
}
return(NULL);
}

--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
D

Daniel Rudy

At about the time of 2/9/2007 2:32 AM, Richard Heathfield stated the
following:
Daniel Rudy said:


I mean that you made an effort in your original article to present the
problem in a platform-independent way - which was good. Okay, it didn't
work out on this occasion, and it turns out it didn't matter anyway,
but you did make the effort.

Oh, ok.

So I get a B for effort then? I guess I would have gotten an A if my
example was correct.

--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
D

Daniel Rudy

At about the time of 2/9/2007 7:22 AM, CBFalconer stated the following:
Daniel Rudy wrote:
... snip ...

While I am annoyed by (not to mention the non-existent open):

Hmm...open(2) exists on my system, but then again, I'm running Unix, so
it's probably not in the standard C library. It does the same thing as
fopen though, but take different parameters.
fc = fopen(filename, etc);
if (!fc) erroraction();
dowhatever(fc);

when

if (!(fc = fopen(filename, etc)))
erroraction(filename);
else (
/* all is well */
dowhatever(fc);
}

I would write that like this:

fc = fopen(filename, etc);
if (fc == NULL)
{
ERR("fopen failed", errno, 1)
return(EFUNCTERR);
}
dowhatever(fc);
return(0);

ERR is a macro that I made that is defined like this:

#define ERR(MM,CC,EE) ERROR(MM, PARAM_XXX_ERROR_SHOW,
PARAM_XXX_ERROR_FATAL, CC, EE)

From there, there is a #define ERROR and then it goes on to my error
handler.

will show me precisely what is required to get to dowhatever, or to
erroraction. I also know that I will not fall into dowhatever when
erroraction (somewhere else) returns. I also trivially know that
dowhatever has a valid opened fc value passed to it.

Note that the preferred method above can be wrapped in:

do {
filename = getfcspecification();
if (!(fc = fopen(filename, etc)))
erroraction(filename);
else (
/* all is well */
dowhatever(fc);
}
} while (!fc);
fclose(fc); /* knowing that fc is non-NULL */

It is clearer because the thing that is being tested is the success
of fopen, not the value of fc (which is incidental). It also leads
to single entry / single exit modules, and conserves vertical
space, all of which enhances code readability.

Think it over.

The problem that I have with that is the if line.

if (!(fc = fopen(filename, etc)))

I'm not savvy doing tricks like that, so I would have to think about
that statement to fully grasp the meaning of it. For the most part, I
follow the single entry / exit, but in the case of errors, I do an
immediate return with an error indication.

--
Daniel Rudy

Email address has been base64 encoded to reduce spam
Decode email address using b64decode or uudecode -m

Why geeks like computers: look chat date touch grep make unzip
strip view finger mount fcsk more fcsk yes spray umount sleep
 
K

Keith Thompson

Daniel Rudy said:
At about the time of 2/9/2007 3:09 AM, Keith Thompson stated the following: [...]
There are two permitted forms for main:

int main(int argc, char *argv[]) { /* ... */ }

int main(void) { /* ... */ }

(or equivalent).

This:

int main(argc, argv)
int argc;
char *argv;
{
/* ... */
}

is an old-style declaration; it's still legal, but it shouldn't be
used in new code.

Which begs the question as to why they did it that way.

I think it goes back to C's ancestor languages B and BCPL, and
possibly to Fortran. In the old days, the idea was that the compiler
would generate code for a call to a function based on the arguments in
the call; they weren't cross-checked against what the function
expected. The declarations of the types of the parameters are
internal to the function, and not visible at the point of call. If
you correctly pass an int and a char**, that's great. If you pass a
double and a function pointer, you lose, and the compiler won't help
you by printing an error message.

Prototypes, which specify the number and types of the expected
arguments, were a considerable improvement.

[...]
I've never used anything but this for programs that take command line
arguments:

int main(int argc, char **argv)

But you say that you can also do this?

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

In a parameter declaration (and *only* in a parameter declaration),
"char **argv" and "char *argv[]" are exactly equivalent.

The relationship between arrays and pointers in C is a tremendous
source of confusion. It's not really all that complex once you learn
the rules. But this special-case rule that what looks like an array
parameter declaration is really a pointer parameter declaration just
adds to the confusion, in my opinion. I recommend sections 4 and 6 of
the comp.lang.c FAQ, <http://www.c-faq.com/>.
 
C

Christopher Layne

Hard. Get used to the idiom, it's not a big deal unless it's causing line
wrapping issues. The idiom will become familiar to you in use.

Vertical real estate is always in demand unless you've got a different type of
monitor/terminal.
I've seen code like this that compiles just fine under C with all
warnings enabled (or at least the ones that I have turned on):

But you forgot the -Wimpending-beating option for indentation like that. That
is ridiculous.

Regardless of your level of affinity towards Linux (which might well be none
since I saw you mention FBSD), this is a good guide with clean semantics and
pragmatic execution:

Linux Kernel Coding Style guide:
http://www.mjmwired.net/kernel/Documentation/CodingStyle

With such classic Linus gems such as:

16 Chapter 1: Indentation
17
18 Tabs are 8 characters, and thus indentations are also 8 characters.
19 There are heretic movements that try to make indentations 4 (or even 2!)
20 characters deep, and that is akin to trying to define the value of PI to
21 be 3.
22
23 Rationale: The whole idea behind indentation is to clearly define where
24 a block of control starts and ends. Especially when you've been looking
25 at your screen for 20 straight hours, you'll find it a lot easier to see
26 how the indentation works if you have large indentations.
27
28 Now, some people will claim that having 8-character indentations makes
29 the code move too far to the right, and makes it hard to read on a
30 80-character terminal screen. The answer to that is that if you need
31 more than 3 levels of indentation, you're screwed anyway, and should fix
32 your program.

and

97 Chapter 3: Placing Braces and Spaces
98
99 The other issue that always comes up in C styling is the placement of
100 braces. Unlike the indent size, there are few technical reasons to
101 choose one placement strategy over the other, but the preferred way, as
102 shown to us by the prophets Kernighan and Ritchie, is to put the opening
103 brace last on the line, and put the closing brace first, thusly:
104
105 if (x is true) {
106 we do y
107 }
[...]
108
123 However, there is one special case, namely functions: they have the
124 opening brace at the beginning of the next line, thus:
125
126 int function(int x)
127 {
128 body of function
129 }
130
131 Heretic people all over the world have claimed that this inconsistency
132 is ... well ... inconsistent, but all right-thinking people know that
133 (a) K&R are _right_ and (b) K&R are right. Besides, functions are
134 special anyway (you can't nest them in C).

Amen.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top