Params -> local vars?

J

Jon Howson

Hello

I was reading thru some (yellowing!) mimeographed introductory notes on C
and I found code like the below.

main(argc,argv)
int argc;
char *argv[];
{
char *malloc();
char *strcpy();
int puts();
void free();

char *s;
if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
strcpy(s,"Hello, ");
strcpy(s+7,argv[1]);
puts(s);
free(s);
}else{
puts("Program failed, unknown error");
}
}

I'm trying to work out what's happening. At the start of the function, I
guess the parameters are being converted into local variables, maybe a
way to get pass-by-value in C?

Also what is the advantage of declaring the string and memory functions
locally to the function?

Cheers
Jon
 
E

Eric Sosman

Hello

I was reading thru some (yellowing!) mimeographed introductory notes on C
and I found code like the below.

main(argc,argv)
int argc;
char *argv[];
{
char *malloc();
char *strcpy();
int puts();
void free();

char *s;
if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
strcpy(s,"Hello, ");
strcpy(s+7,argv[1]);
puts(s);
free(s);
}else{
puts("Program failed, unknown error");
}
}

I'm trying to work out what's happening. At the start of the function, I
guess the parameters are being converted into local variables, maybe a
way to get pass-by-value in C?

It's the original "K&R style" way of defining functions and
function parameters. In this style, the names of the parameters
are separated from their types: The first line lists the names,
in order, and the lines before the opening `{' "complete" the
picture by filling in the missing details: types and perhaps
qualifiers.

Modern C still supports K&R style in the name of backward
compatibility, but it's been obsolete since C89/C90 compilers
became generally available some two decades ago. In a way, it's
a good sign that you didn't know about this style; it shows that
progress is indeed taking place!
Also what is the advantage of declaring the string and memory functions
locally to the function?

None; it's an anti-advantage. In the long-gone Days of Yore
there was no universally-available header declaring malloc() and
free() -- some implementations provided <malloc.h>, some didn't.
Also, there was no universal header for the string functions --
some implementations used <string.h> and some used <strings.h>.
In those days there might have been a small advantage in declaring
those functions yourself, so you didn't have to worry about them
being in different (or no!) places on different systems. (Note also
that malloc() is declared as returning a `char*' -- K&R C had no
`void*'.)

But there's never been a good reason to write a free-hand
declaration of puts()! The <stdio.h> header has been around since
a microsecond after the Big Bang, and the right way to declare the
I/O functions has always been `#include <stdio.h>'.

Those old notes may have value, but only on Antiques Road Show.
 
S

Stefan Ram

Jon Howson said:
main(argc,argv)
int argc;
char *argv[];

This is an older variant of the C language,
that is usually not used today to write new code in.
I'm trying to work out what's happening.

If your »introductory notes on C« do not explain this,
try other tutorials or textbooks, possibly, describing
a more recent C version.
 
J

James Kuyper

Hello

I was reading thru some (yellowing!) mimeographed introductory notes on C
and I found code like the below.

main(argc,argv)
int argc;
char *argv[];
{
char *malloc();
char *strcpy();
int puts();
void free();

char *s;
if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
strcpy(s,"Hello, ");
strcpy(s+7,argv[1]);
puts(s);
free(s);
}else{
puts("Program failed, unknown error");
}
}

I'm trying to work out what's happening. At the start of the function, I
guess the parameters are being converted into local variables, maybe a
way to get pass-by-value in C?

No - that's a non-prototyped function definition. They're still
supported by the C standard, but they've been obsolete ever since the
introduction of function prototypes in C90. Function prototypes turn on
various mandatory diagnostics, but as far as the actual behavior of the
code, that declaration is equivalent to:

main(int argc, char *argv[])

Note that no return type is specified. This used to be permitted; the
function was implicitly declared as returning an 'int'. However,
"implicit int" has not been supported since C99.
Also what is the advantage of declaring the string and memory functions
locally to the function?

You are allowed to provide your own declaration of some standard library
functions, so long as it is compatible with the correct declaration.
However, it's always a bad idea, even when permitted. You're much better
off #including the appropriate standard headers. In this case, those are
strings.h, stdio.h, and stdlib.h.

The advantage is very small, or possibly even negative: it restricts the
scope of those declarations to the body of main(). This allows
definition of identifiers with the same name and a different meaning
outside of main(), but that would still only work if they did not have
external linkage.

One disadvantage is that a declarations may become incorrect, even if it
originally was correct. That is in fact the case in this code. At one
time, before the first standard, malloc() returned a char*. However,
since the first standard malloc() has returned a void*. However, since
char* and void* are required to have the same representation and
alignment requirements, they are intended to be equivalent for this kind
of use; it would take a pretty perverse (but still fully conforming)
implementation of C for that to be a real problem.
 
S

Stephen Sprunk

I was reading thru some (yellowing!) mimeographed introductory notes on C
and I found code like the below.

main(argc,argv)
int argc;
char *argv[];
{
char *malloc();
char *strcpy();
int puts();
void free();

char *s;
if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
strcpy(s,"Hello, ");
strcpy(s+7,argv[1]);
puts(s);
free(s);
}else{
puts("Program failed, unknown error");
}
}

I'm trying to work out what's happening. At the start of the function, I
guess the parameters are being converted into local variables, maybe a
way to get pass-by-value in C?

Also what is the advantage of declaring the string and memory functions
locally to the function?

This is K&R C, which had a different syntax for arguments and no
standard headers. To restate it in ANSI C:

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

int main(int argc, char *argv[]) {
char *s;

if (argc>1 && (s = malloc(strlen(argv[1]) + 8))) {
strcpy(s, "Hello, ");
strcpy(s+7, argv[1]);
puts(s);
free(s);
} else {
puts("Program failed, unknown error");
}
}

A modern coder would just use printf(), but perhaps that hadn't been
taught yet or would have obscured what the text was trying to teach
about string manipulation--or printf() was considered excessive for such
a trivial task on the day's limited machines.

Unless you're interested in software archaeology, I would recommend you
discard any text that uses K&R C, which has been obsolete since ANSI C
was published in 1989.

S
 
K

Keith Thompson

Jon Howson said:
I was reading thru some (yellowing!) mimeographed introductory notes on C
and I found code like the below.

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

This is an old-style function definition. It was the only form
available before the 1989 ANSI C standard introduced prototypes
(borrowed from C++). It's still legal -- except for the omission of the
return type of main, which has been invalid since C99.

The modern equivalent is:

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

which has the advantage that it makes the parameter types visible to
callers (which is typically less relevant for main than for other
functions).
{
char *malloc();
char *strcpy();
int puts();
void free();

char *s;
if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
strcpy(s,"Hello, ");
strcpy(s+7,argv[1]);
puts(s);
free(s);
}else{
puts("Program failed, unknown error");
}
}

I'm trying to work out what's happening. At the start of the function, I
guess the parameters are being converted into local variables, maybe a
way to get pass-by-value in C?

As I wrote above, it's an old-style definition. (Parameters are really
local variables anyway; they differ from other local variables in how
they're declared and in the way they're initialized with the values of
the arguments given in the call.)
Also what is the advantage of declaring the string and memory functions
locally to the function?

None. The right way to get those declarations is:

#include <stdlib.h> /* malloc, free */
#include <string.h> /* strcpy */
#include <stdio.h> /* puts */

which also has the advantage of providing prototypes, so the compiler
can diagnose calls with incorrect arguments.

The code as posted will probably work with modern compilers (as long as
they permit the old-style "implicit int" rule, dropped by C99), but it's
not a good example for writing new code.
 
J

Joe Pfeiffer

Jon Howson said:
Hello

I was reading thru some (yellowing!) mimeographed introductory notes on C
and I found code like the below.

main(argc,argv)
int argc;
char *argv[];
{
char *malloc();
char *strcpy();
int puts();
void free();

char *s;
if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
strcpy(s,"Hello, ");
strcpy(s+7,argv[1]);
puts(s);
free(s);
}else{
puts("Program failed, unknown error");
}
}

I'm trying to work out what's happening. At the start of the function, I
guess the parameters are being converted into local variables, maybe a
way to get pass-by-value in C?

Do you mean this part?
main(argc,argv)
int argc;
char *argv[];
{

That's just the way parameters used to be declared. It means exactly
the same thing as

main(int argc, char *argv[])
Also what is the advantage of declaring the string and memory functions
locally to the function?

I can't think of one off-hand.
 
M

Mark Bluemel

Unless you're interested in software archaeology, I would recommend you
discard any text that uses K&R C, which has been obsolete since ANSI C
was published in 1989.

A little harsh - I think that K&R 1st edition is worth keeping if you
have a copy.
 
K

Kleuske

A little harsh - I think that K&R 1st edition is worth keeping if you
have a copy.

Not really smart, either. It may just be a collectors item.I would pay
money for an original K&R manual, if only for sentimental reasons.

For educational purposes, however, I'm with Sprunk.
 
E

Eric Sosman

Not really smart, either. It may just be a collectors item.I would pay
money for an original K&R manual, if only for sentimental reasons.

US$3.44 plus $3.99 shipping on Ebay. Get 'em while they're hot.

(No, I'm not selling mine.)
For educational purposes, however, I'm with Sprunk.

Seconded, or maybe thirded. The language has changed a lot
in the three and a half decades since K&R came off the presses,
and many code samples in K&R, though exemplary (by definition!)
at the time would nowadays be considered poor style if not just
plain broken.

"Whan that Aprill, with his shoures soote
The droghte of March hath perced to the roote
And bathed every veyne in swich licour,
Of which vertu engendred is the flour ..."

A classic of English literature, but not much help in learning
English. Just so with K&R: The language has moved on.
 
T

Tim Rentsch

Jon Howson said:
Hello

I was reading thru some (yellowing!) mimeographed introductory notes on C
and I found code like the below.

main(argc,argv)
int argc;
char *argv[];
{
char *malloc();
char *strcpy();
int puts();
void free();

char *s;
if(argc>1 &&(s=malloc(8+strlen(argv[1])))){
strcpy(s,"Hello, ");
strcpy(s+7,argv[1]);
puts(s);
free(s);
}else{
puts("Program failed, unknown error");
}
}

I'm trying to work out what's happening. At the start of the function, I
guess the parameters are being converted into local variables, maybe a
way to get pass-by-value in C? [snip]

Just a few clarifications/additions to points made in other responses.

1. This style of parameter declaration (dating from more than 30
years ago, before C was standardized) is labelled 'obsolescent'
by the Standard. That means it's accepted now but don't count
on it being so in future revisions of the Standard. (On the other
hand it has been obsolescent for more than 20 years now.)

2. The newer (ie, current) style of parameter declarations is called
"prototypes", as for example

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

3. The prototype style of parameter declaration allows (and requires)
any call after the prototype declaration to have the types of its
arguments checked against the parameter types given in the prototype.
In contrast, the old style of parameter declaration does not do
such type checking (and the Standard says specifically that such
type checking will not be done).

4. The argument values are passed the same way in both cases,
except that, when old-style parameter declarations are used,
the parameter types are adjusted in some cases to reflect the
lack of knowledge about the types used. For example, a parameter
declared as 'char' will be passed as 'int' (or 'unsigned int' in
some implementations).

5. Except for the change of types mentioned in (4), how parameters
are used in function bodies is the same for both old-style and
the prototype form of parameter declaration.

6. Most people advise using the prototype declaration style when
writing any new code. I myself follow that advice in most cases
(and refrain now from trying to explain any of the exceptions).

7. For existing code using the older-style (aka "K&R style")
declarations, the most obvious benefit of changing it over to the
prototype form is better type checking. There are however two
gotcha's worth noting. One, the higher level of type checking
could produce compilation errors even though the code in question
works fine. (Of course it's also possible the code doesn't work
fine, and the type checking showed a problem.) Two, because of
the different rules for how parameter types work, it's possible
for a change that looks innocuous at face value to turn working
code into non-working code (and probably also the reverse, but I
think that's even less likely). So those changes are not ones
that should be made completely mindlessly and automatically.

8. (I'm skipping the question about the local function declarations
for malloc(), etc, because I don't have anything to add about
that here.)
 
K

Keith Thompson

Tim Rentsch said:
3. The prototype style of parameter declaration allows (and requires)
any call after the prototype declaration to have the types of its
arguments checked against the parameter types given in the prototype.
In contrast, the old style of parameter declaration does not do
such type checking (and the Standard says specifically that such
type checking will not be done).
[...]

Checking is not *required*, but an implementation is free to perform it
anyway.

For example:

void func(arg)
int arg;
{
/* ... */
}

...

func("foo");

The call has undefined behavior. No diagnostic is required (and I
don't think I've seen a compiler warn about this), but (a) compilers
are free to warn about anything they like, and (b) given undefined
behavior that's detectable at compile time, a compiler is free to
treat it as a fatal error.
 
T

Tim Rentsch

Keith Thompson said:
Tim Rentsch said:
3. The prototype style of parameter declaration allows (and requires)
any call after the prototype declaration to have the types of its
arguments checked against the parameter types given in the prototype.
In contrast, the old style of parameter declaration does not do
such type checking (and the Standard says specifically that such
type checking will not be done).
[...]

Checking is not *required*, but an implementation is free to perform it
anyway. [snip example]

Talking about function calls, 6.5.2.2 p 8 says "the number and
types of arguments are not compared with those of the parameters
in a function definition that does not include a function
prototype declarator."

You're right that implementations are free to issue any
diagnostics they want. At the same time, what is in those
diagnostics cannot depend on the results of comparisons
that implementations are prohibited from making.
 
K

Keith Thompson

Tim Rentsch said:
Keith Thompson said:
Tim Rentsch said:
3. The prototype style of parameter declaration allows (and requires)
any call after the prototype declaration to have the types of its
arguments checked against the parameter types given in the prototype.
In contrast, the old style of parameter declaration does not do
such type checking (and the Standard says specifically that such
type checking will not be done).
[...]

Checking is not *required*, but an implementation is free to perform it
anyway. [snip example]

Talking about function calls, 6.5.2.2 p 8 says "the number and
types of arguments are not compared with those of the parameters
in a function definition that does not include a function
prototype declarator."

The full paragraph is:

No other conversions are performed implicitly; in particular,
the number and types of arguments are not compared with those
of the parameters in a function definition that does not include
a function prototype declarator.
You're right that implementations are free to issue any
diagnostics they want. At the same time, what is in those
diagnostics cannot depend on the results of comparisons
that implementations are prohibited from making.

My suspicion is that the intent was not to forbid making those
comparisons, just that any argument conversions should not depend
on such comparisons. It certainly does *say* that the comparisons
are not performed, but it's in the context of implicit conversions.

It doesn't make much sense to forbid a compiler to perform
comparisons don't necessarily have any effect. And as long as
old-style function definitions are in the language, it would be nice
if some compilers could warn about calls with mismatched arguments.

Off to comp.std.c.
 
R

Richard Damon

Keith Thompson said:
Tim Rentsch said:
3. The prototype style of parameter declaration allows (and requires)
any call after the prototype declaration to have the types of its
arguments checked against the parameter types given in the prototype.
In contrast, the old style of parameter declaration does not do
such type checking (and the Standard says specifically that such
type checking will not be done).
[...]

Checking is not *required*, but an implementation is free to perform it
anyway. [snip example]

Talking about function calls, 6.5.2.2 p 8 says "the number and
types of arguments are not compared with those of the parameters
in a function definition that does not include a function
prototype declarator."

You're right that implementations are free to issue any
diagnostics they want. At the same time, what is in those
diagnostics cannot depend on the results of comparisons
that implementations are prohibited from making.

The implementation is not prohibited from making any comparisons that it
might want to make, and can say what ever it wants to say, as long as it
prints at least some sort of diagnostic message for a program that
violates a constraint.

It is free to emit a message for anything it wants, even for things that
are "valid code". It is still conforming to issue warnings for things
like unneeded parenthesis or missing unneeded parenthesis that some
coding style might suggest be included due to people often getting
things wrong.

The resulting program output from the implementation (only required if
the program violates no constraints) must produce the standard defined
behavior (for as long as the standard defines the behavior, possibly
with some implementation defined behavior when that is mandated by the
standard). Since calling a non-prototyped function with the wrong
parameter types is not a constraint violation, the compiler can not use
this as grounds to not produce a resulting program (although when/if the
call occurs it creates undefined behavior), but it is free to make what
ever warnings it wants.
 
T

Tim Rentsch

Richard Damon said:
Keith Thompson said:
[...]
3. The prototype style of parameter declaration allows (and requires)
any call after the prototype declaration to have the types of its
arguments checked against the parameter types given in the prototype.
In contrast, the old style of parameter declaration does not do
such type checking (and the Standard says specifically that such
type checking will not be done).
[...]

Checking is not *required*, but an implementation is free to perform it
anyway. [snip example]

Talking about function calls, 6.5.2.2 p 8 says "the number and
types of arguments are not compared with those of the parameters
in a function definition that does not include a function
prototype declarator."

You're right that implementations are free to issue any
diagnostics they want. At the same time, what is in those
diagnostics cannot depend on the results of comparisons
that implementations are prohibited from making.

The implementation is not prohibited from making any comparisons that it
might want to make, and can say what ever it wants to say, as long as it
prints at least some sort of diagnostic message for a program that
violates a constraint. [snip elaboration]

If you're arguing that the wording of 6.5.2.2 p 8 allows more
than one interpretation, and that interpretation might not
disallow such checking, I agree that aspect is debatable.

If you're arguing that implementations are always free to issue
diagnostics, regardless of how the restrictions in 6.5.2.2 p 8 are
worded, I can't agree with that -- the license to issue diagnostics
does not trump the obligation to meet all other requirements in the
Standard, whatever they are, for all conforming implementations.
 

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