Is this acceptable (i.e., compliant) code?

  • Thread starter William L. Bahn
  • Start date
W

William L. Bahn

Am I invoking any undefined behavior in the following program?

/* Function to print out the bit pattern stored in a variable regardless of
type */

#include <limits.h> /* CHAR_BIT */
#include <stdio.h> /* putc() */

void printbits(unsigned char *p, int bytes);

int main(void)
{
double x;
int k;

k = 64;
printbits((unsigned char *) &k, sizeof(int));
putc('\n', stdout);

k = -1;
printbits((unsigned char *) &k, sizeof(int));
putc('\n', stdout);

x = 3.1415;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

x = -1e300;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

x = 1e300;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

return(0);
}

void printbits(unsigned char *p, int bytes)
{
int byte;
unsigned char mask;

for(byte = 0; byte < bytes; putc(' ', stdout), byte++)
for(mask = 1 << (CHAR_BIT-1); mask; mask >>= 1)
putc((mask & p[byte])? '1': '0', stdout);

return;
}
 
M

Malcolm

William L. Bahn said:
Am I invoking any undefined behavior in the following program?
Can't see anything wrong. The main problem, which you have avoided, is that
plain chars can have trap representations, so you need unsigned char for
arbitrary bits.
Note that the output of the program will vary acording to the platform. ints
may not always have the same size and endianness. Theoretically double can
also have different representations, but pretty much everyone has settled on
the IEEE standard.

Convention is not to use parentheses for a return expression, BTW, but this
is the only quibble.
/* Function to print out the bit pattern stored in a variable regardless of
type */

#include <limits.h> /* CHAR_BIT */
#include <stdio.h> /* putc() */

void printbits(unsigned char *p, int bytes);

int main(void)
{
double x;
int k;

k = 64;
printbits((unsigned char *) &k, sizeof(int));
putc('\n', stdout);

k = -1;
printbits((unsigned char *) &k, sizeof(int));
putc('\n', stdout);

x = 3.1415;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

x = -1e300;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

x = 1e300;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

return(0);
}

void printbits(unsigned char *p, int bytes)
{
int byte;
unsigned char mask;

for(byte = 0; byte < bytes; putc(' ', stdout), byte++)
for(mask = 1 << (CHAR_BIT-1); mask; mask >>= 1)
putc((mask & p[byte])? '1': '0', stdout);

return;
}
 
M

Mark F. Haigh

William L. Bahn wrote:
<snip>

Although the code looks ok, I think the style can be brushed up a bit.

Suggestions:

1. printbits may be able to be static. Move it up in the file so the
definition serves as the prototype.

2. Don't unnecessarily jam code into loop constructs.

3. Use a void pointer argument instead of casting every argument to
printbits to unsigned char.

4. Use a size_t to represent sizes of objects. size_t is the type of
the result of the sizeof operator. size_t is defined in stddef.h.

5. Apply sizeof to the variable itself, rather than to the type of the
variable.

6. 'return(0);' is ugly, use 'return 0;' instead.


Mark F. Haigh




#include <stdio.h>
#include <stddef.h>
#include <limits.h>



static void printbits(void *mem, size_t size)
{
size_t i;
unsigned char mask;
char *p = mem;

for(i = 0; i < size; i++) {
for(mask = 1 << (CHAR_BIT - 1); mask; mask >>= 1)
putc((mask & p) ? '1' : '0', stdout);

putc(' ', stdout);
}
putc('\n', stdout);
}



int main(void)
{
double x;
int k;

k = 64;
printbits(&k, sizeof k);

k = -1;
printbits(&k, sizeof k);

x = 3.1415;
printbits(&x, sizeof x);

x = -1e300;
printbits(&k, sizeof x);

x = 1e300;
printbits(&k, sizeof x);

return 0;
}
 
T

Tak-Shing Thomas Chan

Am I invoking any undefined behavior in the following program?

/* Function to print out the bit pattern stored in a variable regardless of
type */

#include <limits.h> /* CHAR_BIT */
#include <stdio.h> /* putc() */

void printbits(unsigned char *p, int bytes);

size_t bytes?
int main(void)
{
double x;
int k;

k = 64;
printbits((unsigned char *) &k, sizeof(int));
putc('\n', stdout);

k = -1;
printbits((unsigned char *) &k, sizeof(int));
putc('\n', stdout);

x = 3.1415;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

x = -1e300;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

x = 1e300;
printbits((unsigned char *) &x, sizeof(double));
putc('\n', stdout);

return(0);
}

void printbits(unsigned char *p, int bytes)

void printbits(unsigned char *p, size_t bytes)?
{
int byte;

size_t byte?
unsigned char mask;

for(byte = 0; byte < bytes; putc(' ', stdout), byte++)
for(mask = 1 << (CHAR_BIT-1); mask; mask >>= 1)

(unsigned char) 1 << CHAR_BIT - 1?
putc((mask & p[byte])? '1': '0', stdout);

return;
}

Tak-Shing

P.S. Pedants might argue that the cast is unnecessary for hosted
implementations...
 
W

William L. Bahn

Mark F. Haigh said:
William L. Bahn wrote:
<snip>

Although the code looks ok, I think the style can be brushed up a bit.

Suggestions:

1. printbits may be able to be static. Move it up in the file so the
definition serves as the prototype.

I require my students (first year engineering students, not computer science
majors)
to have main() be the first function in the file - primarily so that the
grader can find
it quickly and efficiently. I also only have them place those prototypes
above main()
that are for functions called by main(). The rest of the function protorypes
go after
main() just before the function definitions themselves.
2. Don't unnecessarily jam code into loop constructs.

Agreed. As a rule I only like to put housekeeping tasks in the loop control.
3. Use a void pointer argument instead of casting every argument to
printbits to unsigned char.

4. Use a size_t to represent sizes of objects. size_t is the type of
the result of the sizeof operator. size_t is defined in stddef.h.

5. Apply sizeof to the variable itself, rather than to the type of the
variable.

Excellent suggestions.
6. 'return(0);' is ugly, use 'return 0;' instead.

Why? Is there some actual reason vs. just a style preference? The reason I
ask
is that I found that before I required my students to use the parens, they
were
more likely to leave out the parens on other statements and function calls.
By
imposing an in-course style guideline that requires the parens on return
statements
it seems to create a level of consistency that is less confusing for them at
this
stage in their programming. The hangup they have now, but it doesn't seem to
present a problem since the compiler catches it with a glaring error, is
they
want to use an empty parens to return from void functions. But they seem to
catch on to that quite quickly.

That's also why I require they put the names of functions and other elements
that
are used from one of the header files in a comment next to the #include.
They can
only keep the names of the elements they use and can't include a header that
they
aren't utilizing. It really seems to help them get familiar with what
functions are
associated with what header and keeps them from throwing tons of unneeded
#includes at the compiler. I explain to them that this is the only reason
that this
is required within the context of this course.

My new function:

#include <limits.h> // CHAR_BIT
#include <stdio.h> // putc()

typedef unsigned char BYTE;

void printbits(void *p, size_t bytes)
{
size_t byte;
BYTE mask;

for(byte = 0; byte < bytes; byte++)
{
if(byte) // Only put a space between byte patterns
putc(' ', stdout);
for(mask = 1 << (CHAR_BIT-1); mask; mask >>= 1)
putc((mask & ((BYTE *) p)[byte])? '1': '0', stdout);
}
return;
}

Casting the 1 to an unsigned char was a recommendation from another
responder. I don't know that it is necessary since I think I saw in the
standard that the representations for positive signed integers is supposed
to be identical to the representation for unsigned integers of the same base
type. Is this correct? But since I am wanting to establish a specific bit
pattern instead of a specific value, I didn't see that it could hurt.

I could also have had another variable that was declared as a pointer to an
unsigned char and set it equal to the passed-in variable, but I'm figuring
that the typecasting in the loop doesn't add any overhead but potentially
saves some memory - although I imagine most compilers would optimize out the
additional variable anyway.

I noticed that in your version you made p just a pointer to a char instead
of an unsigned char. Is this safe? What happens if I try to perform a
bitwise operation between an unsigned char and a signed char when (a) the
unsigned value is outside the range of a signed char. or (b) the signed
value is negative? Given the options for representing a negative integer,
couldn't that cause me problems when they are promoted?
 
J

Jens.Toerring

Why? Is there some actual reason vs. just a style preference? The reason I
ask is that I found that before I required my students to use the parens,
they were more likely to leave out the parens on other statements and
function calls. By imposing an in-course style guideline that requires the
parens on return statements it seems to create a level of consistency that
is less confusing for them at this stage in their programming. The hangup
they have now, but it doesn't seem to present a problem since the compiler
catches it with a glaring error, is they want to use an empty parens to
return from void functions. But they seem to catch on to that quite quickly.

I guess the reason it's seen as "ugly" by quite a few people is that
it's as superfluous as the parentheses would be in e.g.

a = b + ( 1 );

and might get unexperienced people confused whether return is supposed
to be a function (which it isn't as they will find out the hard way when
they write "return();" for a void returning function - as you describe).

Regards, Jens
 
C

CBFalconer

William L. Bahn said:
I require my students (first year engineering students, not
computer science majors) to have main() be the first function
in the file - primarily so that the grader can find it quickly
and efficiently. I also only have them place those prototypes
above main() that are for functions called by main(). The rest
of the function protorypes go after main() just before the
function definitions themselves.

Change it so that main is last. You get all the same benefits for
the graders, and teach better habits. Prototypes in headers are
for export. C requires declare before use, and duplicate
prototypes are an error source. It also discourages marking
non-exported routines as static.
 
E

Emmanuel Delahaye

William L. Bahn wrote on 09/08/04 :
I require my students (first year engineering students, not computer science
majors)
to have main() be the first function in the file - primarily so that the
grader can find
it quickly and efficiently.

I (old programmer with a background of 15 years) place the public
functions (like main() is) at the end of the compile unit. Easy to
find, just go to the end of the file!

The private functions (static) are defined before following the my
'define before use' personnal rule. If a detached prototype is
necessary in this source file, it means that there is some [un]wanted
recursive (aka mutual) call. This is the kind of thing I want to be
aware of.

Ugliness is enough for me.
 
F

Flash Gordon

Why? Is there some actual reason vs. just a style preference? The
reason I ask is that I found that before I required my students to
use the parens, they were more likely to leave out the parens on
other statements and function calls. By imposing an in-course style
guideline that requires the parens on return statements it seems to
create a level of consistency that is less confusing for them at
this stage in their programming. The hangup they have now, but it
doesn't seem to present a problem since the compiler catches it with
a glaring error, is they want to use an empty parens to return from
void functions. But they seem to catch on to that quite quickly.

I guess the reason it's seen as "ugly" by quite a few people is that
it's as superfluous as the parentheses would be in e.g.

a = b + ( 1 );

and might get unexperienced people confused whether return is supposed
to be a function (which it isn't as they will find out the hard way
when they write "return();" for a void returning function - as you
describe).[/QUOTE]

Another good reason for not using return(1); is the output of the
compiler when compiling the following two functions (use cut and paste,
don't retype):

int dodgy1(void)
{
returm(1);
}

int dodgy2(void)
{
returm 1;
}

Neither is what the programmer intended, but you generally get much more
help from the compiler with the second function.
 
O

Old Wolf

William L. Bahn said:
void printbits(unsigned char *p, int bytes);

printbits((unsigned char *) &k, sizeof(int));
printbits((unsigned char *) &k, sizeof(int));
printbits((unsigned char *) &x, sizeof(double));
printbits((unsigned char *) &x, sizeof(double));
printbits((unsigned char *) &x, sizeof(double));

You could save yourself a whole lot of casts, by making the
parameter a (void *), or a (void const *) if you want to add
some const-correctness, and then in the printbits function,
either cast it or declare another pointer of unsigned char type.
 
M

Mark F. Haigh

William L. Bahn said:
I require my students (first year engineering students, not computer science
majors)
to have main() be the first function in the file - primarily so that the
grader can find
it quickly and efficiently. I also only have them place those prototypes
above main()
that are for functions called by main(). The rest of the function protorypes
go after
main() just before the function definitions themselves.

So you're trying to teach first year, non-CS students C? I can only
hope, both for you and these kids, that you require some kind of prior
programming experience as a prerequsite.

As with other people here, I do not agree with your constraints on the
location of the main function. I'm sure the graders can be trained to
use the search feature of their favorite editor.


Why? Is there some actual reason vs. just a style preference?
<snip>

Technically, the only difference between IOCCC entries and good code
is style. By far and away, most professional C coders do not
syntactically treat the return keyword as some kind of
pseudo-function.

#include <limits.h> // CHAR_BIT
#include <stdio.h> // putc()

typedef unsigned char BYTE;

I highly recommend against this.

void printbits(void *p, size_t bytes)
{

I also recommend against using a pointer named 'p' unless the object
type makes it clear what 'p' actually is (for example, 'XmlParser *p'
or something)

I also recommend against calling the second parameter 'bytes' in
preference to 'size', but that's relatively minor.

size_t byte;

Generally speaking, many professional coders think it poor style to
declare a simple loop like this with a controlling variable other than
'i'.

BYTE mask;

for(byte = 0; byte < bytes; byte++)
{
if(byte) // Only put a space between byte patterns
putc(' ', stdout);
for(mask = 1 << (CHAR_BIT-1); mask; mask >>= 1)
putc((mask & ((BYTE *) p)[byte])? '1': '0', stdout);
}
return;
}


static void printbits(void *mem, size_t size)
{
size_t i;
unsigned char mask;
unsigned char *p = mem;
char ch;

for(i = 0; i < size; i++) {
for(mask = 1 << (CHAR_BIT - 1); mask; mask >>= 1) {
ch = (mask & p) ? '1' : '0';
putc(ch, stdout);
}
putc(' ', stdout);
}
putc('\n', stdout);
}


I'd wager that most c.l.c readers find this to be a more
understandable and idiomatic version of your original function. Quite
franly, any time spent "optimizing" this function is likely to be a
real-world waste of time, where this would only hopefully be used
during debugging.


Mark F. Haigh
 
P

pete

William said:
1 << (CHAR_BIT-1)
Casting the 1 to an unsigned char was a recommendation from another
responder. I don't know that it is necessary since I think
I saw in the standard that the representations for positive signed
integers is supposed to be identical to the representation for
unsigned integers of the same base type. Is this correct?
But since I am wanting to establish a specific bit
pattern instead of a specific value, I didn't see that it could hurt.

The problem is if sizeof(int) equals one.
In that case, (1 << CHAR_BIT - 1) is negative.
 
W

William L. Bahn

Mark F. Haigh said:
"William L. Bahn" <[email protected]> wrote in message

So you're trying to teach first year, non-CS students C? I can only
hope, both for you and these kids, that you require some kind of prior
programming experience as a prerequsite.

Unfortunately no - most of them have basically none although it
varies quite a bit. The most frustrating part is the lack of
mathematical preparedness that even the brightest among them
have. Last semester I was working with my two sharpest students
after class talking about the properties of modulo arithmetic and
some other things such as logarithms to an arbitrary base and
both of them (from different high schools) remarked that they had
never actually seen the log function used to solve a problem -
that they had merely been taught that when you see it in an
equation you punch this button on your calculator. The calculator
generation.

I now spend the first two weeks covering what a positional
numbering system is and how to do basic math using one. The
pre-req for the course is Calc I, but very few of them have any
understanding of how to work with exponents or the relationship
between a logarithm and an exponent or even how do perform long
division. One of the first example problems I work in class when
we get to functions is writing a function that computes x^y
without using the pow() function. It gives me the opportunity to
link the problem statement to the basic logarithmic relationship,
solve it for the quantity of interest, and then write a simple
function to spit back the result. I warn them that a problem like
this is probably going to be on the exam. Come the exam, almost
no one gets it right and the most common attempt is to write a
for loop multiplying x by itself y times - even though in class I
stressed how repeated multiplication doesn't work for exponents
that are not positive integers and gave examples and on the test
the problem stressed that x was a positive floating point value
and y was any floating point value. The most frustrating part is
that, after getting dinged on one exam, walking though the exam
in detail when it is handed back, and warning them that problems
that people struggled with are likely to appear on the next exam,
the same thing happens.

Heck, I have a hard time getting them to understand the
difference between "the first positive number greater than a
hundred" and "the first positive number that has greater than a
hundred digits".
As with other people here, I do not agree with your constraints on the
location of the main function. I'm sure the graders can be trained to
use the search feature of their favorite editor.

A couple of the other responders provided tangible benefits for
locating main() last, in particular to make any recursive
relationships readily apparent. That's a demonstrable benefit I
can accept and express and impose, so I'm changing the style
standards accordingly.

The same with the return statement. As long as it was merely what
someone terms "ugly" I have a hard time changing my standards
since I saw a benefit. Any of these students that ever write much
code are likely to be doing it at the level that I do - namely
small programs to perform specific tasks as a minor part of
completely unrelated duties. I design full custom integrated
circuits - and occasionally I write a C program to perform some
utility or task to support that. So rules that get them to make
fewer mistakes when working at that level have merit.

But getting a different compiler message if you mistype the
return statement (I'm particularly prone to putting in 'retrun'
statements) is something worthwhile to look at and I can also see
the merit in emphasizing that return is not a function - but then
why should it be different than if(), while(), for(), and others
that are not functions but yet require the associated expression
to be in parentheses? Why should the return statement be
different? Just because the standard will let you get away
without the parens I don't see as being a good justification for
not including them.
 
C

CBFalconer

William L. Bahn said:
.... snip ...

But getting a different compiler message if you mistype the
return statement (I'm particularly prone to putting in 'retrun'
statements) is something worthwhile to look at and I can also see
the merit in emphasizing that return is not a function - but then
why should it be different than if(), while(), for(), and others
that are not functions but yet require the associated expression
to be in parentheses? Why should the return statement be
different? Just because the standard will let you get away
without the parens I don't see as being a good justification for
not including them.

if, while, etc. are not functions, and therefore should never be
written without a blank following. IMNSHO. On the other hand, I
never leave a blank between a function and its parameter
delimiting '(', as in "while (blah)" vs "puts(string)". This is
purely a style matter.
 
F

Flash Gordon

On Mon, 9 Aug 2004 23:33:52 -0600

The same with the return statement. As long as it was merely what
someone terms "ugly" I have a hard time changing my standards
since I saw a benefit. Any of these students that ever write much
code are likely to be doing it at the level that I do - namely
small programs to perform specific tasks as a minor part of
completely unrelated duties. I design full custom integrated
circuits - and occasionally I write a C program to perform some
utility or task to support that. So rules that get them to make
fewer mistakes when working at that level have merit.

But getting a different compiler message if you mistype the
return statement (I'm particularly prone to putting in 'retrun'
statements) is something worthwhile to look at and I can also see
the merit in emphasizing that return is not a function - but then
why should it be different than if(), while(), for(), and others
that are not functions but yet require the associated expression
to be in parentheses? Why should the return statement be
different? Just because the standard will let you get away
without the parens I don't see as being a good justification for
not including them.

However, if, while and for are NOT the same syntactically as functions.
For some random functions "fred" and "derf" the line
fred(0) derf(0);
is invalid. However,
if(0) fred(0);
is valid and
return (0) fred(0);
is invalid.

With if, for and while the parenthesis have a specific purpose.
With functions the parenthesis used in C is following general
conventions.
The return statement is a separate case. If you use parenthesis there,
why not also with case, like so:
switch (fred) {
case (0): do_something();
break;
case (1): do_something_else();
break;
}

Other reasons for not bothering with the parenthesis include:
I'm lazy and it saves two key strokes.
I'm lazy and it saves reading two characters
It is more consistent with:
void blogs(void)
{
/* do stuff */
if (condition)
return;
/* do more stuff */
}

In fact, most of my coding decisions are based on some form of
constructive laziness. Either making it easier for the compiler to do my
job for me, making it easier for me to understand the code or reducing
the amount I have to type.
 
M

Mark F. Haigh

Unfortunately no - most of them have basically none although it
varies quite a bit. The most frustrating part is the lack of
mathematical preparedness that even the brightest among them
have. Last semester I was working with my two sharpest students
after class talking about the properties of modulo arithmetic and
some other things such as logarithms to an arbitrary base and
both of them (from different high schools) remarked that they had
never actually seen the log function used to solve a problem -

Maybe the problem here is that they haven't ever _needed_ to use
logarithms to solve a problem. The US public school system is a bit
lacking sometimes. Maybe they've used them for some math problems, but
nothing non-abstract. This is what you're signing up for when you
become a teacher, you know.


The
pre-req for the course is Calc I, but very few of them have any
understanding of how to work with exponents or the relationship
between a logarithm and an exponent or even how do perform long
division.


If your kids can't perform long division after Calc I, then you probably
have sufficient cause for an "incident" with the Mathematics department.


A couple of the other responders provided tangible benefits for
locating main() last, in particular to make any recursive
relationships readily apparent. That's a demonstrable benefit I
can accept and express and impose, so I'm changing the style
standards accordingly.

That's reassuring. I'd like to reiterate something I hope is shared by
most here: if you're a C teacher yourself and have any questions
whatsoever, please post your questions and take advantage of the
excellent resource that is c.l.c.
The same with the return statement. As long as it was merely what
someone terms "ugly" I have a hard time changing my standards
since I saw a benefit. Any of these students that ever write much
code are likely to be doing it at the level that I do - namely
small programs to perform specific tasks as a minor part of
completely unrelated duties. I design full custom integrated
circuits - and occasionally I write a C program to perform some
utility or task to support that. So rules that get them to make
fewer mistakes when working at that level have merit.

But getting a different compiler message if you mistype the
return statement (I'm particularly prone to putting in 'retrun'
statements) is something worthwhile to look at and I can also see
the merit in emphasizing that return is not a function - but then
why should it be different than if(), while(), for(), and others
that are not functions but yet require the associated expression
to be in parentheses? Why should the return statement be
different? Just because the standard will let you get away
without the parens I don't see as being a good justification for
not including them.

The return keyword is different because it's different. You say you
design "full custom integrated circuits", so you must be familiar with
this concept. You have it backwards: *you* are the one that needs to
provide justification for diverging from common C coding practice, not
me. C has a small vocabulary of keywords; I don't think it's a huge
problem here.

If you have problems putting in returm, retrun, or whatever, *use*
*your* *editor's* *syntax* *highlighting* to help you. Quite simply,
have it highlight the return keyword in one color (the right color), and
use the opposite inverse text color for your common typos (the wrong color).

Many engineers that are taking your class will go on to professionally
code in C for some (albiet perhaps small) period of their lives. Teach
them well!



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

pete

CBFalconer said:
if, while, etc. are not functions, and therefore should never be
written without a blank following. IMNSHO. On the other hand, I
never leave a blank between a function and its parameter
delimiting '(', as in "while (blah)" vs "puts(string)". This is
purely a style matter.

I use the same rule, except that I think of "if, while, etc."
as keywords, and I follow keywords with a blank space always.
 
C

Christopher Benson-Manica

Mark F. Haigh said:
By far and away, most professional C coders do not
syntactically treat the return keyword as some kind of
pseudo-function.

Unfortunately, that's the prescribed style at my place of
employment...
 

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,774
Messages
2,569,596
Members
45,140
Latest member
SweetcalmCBDreview
Top