Posted on 6 mins read

Algorithms and data structures are a central part of software development. I thought it would be reinforce the knowledge I have in this area. Competitions in this domain seem to favor cpp and knowing a variant of c better will certainly help when doing hardware programming. Hence, we eplore the basics of cpp before we move on to data structures and then algorithms.

A series in cpp begins. Naturally, it involves more than just language.

Reference: Accelerated C++ Note: This post is written largely in the context of using OSX (my laptop).

A program

#include <iostream>
#include <string>

int main() {
    {
        const std::string s = "Hot chocolate is nice.";
        std::cout << s << std::endl;
    }
    {
        const std::string s = "COOP iced tea is nice!";
        std::cout << s << std::endl;
    }
    return 0;
}

A basic program might look like something above. #include libraries and send something to the output.

Things to note here would be: - std namespace is used explicitly. - The first :: used may be called the scope operator. - << and its partner >> are redirection operators. - { } brackets create scopes. You may consider them as units. The program runs here because there 2 units and they only access the variables within them. - return 0: 0 return value implies the program executed fine. Any other non-zero return value means an implementation defined failure..

Makefile

To run a program coded in cpp, you need to first compile the cpp code and then execute the resulting executable file. It can get quite repetive. To make things simpler it useful to have a script to automate the build process. Creating a Makefile for the make tool is one way you can achieve such automation. Some call it a Macro.

run: compile
        ./play

compile:
        g++ play.cpp -o play

By default the tool make process the first target. By convention, the first target is called all (we are not following that here to be clear on how make works). The questions that arise now are: What is the format of a make file? What is target?

A makefile has a format like so:

target: dependencies
[tab character] system command

Another way you can specify the default target is link:

.DEFAULT_GOAL := mytarget

For a newer make > 3.80:

.PHONY: default
default: mytarget ;

gcc vs g++

You could just do make in the same directory as your cpp code. An output file will be created which you can execute like ./the_output_file.out. What I think happens is that make gets the default compiler for the cpp code. For OSX, in my case, it should be gcc.

Anyway, whats important to know here is the difference between g++ and gcc as above we specified g++.

Quoting a stackoverflow post:

The main differences:

1. gcc will compile: *.c/*.cpp files as C and C++ respectively.
2. g++ will compile: *.c/*.cpp files but they will all be treated as C++ files.
3. Also if you use g++ to link the object files it automatically links in the std C++ libraries (gcc does not do this).
4. gcc compiling C files has less predefined macros.
5. gcc compiling *.cpp and g++ compiling *.c/*.cpp files has a few extra macros.

Knowing the difference might just help you when you have one of those projects involving sensors or microcontrollers!

C++ Strings

Frankly, at the moment I still do not understand enough about between strings and string literals. Being pragmatic, I will look at some practical use cases.

#include <string>
using std::string;

string stringLiteral = "characters enclose in double quotes and cannot span lines";

//overloaded constructor to create a string with n of character.
string repeatCharacter z(10, '*');

//a string
string someString = string("i am a string");

A string literal is a bunch of characters in doubles and they cannot span lines. Having some prior knowledge in C and C++, I would say that a string literal is a series of characters in memory terminated by a null character \0. Maybe it would be better to call them character literal arrays.

As for a string, you use a contructor string("some text").

A common use case with strings is to join them just like creating a sentence where you want to join words or phrases together. This is where you can see a difference (refence link):

string stringLiteral1 = "hello";
string stringLiteral2 = " world";

//you cannot do because they are both string literals.
string s = stringLiteral1 + stringLiteral2;

//(A)convert them to strings
string newstring = string(stringLiteral1) + string(stringLiteral2);

//(B)or perhaps
std::stringstream ss;
ss << "Hello, world, " << myInt << niceToSeeYouString;
std::string s = ss.str();

//(C)or using append; a little verbose?.
std::string s;
s.append("Hello world, ");
s.append("nice to see you, ");
s.append("or not.");

At a glance, I think I would prefer using method (A) or (B) to create dynamic strings. In a game prototype, NPCs could have a lengthly paragraph text to say depending on a player’s actions. What if there really was a lot to output because the NPC is such a great storyteller?

string::size_type could be useful if you have > 32767 characters. The book says that it is good practice to use size_type for the size of strings since it was created for the purpose of sizing strings.

// size_type used intead of int
const std::string::size_type cols = greeting.size() + pad * 2 + 2;

The last point for this post is off-topic from strings but it is relevant to cpp… Quoting the book:

One reason to count from 0 is that doing so encourages us to use asymmetric ranges to express intervals. For example, it is natural to use the range [0, rows) to describe the first for statement, as it is to use the range [1, rows] to describe the second one.
Asymmetric ranges are usually easier to use than symmetric ones because of an important property: A range of the form [m, n) has n - m elements, and a range of the form [m, n] has n - m + 1 elements. So, for example, the number of elements in [0, rows) is obvious (i.e., rows - 0, or rows) but the number in [1, rows] is less so.
This behavioral difference between asymmetric and symmetric ranges is particularly evident in the case of empty ranges: If we use asymmetric ranges, we can express an empty range as [n, n), in contrast to [n, n-1] for symmetric ranges. The possibility that the end of a range could ever be less than the beginning can cause no end of trouble in designing programs.