Indentation means that the contents of every block are promoted from their containing environment by using a shift of some space. This makes the code easier to read and follow.
Code without indentation is harder to read and so should be avoided. The Wikipedia article lists several styles – pick one and follow it.
Some people call their variables “file”. However, file can mean either file handles, file names, or the contents of the file. As a result, this should be avoided and one can use the abbreviations “fh” for file handle, or “fn” for filenames instead.
char * namesofpresidents[NUM_PRESIDENTS];
Say:
char * names_of_presidents[NUM_PRESIDENTS];
Or maybe:
char * presidents_names[NUM_PRESIDENTS];
In C++, classes should start with an uppercase letter (see the Wikipedia article about letter case) and starting them with a lowercase letter is not recommended.
# Bad code class my_class { . . . };
class MyClass { . . . };
If you’re interested in documenting the public interface of your modules and command-line programs, refer to tools such as Doxygen, which may prove of use.
# Bad code
obj->my_slot = 5;
.
.
.
if (my_obj.my_boolean_slot)
{
}
Instead, create accessors, readers and writers – see mutator method on the Wikipedia and the class Accessors section of the Perl for Newbies tutorial.
Your code should not include unnamed numerical constants also known as “magic numbers” or “magic constants”. For example, there is one in this code to shuffle a deck of cards:
# Bad code
for (int i = 0; i < 52; i++)
{
const int j = i + rand() % (52-i);
swap(cards[i], cards[j]);
}
This code is bad because the meaning of 52 is not explained and it is arbitrary. A better code would be:
const int deck_size = 52;
for (int i = 0; i < deck_size; i++)
{
int j = i + rand() % (deck_size - i);
swap(cards[i], cards[j]);
}
Some improperly configured text editors may be used to write code that, while indented well at a certain tab size looks terrible on other tab sizes, due to a mixture of tabs and spaces. So either use tabs for indentation or make sure your tab key expands to a constant number of spaces. You may also wish to make use of auto-formatters like GNU indent to properly format your code.
# Bad code
char * names[ITEMS_COUNT];
char * addresses[ITEMS_COUNT];
int ages[ITEMS_COUNT];
char * phone_numbers[ITEMS_COUNT];
.
.
.
names[num] = strdup("Isaac Newton");
addresses[num] = strdup("10 Downing St.");
ages[num] = 25;
phone_numbers[num] = strdup("123456789");
These arrays will become hard to synchronise, and this is error prone. A better idea would be to use an array (or a different data structure) of structs, classes, or pointers to them:
Person * people[ITEMS_COUNT];
num = 0;
people[num++] = create_person(
"Isaac Newton",
"10 Downing St.",
25,
"123456789"
);
Consider what Eric Raymond wrote in his “How to Become a Hacker” document (where hacker is a software enthusiast and not a computer intruder):
So if you’re posting code for public scrutiny, make sure it is written with English identifiers and comments.
See the Wikipedia article about “The Law of Demeter” for more information. Namely, doing many nested method calls like obj->get_employee('sophie')->get_address()->get_street()
is not advisable, and should be avoided.
A better option would be to provide methods in the containing objects to access those methods of their contained objects. And an even better way would be to structure the code so that each object handles its own domain.
As noted in Martin Fowler’s “Refactoring” book (but held as a fact for a long time beforehand), duplicate code is a code smell, and should be avoided. The solution is to extract duplicate functionality into subroutines, methods and classes.
Another common code smell is long subroutines and methods. The solution to these is to extract several shorter methods out, with meaningful names.
# Bad code
cond_var ? (hash["if_true"] += "Cond var is true")
: (hash["if_false"] += "Cond var is false")
(This is assuming the ternary operator was indeed written correctly, which is not always the case).
However, the ternary operator is meant to be an expression that is a choice between two values and should not be used for its side-effects. To do the latter, just use if
and else
:
if (cond_var)
{
hash["if_true"] += "Cond var is true";
}
else
{
hash["if_false"] += "Cond var is false";
}
This is safer, and better conveys one’s intentions.
For more information, refer to a relevant thread on the Perl beginners mailing list (just make sure you read it in its entirety).
It is a good idea to avoid global variables or static variables inside functions; at least those that are not constant. This is because using such variables interferes withmultithreading, re-entrancy and prohibits instantiation. If you need to use several common variables, then define an environment struct or class and pass a pointer to it to each of the functions.
# Bad code
int main(int argc, char * argv[])
{
const char * name;
int i;
name = "Rupert";
for (i=1 ; i<=10 ; i++)
{
printf ("Hello %s - No. %d!\n", name, i);
}
return 0;
}
Should be replaced with:
int main(int argc, char * argv[])
{
const char * const name = "Rupert";
for (int i=1 ; i<=10 ; i++)
{
printf ("Hello %s - No. %d!\n", name, i);
}
return 0;
}
Here is an example of having trailing whitespace demonstrated using the --show-ends
flag of the GNU cat command:
> cat --show-ends toss-coins.pl
$
use strict;$
use warnings;$
$
my @sides = (0,0);$
$
my ($seed, $num_coins) = @ARGV;$
$
srand($seed); $
$
for my $idx (1 .. $num_coins)$
{$
$sides[int(rand(2))]++;$
$
print "Coin No. $idx\n";$
}$
$
print "You flipped $sides[0] heads and $sides[1] tails.\n";$
>
While you should not feel bad about having trailing space, it is a good idea to sometimes search for them using a command such as ack '[ \t]+$'
(in version 1.x it should be ack -a '[ \t]+$'
, see ack), and get rid of them.
Some editors also allow you to highlight trailing whitespace when present. See for example:
Finally, one can check and report trailing whitespace using the following CPAN modules:
- “Code/Markup Injection and Its Prevention” resource on the Perl beginners site.
- Wikipedia articles about SQL injection and Cross-site scripting.
- The site Bobby Tables about SQL injections.
You should add #include guards, or the less standard but widely supported #pragma once into header files (“*.h” or “*.hpp” or whatever) to prevent them from being included times and again by other “#include” directives. Otherwise, it may result in compiler warnings or errors.
You can sometimes see code like that:
# Bad code
int * my_array[NUM];
int * sub_array = malloc(sizeof(sub_array[0]) * SUB_NUM);
if (! sub_array)
{
/* Handle out-of-memory */
}
for (int i = 0 ; i < NUM ; i++)
{
populate_sub_array(i, sub_array);
my_array[i] = sub_array;
}
The problem with code like this is that the same physical memory location is being used in all places in the array, and so they will always be synchronised to the same contents.
As a result, the code excerpts should be written as such instead:
int * my_array[NUM];
for (int i = 0 ; i < NUM ; i++)
{
int * sub_array = malloc(sizeof(sub_array[0]) * SUB_NUM);
if (! sub_array)
{
/* Handle out-of-memory */
}
populate_sub_array(i, sub_array);
my_array[i] = sub_array;
}
my @array_of_arrays = map { [] } (1 .. $num_rows);
The improvement in speed from Example 2 to Example 2a is only about 12%, and many people would pronounce that insignificant. The conventional wisdom shared by many of today’s software engineers calls for ignoring efficiency in the small; but I believe this is simply an overreaction to the abuses they see being practiced by penny-wise-and-pound-foolish programmers, who can’t debug or maintain their “optimized” programs. In established engineering disciplines a 12% improvement, easily obtained, is never considered marginal; and I believe the same viewpoint should prevail in software engineering. Of course I wouldn’t bother making such optimizations on a one-shot job, but when it’s a question of preparing quality programs, I don’t want to restrict myself to tools that deny me such efficiencies.
There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.
(Knuth reportedly attributed the exact quote it to C.A.R. Hoare).
If you do find that your program runs too slowly, refer to our resources about Optimising and Profiling code.
For more information and the motivation behind using version control systems, see the relevant section out of the fifth part of “Perl for Perl Newbies” for more discussion about the motivation behind that, some links and a demonstration.
Some links for further discussion:
- The Better SCM Site
- The Free Version Control Systems Appendix of Producing Open Source Software.
- The Wikipedia List of revision control software.
- “You Must Hate Version Control Systems” – a discussion on Dave Cross’ blog about best practices in the software development industry.
For more information about how to write automated tests, see our page about quality assurance in C.
You should make sure that the HTML markup you generate is valid HTML and that it validates as XHTML 1.0, HTML 4.01, HTML 5.0, or a different modern standard. For more information, see the “Designing for Compatibility” section in a previous talk.
If you want to group a certain sub-expression in a regular expression, without the need to capture it (into the $1
, $2
, $3
, etc. variables and related capture variables), then you should cluster them using (?: … )
instead of capturing them using a plain ( … )
, or alternatively not grouping them at all if it’s needed. That is because using a cluster is faster and cleaner and better conveys your intentions than using a capture.
Buffer overflows involve reading or writing after one end of the buffer and can lead to exploitation, or crashes. More information can be found in the Wikipedia article.
When passing a non-literal-constant string as the first parameter to “printf()”/sprintf()” and friends, one runs the risk of format string vulnerabilities (more information in the link). As a result, it is important to always use a literal constant string to format the string. E.g:
# Bad code
fgets(str,sizeof(str), stdin);
str[sizeof(str)-1] = '\0';
printf(str);
should be replaced with:
fgets(str,sizeof(str), stdin);
str[sizeof(str)-1] = '\0';
printf("%s", str);
One can also use the relevant warning flags of GCC and compatible compilers to warn and possibly generate an error for that.
# Bad code
void my_sort(my_type * const array, const size_t count,
int (*compare)(my_type *, my_type *));
And instead do that:
void my_sort(my_type * const array, const size_t count,
int (*compare)(my_type *, my_type *, void *), void * const context);
It is a very good idea for C and C++ code to use a good build and configuration system. There’s a page listing some prominent alternatives. For simple setups, a make file may be suitable, but more complex tasks require a configuration and build system such as CMake.
It is important to use a bug tracking system to maintain a list of bugs and issues that need to be fixed in your code, and of features that you’d like to work on. Sometimes, a simple file kept inside the version control system would be enough, but at other times, you should opt for a web-based bug tracker.
For more information, see:
- Joel on Software article about “Painless Bug Tracking”
- “Bug Trackers” list on Shlomi Fish’s “Software Construction and Management Tools” page.
- “Top 10 Open Source Bug Tracking Systems”
- A large part of this document is derived from a similar documentwritten earlier for the Perl programming language.
- The Book “Perl Best Practices” by Damian Conway – contains a lot of good advice and food for thought, but sometimes should be deviated from. Also see the “PBP Module Recommendation Commentary” on the Perl 5 Wiki.
- “Ancient Perl” on the Perl 5 Wiki.
- chromatic’s “Modern Perl” Book and Blog
- The book Refactoring by Martin Fowler – not particularly about Perl, but still useful.
- The book The Pragmatic Programmer: From Journeyman to Master – also not particularly about Perl, and I found it somewhat disappointing, but it is an informative book.
- The list “How to tell if a FLOSS project is doomed to FAIL”.
- Advice given by people on Freenode’s #perl channel, on the Perl Beginners mailing list, and on other Perl forums.
- Advice given by people on Freenode’s ##programming channel and on other forums.
Formats
- Read Online – one page per section.
- EPUB.
- Original DocBook 5/XML
- PDF (Acrobat Reader)
Version Control Repository
This document is maintained in a GitHub Repository which one can clone, fork, send pull-requests, and file issues for. Note that all contributions are assumed to be licensed under theCreative Commons Attribution 4.0-and-above (CC-by) licence. Enjoy!
Recent Comments