This document is intended to help you meet the design and coding style expectations for programs you submit for CSF.
These guidelines are somewhat specific to C and C++ programs. If the assignment specifications allow you to use other languages (such as Python), then we expect you to follow standard good coding style practices for the language you’re using.
It’s worth keeping in mind that the point of style guidelines is to help the programmer write readable and understandable code. They are not meant to be absolute rules.
Identifier naming
Choose good identifier names for your variables, functions, data types, etc.
Variable names should indicate the meaning/purpose of the information stored in the variable.
Function names should concisely describe the purpose of the function.
Data type names should name the problem domain entity that an instance of the data type refers to.
Identifiers consisting of multiple words can be “traditional style” (lower-case, words separated by underscores) or “camel case” (words except first are capitalized.) Traditional:
int cache_size;
float velocity_meters_per_sec;
Camel case:
int cacheSize;
float velocityMetersPerSec;
Indentation
Your program should use indentation levels to indicate the nesting level of nested blocks. Each increase in nesting should increase the indentation by one level. One indentation level should be between 2 and 8 spaces (inclusive). If we provide starter code, it will use 2 spaces per indent. If we do not provide starter code, then the choice is completely up to you.
You should either use exclusively spaces or exclusively tabs for indentation. Do not mix spaces and tabs. We recommend using spaces exclusively.
For assembly language source code, it is traditional to indent each instruction using a single tab character. This is the only situation in which we recommend using tabs. Note that you can still use spaces for formatting towards the end of the line (e.g., to align comments) as long as the tab is at the beginning of the line.
Brace placement
“Java style” is fine:
#include <stdio.h>
int main(void) {
for (int i = 0; i < 10; i++) {
printf("%d\n", i);
}
return 0;
}
“Microsoft style” is also fine:
#include <stdio.h>
int main(void)
{
for (int i = 0; i < 10; i++)
{
printf("%d\n", i);
}
return 0;
}
Comments
Each source file (header and implementation) should have a header comment describing the purpose of the file, the assignment name, your name and email, and (if appropriate) your partner’s name and email. Example:
/*
* String functions for multithreaded client/server calculator program
* CSF Assignment 7
* A. Student
* astude99@jhu.edu
*/
Each function should have a comment describing the purpose and operation of the function, its parameters, and its return value (if any). Example:
/*
* Skip leading whitespace in a C character string, returning
* a string (using the same storage as the original string)
* with leading whitespace removed. If there is no leading
* whitespace, returns the original string.
*
* Parameters:
* s - pointer to a C character string
*
* Returns:
* a pointer to the first non-whitespace character in s
*/
const char *skip_whitespace(const char *s) {
...
}
Use appropriate comments to document the intent behind your code, especially the more complex parts of your code. Note that comments are not a substitute for writing clear and understandable code.
Consistency
Be consistent in your coding style. For example:
- Don’t mix identifier naming conventions
- Use consistent indentation
- Use consistent brace placement
- Use consistent function comments
If you are working in a team, make sure that all of the team members are following the same coding style conventions.
Design
Use good modular design practices in your program. Use functions to manage the complexity of your code: once the body of a function reaches about 20 (non-comment) lines of code, you should consider refactoring your code to use helper functions. There is no absolute rule about function length, but the following guidelines about function length are worth considering:
- 1-20 non-comment lines of code: most functions should be in this range
- 20-30 non-comment lines of code: you should consider refactoring code into one or more helper functions to reduce complexity
- 30-50 non-comment lines of code: it might be reasonable for one or two
functions in your program to be this long (perhaps your
main
function), but deductions for over-complexity are a possibility - More than 50 non-comment lines of code: expect a style deduction
In general you could possibly have one function in the program longer
than 50 lines if there is a compelling reason. For example, in a network
server, you might have a switch
statement which handles all of the various
kinds of messages the server might receive. You should still strive to
modularize your code in a sensible way; for example, in the network server,
each switch
case could call a helper function to handle the message.
Note that the above length guidelines pertain only to high-level source code. For assembly language code, where there is much lower code density and factoring out helper functions is more challenging, functions of 100+ lines can be defensible.
Use problem-domain-specific data types as appropriate. For example, in a
file server application, you might have a data type called FileResource
representing a file resource managed by the server. Each problem-domain
data type should be supported by functions to implement important
operations. For example, the FileResource
data type might support
the following function to get the file size of a file resource:
ssize_t file_resource_get_size(struct FileResource *res);
Memory correctness
We expect that all code written in memory-unsafe languages such as C and C++ to execute without any memory errors or memory leaks. Make sure that you use tools such as valgrind when testing your programs. In general, we will deduct points for code that does not execute cleanly in valgrind, or exhibits other types of memory errors at runtime.