C++ Data Types and Variables

C++ variables:

#include <iostream>

using namespace std;

int main() {

int someVar = 123;

int someOtherVar = 555;

int sum = someVar + someOtherVar;

cout << “Sum is ” << sum << endl;

string firstName = “Alice”;

cout << firstName << endl;

float decimalNum = 123.45;

double doubleNum = 234.56;

bool trueOrFalse = false;

char myLetter = 65;

printf(“%c”, myLetter);

return 0;

}

For more information about chars, see http://www.asciitable.com/

A char numeric value corresponds to an ASCII character. For example, 65 is A.

One important note about variables in C/C++ is that they don’t have a default initialization. Other languages will have a default initialization, or default value, when you make a variable. Some other languages will just complain and give you a compiler error if you try to compile code where your variables haven’t been initialized. In C/C++, it doesn’t care and will allow you to compile code even if something hasn’t been initialized. This is bad for stability and security, because the value of the variable will just be whatever garbage value was in RAM at the time, so it’s not predictable or safe.

So even though C++ will let you declare and use a variable without initialization, it’s not something you should get in the habit of doing. In Java, it’s easy to check if something hasn’t been initialized. Not only will you be unable to run code that uses an uninitialized variable, but you can also compare a variable to null. If something is equal to null, it’s not initialized (or you deleted it). But in C++, you can do no such comparison. In C++, a pointer can be null, but a reference can’t. If you declare something like an int with no initialization or assignment, it still isn’t null per se, it just has an unpredictable value because it will be whatever values were in RAM at the time. In more modern languages, a lack of initialization will mean something is null, and it’s easier to check for issues related to that.

C++ wide characters (wchar_t):

chars only support basic stuff like A-Z, a-z, 0-9, and some basic punctuation. If you want to use characters from different languages, or even A-Z characters with accent marks, then you’ll need to use wide characters. If, for example, you want your app to support Chinese character output, it will need wide characters, a.k.a. wchar_t. There is a related concept called wstring. Even Spanish needs support for more characters, such as ñ, é, and ¿, though those are all supported in extended ASCII (but other language characters are not). It can get really complicated when you’re dealing with both left-to-right (English and Spanish) and right-to-left (Arabic or Hebrew) text, but that is beyond the scope of this book.

The reason why wide characters are called wide is because they need more bits to represent the characters, because ASCII is shorter due to only supporting very few characters. Each character needs a unique combination of bits to represent it, so if the character set wants to support more types of characters, such as for many different languages or even emojis, it needs to be longer, or wider. You get 2n character support, where n is the number of bits. ASCII is 8-bit (there’s also 7-bit ASCII but we’ll ignore that in this book), and Unicode is 16-bit. That means ASCII can support 28 or 256 characters, and Unicode can support 216, or 65,536 characters. Wide characters require more memory, but the advantage is the support for all kinds of characters. ASCII is limited in what you can use in it, but it takes up less space. These days, a byte (8 bits) here and there being wasted isn’t the end of the world, as computers are very fast and have lots of storage and RAM now. But in the past, or even in current jobs where you’re coding for a low-end embedded system, efficient use of memory is important.

If your program is only in English, then you don’t need to worry too much about wchar_t or character encoding. But if you want to write it in another language, or even have multi-language support (such as where the user can choose one of many languages), then you really want to look into this stuff more in-depth. Another related concept is locales, which means language support within an operating system or program.

C++ constants (things that can’t be changed):

#include <iostream>

using namespace std;

int main() {

const int MY_CONST = 123;

cout << MY_CONST << endl;

return 0;

}

C++ type casting:

The following is an example of type casting a double (a decimal number) to an int in a semi-useless program that only serves to demonstrate type casting:

#include <iostream>

using namespace std;

int main() {

double salary = 50000.0;

int twoYears = ((int) salary) * 2;

cout << twoYears << endl;

return 0;

}

And here is how you convert strings to ints using stoi() from <string>:

#include <iostream>

#include <string>

using namespace std;

int main() {

string notIntYet = “1234”;

int nowAnInt = stoi(notIntYet);

cout << nowAnInt * 5 << endl;

return 0;

}

The following code example shows how to convert an int to a string using to_string() from <string>:

#include <iostream>

#include <string>

using namespace std;

int main() {

string firstPart = “I’m so “;

int coolNumber = 1337;

string secondPart = to_string(coolNumber);

string wholeMessage = firstPart + secondPart;

cout << wholeMessage << endl;

return 0;

}

The above also demonstrates operator overloading of + and also string concatenation. String concatenation can be better than using a zillion << operators in a cout if you’re trying to piece together something to output.

C++ arrays:

If you want to have a lot of similar things, you use an array. You can have an array of integers to represent many numbers. They are their own individual things, but the array aspect of it is the grouping of it. You can have an array of user-defined objects. For example, if you make a video game where there are lots of enemies on the screen at the same time, you might make an Enemy array, where every element in the array is an instance of the Enemy class, each with its own separate instance variables, such as hitpoints, x_position, and y_position, but still all being related. This is easier than making tons of separate but similar objects with no grouping. Arrays are fast for lookups, but slow for shifting. For example, if you had an array of 12 eggs, and you filled it with eggs [0] to [10], and you wanted to put an egg in between to others, such as between [2] and [3], you would have to shift all the subsequent eggs down one, which is a time-consuming process. You can write it pretty easily with loops, but it would take the CPU a while to perform the shifting. A linked list is better if you need to insert things in between other things, as linked list nodes point to the next item in the linked list (or both next and previous for a doubly-linked list), but they are slower for accessing things because they aren’t a contiguous block of memory.

In some languages, arrays can be resized. But in other languages, arrays are a fixed size. So what happens if you have an Egg class array with 12 eggs in it, but you want to add a 13th egg to it (at index 12 because you start with 0)? That kind of thing is a big limitation of arrays.

Python lists can keep on appending more stuff to them. Java’s ArrayLists can do similar things. But in C++, arrays are a fixed length. If you want something like an array that is able to be resized, you will have to use something called a vector instead, which is basically a dynamic array. Also, keep in mind that while Python is a simpler and more flexible language, C++ is a lot faster. A C++ array can sometimes be a little rough to deal with, but it will be much faster than a Python list.

I listened to a podcast once where people were discussing the pros and cons of different things in programming, and someone said they preferred things with key-value pairs (instead of arrays) so that you can access something by a name rather than an index. Indeed, a numeric index for an element is a little less obvious than being able to refer to something by a name. But arrays are still useful in some situations.

Here is an array example in C++ where elements are added one by one after the array is declared:

#include <iostream>

using namespace std;

int main() {

int arrayExample [4];

arrayExample[0] = 123;

arrayExample[1] = 2;

arrayExample[2] = arrayExample[1] + 5;

arrayExample[3] = 45;

return 0;

}

In some cases, you might prefer to use a loop to populate an array iteratively. This method is useful for large arrays. It would be very tedious to manually assign, say, 100 different things separately rather than with a loop that does it all in one go. Or if you really have a ton of data, you might want to keep it in a separate file (such as .txt, .csv, .xml, or .json), and then have your program merely read the data from the file. But file IO will be covered later in the chapter.

Below is an example of incorporating loops into the process of creating an array:

#include <iostream>

using namespace std;

int main() {

int arrayExample [4];

cout << “Creating the array…” << endl;

for (int i = 0; i < 4; i++) {

arrayExample[i] = i;

}

cout << “Array values: “;

for (int i = 0; i < 4; i++) {

cout << arrayExample[i] << ” “;

}

printf(“”);

return 0;

}

Here is an example of an array in C++ where all items are listed, which is an alternative way of doing it:

#include <iostream>

using namespace std;

int main() {

int arrayExample [4] = {634, 852, 9, -1};

cout << arrayExample[1] << endl;

return 0;

}

Arrays start with 0, just like many other things in programming. The first element in an array is at index 0, the second item is [1], the third one is [2], and so on. Any time you see square brackets with numbers in it, you’re probably dealing with arrays. The following example shows a problem with an incorrect array index, being out of bounds:

#include <iostream>

using namespace std;

int main() {

//valid indices are 0-3

int arrayExample [4] = {0, 1, 2, 3};

//4 is the 5th item in the array

//because the first is 0

cout << arrayExample[4] << endl;

//the above line is bad

return 0;

}

When you compile the above code, it will still compile with no errors or warnings, despite the fact that it’s really bad code and shouldn’t be allowed:

$ g++ main.cpp -o hello.exe

And here is the output of running the above program after compiling it:

$ ./hello.exe

4200896

If you run the above program, it won’t display the same number for you. Instead, it will be whatever random junk is in memory for you. Notice how there is nothing in the code that mentions the number 4200896.

Sometimes, array indices being really far out of bounds in C++ can cause a segmentation fault (a.k.a. segfault) if it’s trying to access memory locations it’s not allowed to, thus causing the program to crash. This can happen in some cases, but not all. It’s not defined and not predictable. This is sometimes referred to as undefined behavior, which is something you need to avoid.

Here’s the problem with arrays indices being out of bounds in C++: it doesn’t care. It will compile anyway, even though it’s wrong and will lead to unpredictable behavior. If you tried to do something similar in Java, it would raise an exception called ArrayIndexOutOfBoundsException. Some people are annoyed by exceptions or compile-time errors, but they exist for a reason: to protect you from the mistakes you make. But in C++, the way an array’s memory is allocated means that it’s a bunch of contiguous addresses in memory, where the size of each element depends on its type, so trying an array index that is after the final real array element will just get you the memory of whatever was in the next bits of RAM, which will be seemingly random garbage. C++ won’t hold your hand and lets you make egregious mistakes, whereas a language like Java tries to make sure you don’t make big mistakes like this.

C++ is like a chainsaw. It’s very powerful and useful, but it can hurt you if you don’t use it correctly. A lot of hacking is just people finding and using a bug in software to make the software do something that the developer didn’t intend to happen. So write good code to avoid bugs. Easier said than done, but being aware of even little things like array index bounds issues will help. Be cognizant of all the little idiosyncrasies of the language you’re using.

To recap array indices (plural for index), if you have an array of size [4], the last element in the array is [3] and you start with [0], not [1]. Another way to understand this is, if your array is of size n, then the final element in the array will be at index n – 1. It sounds like a simple concept, but off-by-one errors and array index out of bounds issues are common, especially among beginner programmers.

Arrays aren’t a type by themselves, but rather, you create arrays of different types, like int, string, double, boolean, Cat, Person, or whatever other class, type, or data structure you want.

If you specify the size of an array using a variable rather than a hard-coded number, it will need to be a constant. You can always reference an array element using a variable as the index (such as someArray[i]), but you can’t create it that way.

If you are working with code that has an array and you don’t know what the size is, you could either spend a lot of time looking through all the other code to figure its size out, or you could do this simple method to find an array’s length/size:

#include <iostream>

using namespace std;

int main() {

int arrayExample [4] = {0, 1, 2, 3};

int arrayLength = sizeof(arrayExample) / sizeof(arrayExample[0]);

cout << arrayLength << endl;

return 0;

}

In the above code example, sizeof(arrayExample) gets the entire amount of memory that the array takes up. This is not the same as the number of elements within the array. If you have and int array of size 4, it will typically take up 4 bytes (32 bits) per int element, so that would be 16, because 4*4 is 16 bytes. But if you just want to find out the number of elements in an array, you will have to divide the whole size by the size of a single element within the array. In this case, a single element is 4 bytes. So sizeof(arrayExample) / sizeof(arrayExample[0] is 16/4, or 4. In Java, you could do something much simpler: arrayExample.length. But C++ lacks many of the conveniences that Java has.

It’s true that all elements in the array need to be the same type, but you can also create an array of a superclass with elements that are subclasses. In Java, there is a super-duper superclass called Object, which all classes inherit from (often indirectly because of many layers of inheritance), so in theory, a Java program could feature an Object array with elements of any type. However, that is bad practice. A better example of superclass arrays with subclass elements in C++ would be a Pet superclass array with Dog and Cat subclass elements. That way, you could do something like loop through the entire array and access superclass methods like greet(), and due to polymorphism, the Dog objects in the array would cout “woof” and the cat ones would output “meow.” However, this multi-subclass array concept has some issues, like not knowing the type of a particular element and treating it like the other kind. That’s where typeid can be useful, where you can use if/else to check the type of a particular element. However, some people say this idea of polymorphic arrays (or whatever you want to call them) is a bad idea and you should avoid it entirely. Some would suggest only making a Cat array or a Dog array, but not a superclass Pet array.

C++ typeid():

#include <iostream>

#include <typeinfo>

#include <string>

using namespace std;

int main() {

cout << typeid(5).name() << endl;

cout << typeid(5.4).name() << endl;

cout << typeid(false).name() << endl;

cout << typeid(“a”).name() << endl;

string whatever = “hello123”;

cout << typeid(whatever).name() << endl;

}

C++ vectors (basically arrays that can be resized):

#include <iostream>

#include <vector>

using namespace std;

int main() {

//kind of like a stack and a resizable array

//combined

vector<int> intVector;

intVector.push_back(1); //puts one at back of vector

intVector.pop_back(); //removes last element in vector

intVector.push_back(5); //puts 5 at back of vector

cout << intVector[0] << endl; //5

return 0;

}

The <> used with vectors indicate that they’re generic. That means you can have a vector of different types, like vector<int>, vector<double>, and so on.

C++ tuples:

#include <iostream>

#include <tuple>

#include <string>

//tuple demo

using namespace std;

int main() {

tuple<string, string, string, string> menu;

menu = make_tuple(“eggs”, “toast”, “pancakes”, “waffle”);

string myOrder = get<1>(menu);

cout << myOrder << endl;

return 0;

}

C++ enums:

I’m going to use a car transmission as an example of an enum:

#include <iostream>

//tuple demo

using namespace std;

enum Transmission {

FIRST = 1,

SECOND = 2,

THIRD = 3,

FOURTH = 4,

REVERSE = 5

};

void shiftUp(Transmission &someTransmission) {

int gearInt = someTransmission;

if (gearInt < 4) {

gearInt += 1;

} else if (gearInt == 4) {

cout << “you’re already in the highest gear” << endl;

} else if (gearInt == 5) {

gearInt = 1;

} else {

cout << “gearInt error” << endl;

}

someTransmission = (Transmission)gearInt;

}

int main() {

Transmission myCar = FIRST;

cout << myCar << endl;

shiftUp(myCar);

cout << myCar << endl;

shiftUp(myCar);

cout << myCar << endl;

myCar = REVERSE;

cout << myCar << endl;

shiftUp(myCar);

cout << myCar << endl;

//could’ve also made a shiftDown function

//but didn’t feel like it

//because this example is long enough as it is

return 0;

}

Please note that the & next to the shiftUp() function’s argument means to pass by reference instead of passing by value. This means the original argument will be used and changed, whereas pass by value will create a separate copy and use that, which wouldn’t change the original thing that was being passed into the function. Try it with and without & for pass by reference and you’ll see that the gear won’t shift if it’s not passed as a reference.

← Previous | Next →

C++ Topic List

Main Topic List

Leave a Reply

Your email address will not be published. Required fields are marked *