Just curiosity about some constructs

S

Sensei

Hi again!

I have still curiosity about the reason of some C constructs/keywords...

The first is about static functions. What was the reason of restricting
a function to be visible just in a specific source file? Wasn't it
sufficient not to be given a prototype (for visibility)?

What about register and volatile variables? Was at that time a compiler
not smart enough to optimize with in-register variables? And why would
someone suggest the compiler not to optimze by making a variable
volatile?

Last question! This is about the switch statement. The statement seems
to me to be completely different from others. Let me explain with an
example. A while(condition) will execute the statement after the
while(), and if someone wants to have more instructions to be executed
in the loop, then { } should be used. This is true also for if/else,
do/loop, but not with the switch. A case does not require any { } to
execute more than one instruction, moreover, a brake must be given to
make a single case being executed, otherwise all the following
non-brake case statements will be executed. Why wasn't the switch like
the others with { } and automatic brake?

It's probably useless, but not for curiosity...
 
E

Eric Sosman

Sensei wrote On 01/31/06 10:01,:
Hi again!

I have still curiosity about the reason of some C constructs/keywords...

The first is about static functions. What was the reason of restricting
a function to be visible just in a specific source file? Wasn't it
sufficient not to be given a prototype (for visibility)?

No; you are confusing "scope" and "linkage." Without
`static', a function identifier has "external linkage," which
means that the name can refer to only one thing in the entire
program, no matter how many translation units are used. If
you have `void foo(void) {...}' in one file and in another
you write `double foo(char *ptr) {...}', the identifier has
external linkage in both and the two uses clash. With `static',
the identifier has "internal linkage," meaning that it does
not clash with any uses of `foo' (external or internal) in
other translation units.

A typical use is for a translation unit ("module") to
define some number of functions (and/or variables) with
external linkage, so code in other translation units can
refer to them. Meanwhile, "helper" functions (and/or
variables) are declared `static' so other modules cannot
refer to them and so their names won't clash with identifiers
used in those other modules. Think of `static' in this
sense as meaning "private."
What about register and volatile variables? Was at that time a compiler
not smart enough to optimize with in-register variables? And why would
someone suggest the compiler not to optimze by making a variable
volatile?

Compilers today are much better at optimizing than those
of the past. This is partly because of advances in the state
of the art, but mostly because the machines that run the
compilers are much larger and faster than those of the past.
The computations a modern compiler makes while performing its
optimizations would have taken far too much time and memory
to make them feasible on machines with 64KB of memory and a
250KHz CPU. `register' was a helpful hint to resource-strapped
compilers; nowadays it is largely useless.

`volatile' is a different matter, and is still relevant.
It tells the compiler that it is not safe to optimize the
accesses to a variable, usually because the variable can
change for reasons outside the compiler's knowledge or because
the act of making the access has some side-effect the compiler
cannot know about. One common example is the use of memory
locations that connect to I/O control registers, hardware
clocks, and so on. You might write a delay loop like

while (*hardware_timer < target_time)
;

.... and you'd be, er, "disappointed" if the compiler turned
this into the equivalent of

if (*hardware_timer < target_time)
while (1)
;

.... by optimizing away the "redundant" fetches of the value.
Similarly, you might control an I/O device by writing a
series of command codes to a special location:

*control_word = SEEK_TO_CYLINDER + cyl_num;
*control_word = START_WRITING;

.... and you'd be disappointed again if the compiler decided
to discard the first assignment on the grounds that the
value would be immediately overwritten by the second.
Last question! This is about the switch statement. The statement seems
to me to be completely different from others. Let me explain with an
example. A while(condition) will execute the statement after the
while(), and if someone wants to have more instructions to be executed
in the loop, then { } should be used. This is true also for if/else,
do/loop, but not with the switch. A case does not require any { } to
execute more than one instruction, moreover, a brake must be given to
make a single case being executed, otherwise all the following
non-brake case statements will be executed. Why wasn't the switch like
the others with { } and automatic brake?

The case labels in a `switch' are exactly that: labels
for single points in the code, not identifiers of regions
of code. That makes it easy to send several values to the
same piece of code:

switch (character) {
case ' ':
case '\t':
case '\n':
case '\f':
case '\r':
process_white_space();
break;

case '.':
case ',':
case ':':
process_punctuation();
break;

...
}

Many languages have a construct of this general flavor, not
all with quite the same rules. Why dmr chose this particular
scheme is a question only he can answer. His choice has been
both widely criticized and widely applauded.
 
V

Vladimir S. Oka

Sensei said:
I have still curiosity about the reason of some C constructs/keywords...

The first is about static functions. What was the reason of restricting
a function to be visible just in a specific source file? Wasn't it
sufficient not to be given a prototype (for visibility)?

If you don't make the function `static`, at the linking stage it may be
"found" by the linker looking for a function of the same name in the
object files provided, even if you did not provide its prototype in
other source files. To ensure your function is never "found" in this
way (i.e. is accessible only to other functions within the same source
file) you declare it `static`.
What about register and volatile variables? Was at that time a compiler
not smart enough to optimize with in-register variables? And why would
someone suggest the compiler not to optimze by making a variable
volatile?

The `register` variables were introduced at the time when compilers
were not as good at optimising, you got that one correctly.

The `volatile`, however, does /not/ mean "do not optimise". It means
"this variable may change behind your back, through no action of your
program, so do not make any assumptions about its value". In most, but
not all, cases this does in effect prevent the compiler from optimising
bits of code that refer to a `volatile` variable.
Last question! This is about the switch statement. The statement seems
to me to be completely different from others. Let me explain with an
example. A while(condition) will execute the statement after the
while(), and if someone wants to have more instructions to be executed
in the loop, then { } should be used. This is true also for if/else,
do/loop, but not with the switch. A case does not require any { } to
execute more than one instruction, moreover, a brake must be given to
make a single case being executed, otherwise all the following
non-brake case statements will be executed. Why wasn't the switch like
the others with { } and automatic brake?

I'm sure someone will correct me if I'm wrong, but I think that the
philosophy of the `switch` statement has historical reasons, or some
rationale that today, and if you don't know "the story of C", does not
make much sense. However, I do not think that the way it works is in
any way "wrong" or "strange", just "not as expected, or usual, given
other programming languages in existence today". IMHO, in many cases,
the way it works is actually preferrable. The only downside I can think
of is some extra typing for `break` statements (but you save on /not/
typing the braces! ;-) ).
It's probably useless, but not for curiosity...

I don't quite follow your thought here...

Cheers

Vladimir
 
S

Sensei

I don't quite follow your thought here...

Well, questions like this one can be seen as useless. I asked this just
for curiosity, not for any ``higher'' intention... :)
 
G

Gordon Burditt

I have still curiosity about the reason of some C constructs/keywords...
The first is about static functions. What was the reason of restricting
a function to be visible just in a specific source file? Wasn't it
sufficient not to be given a prototype (for visibility)?

No. The names would still conflict at link time.
And early C had no prototypes, per se, although there were
declarations which declared the return type (but not argument types).
What about register and volatile variables? Was at that time a compiler
not smart enough to optimize with in-register variables?

Yes, and sometimes people wanted to give the compiler hints
that the default optimization wasn't doing the best it could.
And why would
someone suggest the compiler not to optimze by making a variable
volatile?

Because the variable can be changed by *SOMETHING ELSE*. Either
the variable isn't memory but a hardware register, or it's modified
by a signal handler, another thread, etc.

while (siop->statusregister & TX_BUFFER_EMPTY)
/* wait */ ;

The classic problem comes when the compiler loads the value once,
then goes into an infinite loop rather than checking when the flag
comes on repeatedly.

Last question! This is about the switch statement. The statement seems
to me to be completely different from others. Let me explain with an
example. A while(condition) will execute the statement after the
while(), and if someone wants to have more instructions to be executed
in the loop, then { } should be used. This is true also for if/else,
do/loop, but not with the switch. A case does not require any { } to
execute more than one instruction, moreover, a brake must be given to
make a single case being executed, otherwise all the following
non-brake case statements will be executed. Why wasn't the switch like
the others with { } and automatic brake?

The switch *DOES* use { }, but it's very, very, very rarely appropriate
to use switch without { }.

You can do the same thing within { } (anywhere, as in after a while)
with labels and goto (although case labels are a little different
from regular labels, they are still labels).

Gordon L. Burditt
 
V

Vladimir S. Oka

Sensei said:
Well, questions like this one can be seen as useless. I asked this just
for curiosity, not for any ``higher'' intention... :)

Asking out of curiosity is not useless, as it can lead to increased
knowledge. Knowledge is rarely useless. Carry on wondering...

Cheers

Vladimir
 
C

CBFalconer

Eric said:
Sensei wrote On 01/31/06 10:01,:
.... snip ...


The case labels in a `switch' are exactly that: labels
for single points in the code, not identifiers of regions
of code. That makes it easy to send several values to the
same piece of code:

switch (character) {
case ' ':
case '\t':
case '\n':
case '\f':
case '\r':
process_white_space();
break;

case '.':
case ',':
case ':':
process_punctuation();
break;

...
}

Many languages have a construct of this general flavor, not
all with quite the same rules. Why dmr chose this particular
scheme is a question only he can answer. His choice has been
both widely criticized and widely applauded.

Since the case labels are exactly that I format switch statements
to show such. i.e.:

switch (character) {
case ' ':
case '\t':
case '\n':
case '\f':
case '\r': process_white_space();
break;

case '.':
case ',':
case ':': process_punctuation();
break;
...
default: ...
} /* switch */

by pulling the labels out to the left margin. I will then indent
other nested switch labels. Similarly I pull goto labels out to
the left. The indentation then shows flow of control properly.

--
"The power of the Executive to cast a man into prison without
formulating any charge known to the law, and particularly to
deny him the judgement of his peers, is in the highest degree
odious and is the foundation of all totalitarian government
whether Nazi or Communist." -- W. Churchill, Nov 21, 1943
 
R

Rod Pemberton

Sensei said:
Hi again!

I have still curiosity about the reason of some C constructs/keywords...
Last question! This is about the switch statement. The statement seems
to me to be completely different from others.
A case does not require any { } to
execute more than one instruction, moreover, a brake[sic, break] must be given to
make a single case being executed, otherwise all the following
non-brake[sic] case statements will be executed. Why wasn't the switch like
the others with { } and automatic brake[sic]?

The ultimate example of this can be found in "C: A Reference Manual" by
Samuel Harbison and Guy Steele, Tartan Laboratories, 3rd. edition, on page
231:

switch (x)
default:
if (prime(x))
case 2: case 3: case 5: case 7:
process_prime(x);
else
case 4: case 6: case 8: case 9: case 10:
process_composite(x);

The only other situation where I've seen an unstructured switch was to
select between two while's at the end of a do-while loop. Otherwise,
structured programming of switch is the way to go (using {}). I think the
reason behind unstructured elements in C, like 'goto' and 'switch', was to
allow porting of Fortran, which is unstructured, to C.

Rod Pemberton
 
K

Keith Thompson

Rod Pemberton said:
The ultimate example of this can be found in "C: A Reference Manual" by
Samuel Harbison and Guy Steele, Tartan Laboratories, 3rd. edition, on page
231:

switch (x)
default:
if (prime(x))
case 2: case 3: case 5: case 7:
process_prime(x);
else
case 4: case 6: case 8: case 9: case 10:
process_composite(x);

The only other situation where I've seen an unstructured switch was to
select between two while's at the end of a do-while loop. Otherwise,
structured programming of switch is the way to go (using {}). I think the
reason behind unstructured elements in C, like 'goto' and 'switch', was to

See also Duff's Device, <http://www.c-faq.com/misc/duff.html>.
 
R

Rod Pemberton

Keith Thompson said:

Cool! I don't recall seeing that one, although I'm sure I read Stroustrup's
book, just not as many times as Harbison's and Steele's, I guess. But on
the web, Tom Duff, regarding his "Duff's Device" said: "(Actually, I have
another revolting way to use switches to implement interrupt driven state
machines but it's too horrid to go into.)" So, we are still missing the
_ULTIMATE_ unstructured switch. :( The worst (or best?) part was I
understood Duff's Device before reading the description. :(


Rod Pemberton
 
J

Jordan Abel

Cool! I don't recall seeing that one, although I'm sure I read Stroustrup's
book, just not as many times as Harbison's and Steele's, I guess. But on
the web, Tom Duff, regarding his "Duff's Device" said: "(Actually, I have
another revolting way to use switches to implement interrupt driven state
machines but it's too horrid to go into.)" So, we are still missing the
_ULTIMATE_ unstructured switch. :( The worst (or best?) part was I
understood Duff's Device before reading the description. :(

Check this out:
http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

Update, 2005-03-07: Tom Duff confirms this in a blog comment. The
"revolting way to use switches to implement interrupt driven state
machines" of which he speaks in his original email is indeed the same
trick as I describe here.
 
R

Rod Pemberton

Jordan Abel said:
Keith Thompson said:
[...]
The ultimate example of this can be found in "C: A Reference Manual" by
Samuel Harbison and Guy Steele, Tartan Laboratories, 3rd. edition, on page
231:

switch (x)
default:
if (prime(x))
case 2: case 3: case 5: case 7:
process_prime(x);
else
case 4: case 6: case 8: case 9: case 10:
process_composite(x);

The only other situation where I've seen an unstructured switch was to
select between two while's at the end of a do-while loop. Otherwise,
structured programming of switch is the way to go (using {}). I
think
the
reason behind unstructured elements in C, like 'goto' and 'switch',
was
to

Cool! I don't recall seeing that one, although I'm sure I read Stroustrup's
book, just not as many times as Harbison's and Steele's, I guess. But on
the web, Tom Duff, regarding his "Duff's Device" said: "(Actually, I have
another revolting way to use switches to implement interrupt driven state
machines but it's too horrid to go into.)" So, we are still missing the
_ULTIMATE_ unstructured switch. :( The worst (or best?) part was I
understood Duff's Device before reading the description. :(

Check this out:
http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

Update, 2005-03-07: Tom Duff confirms this in a blog comment. The
"revolting way to use switches to implement interrupt driven state
machines" of which he speaks in his original email is indeed the same
trick as I describe here.

Yes! Thank you.

He calls it 'continue and return,' or similar to Pascal's 'with' (I have no
idea what that means). But, it is a nice way to 'ping-pong' between two
communicating/coordinated routines. It essentially allows a single
procedure with one entry point to have as many entry points as needed. Very
cool. Usefulness, questionable... That brings up another unstructured
element of C, besides 'goto' and 'switch', which is 'return'. A truly
structured procedure would only have or allow a single entry point and a
single exit point, i.e., 'return'. Instead , C allows multiple exit points,
via 'return', for procedures but has no multiple entry points by default.

Rod Pemberton
 

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