The right tools make this requirement easier! On
stu (and all Linux systems), there is a command-line utility called
clang-format that will automatically format your source code according
to various standards. You can run it on one file at a time by passing the file
name. By default, it would just print out the formatted version, but you can
redirect that output into a file (in this case, main.c.gnu
) and
rename it to overwrite the original version.
$ clang-format --style=gnu main.c > main.c.gnu [format main.c file]
$ mv main.c.gnu main.c [rename to overwrite the original]
Banned C Functions
The functions listed below are common sources of security vulnerabilities,
and their use is banned. Use of any of these functions will result
in an automatic penalty to projects.
The following list of functions are banned and any use of any of them will
yield a penalty on projects:
atoi()
, atol()
, atoll()
- You must use
strtol()
instead.
atof()
- You must use
strtod()
instead.
gets()
- You must use
fgets()
instead, using stdin
as the FILE*
argument.
strcat()
, strcpy()
, sprintf()
- You must use
strncat()
, strncpy()
,
or snprintf()
instead. (Bonus: It would actually be
better to use strlcat()
or strlcpy()
, but
those are considered non-standard C.)
JMU Honor Code Statement
Every project submission must contain the following section which follows the
class description or must cite any sources used (such as a TA). This statement
should appear in the specified location of the src/main.c file.
/* This work complies with the JMU Honor Code.
* References and Acknowledgments: I received no outside
* help with this programming assignment. */
OR
/* This work complies with the JMU Honor Code.
* References and Acknowledgments: TA Ringo helped me
* with the sgt_pepper() function. */
Additional resources, including generative AI
All code you submit for credit in this course must be your own. However, writing
code (like so many other things in life and learning) is a social endeavor. It is
acceptable to consult other resources (such as Stack Overflow) for clarifying
examples but not for wholesale copying of significant (more than 5-10 lines) pieces
of code. Any such references must be documented explicitly within code comments.
Similarly, generative AI tools can be used to learn common approaches to writing
small (again, no more than 5-10 lines) pieces of code. Any such uses must be documented
in comments, including the specific tools and prompts you used.
Copying and pasting any part of the project descriptions or source code into
generative AI tools or search engines is strictly prohibited.
Coding Style Requirements
- Comments - Use them. Generally speaking, every 5-10 lines of
code should have a brief one-line comment like shown below.
- Consistent file structure - All .c and .h files
should look like this:
/* zoo.h */
#ifndef __CS361_ZOO__
#define __CS361_ZOO__
/* other headers if needed */
#include <stdbool.h> /* bool data types */
/* struct declarations needed by code other than zoo.c */
typedef struct zoo {
/* zoo's internal stuff */
} animal_t;
bool init_zoo (animal_t);
#endif
|
/* main.c */
#include <stdio.h> /* Standard I/O */
/* other includes here */
/* struct declarations needed only by code in THIS file */
typedef struct foo {
/* foo's internal stuff */
} my_foo_struct_t;
/* prototypes for functions needed only by code in THIS file */
void print_foo (my_foo_struct_t *);
/* global variables */
my_foo_struct_t master_foo;
/* function definitions */
int
main (int argc, char **argv)
{
print_foo (&master_foo);
return 0;
}
|
- Indentation and alignment - Two spaces with curly braces on the
next line. Examples:
Do this: |
Don't do this |
int
main (int argc, char **argv)
{
/* code here */
return 0;
}
|
int main (int argc, char **argv) {
/* code here */
return 0;
}
|
for (i = 0; i < 10; i++)
printf ("%d\n", i);
|
for (i = 0; i < 10; i++)
printf ("%d\n", i);
|
for (i = 0; i < 10; i++)
{
printf ("%d\n", i);
}
|
for (i = 0; i < 10; i++) {
printf ("%d\n", i);
}
|
if (i < 10)
{
printf ("%d\n", i);
}
else
{
printf ("Nope\n");
}
|
if (i < 10) {
printf ("%d\n", i);
} else {
printf ("Nope\n");
}
|
switch (input)
{
case 1:
printf ("1");
break;
default:
printf ("2");
}
|
switch (input) {
case 1: printf ("1");
break;
default: printf ("2";
}
|
- Naming conventions - When naming functions and variables, use
all lower-case and use an underscore ('_') to separate words.
Pick meaningful names and types.
Do this: |
Don't do this |
size_t bytes_read; char *message_read;
|
int b; char *MyIncomingMessage;
|
- No long lines - All lines of code should be no longer than 80 characters wide. Long
lines of code should be broken and wrapped to the next line. The
continued line should be indented in a natural alignment:
Do this: |
some_variable = a_really_long_variable_name + another_variable +
yet_another_really_long_name;
|
int
calculate_sum (int first_value, long second_value, int third_value,
unsigned int last_value)
{
/* ... */
}
|
- No long functions - Restrict functions to two screens (about 100 lines of code), and never
have more than 3 levels of indentation. Code that is longer than this
or uses more levels of indentation is typically too complex to
maintain. For instance, you should never do this:
Don't do this |
if (...)
{
while (...)
{
if (...)
{
if (...)
{
for (...)
{
|
- Safe pointer practices - After calling free() to free a pointer, immediate follow up
by setting the pointer to NULL. After calling malloc()
to get dynamically allocated data, validate the allocation was successful
by checking that the pointer is not equal to NULL.
- Use assertions for invariants - If there is a condition that
should never be false (i.e., very bad, unpredictable behavior
will occur), include an assert() in your code in an
appropriate place. (Don't forget to #include <assert.h>)
For instance, assume that a function takes an integer
parameter input and the input should only be between 1 and 5.
At the top of your function, you should have a statement like this:
Do this |
assert (input > 1 && input <= 5);
if (input == 0)
cause_my_computer_to_blow_up_while_playing_one_direction_songs ();
|
- Always initialize local variables - If you do not provide an explicit
initial value for local variables, the variable will be created with an
unpredictable, random value. This could cause very unexpected behavior, which
may be different every time you run the program. If you don't know what
to set it to, 0, false, or NULL tend to be good
options.
Recommended guidelines
In addition to the requirements above, the following practices are used in the
base source code distributions. Following these guidelines would help your code
to match the existing style and make your code more readable.
- When making a function call, put a space between the function name and
the opening '(' as well as one after each comma:
Do this: |
Don't do this |
printf ("hello %s from %s\n",
to_person, from_person);
|
printf("hello %s from %s\n",to_person,from_person);
|
- When using malloc() to dynamically allocate an array, use
sizeof() instead of making assumptions about data sizes:
Do this: |
Don't do this |
size_t num_entries = 5;
uint16_t *data =
malloc (sizeof (uin16_t) * num_entries);
|
uint16_t *data = malloc (10);
|
- Short-circuit functions when checking for errors rather than using
if-else blocks.
Do this: |
Don't do this |
if (value < 0)
{
fprintf(stderr, "bad value\n");
return 1;
}
/* code using value here */
|
if (value >= 0)
{
/* code using value here */
}
else
{
fprintf(stderr, "bad value\n");
return 1;
}
|
- Avoid global variables. Global variables should be thought of as a last
resort, as the values can change from one function call to the next.
- Use #define for constants as appropriate. The name of the
constant should be in all upper-case:
Do this: |
Don't do this |
#define NUMBER_OF_QUESTIONS 5
for (question = 0; question < NUMBER_OF_QUESTIONS;
question++)
{
/* stuff here */
}
|
for (i = 0; i < 5; i++)
{
/* stuff here */
}
|