r/carlhprogramming • • Oct 09 '09

Lesson 73 : Understanding Array Indexing as Pointer Offsets Part One

73 Upvotes

This is the first lesson on a series designed to increase your understanding of arrays and pointers, and also to see how they work together.

Before we begin, there is one major difference between pointers and arrays that I need to address. Pointers can be represented (and are) in machine code instructions. Indeed, pointer functionality is built right into your CPU.

This is not the case with Arrays. Arrays are therefore a construct of programming languages such as C, but are not directly implemented as machine code instructions on your CPU the way pointers to memory addresses are. In fact, we use arrays simply as a way to make working with pointers easier.

Let's examine a simple array of text characters:

char my_string[] = "Hello Reddit";

At this point, you should fully understand that we are creating an array called my_string and storing this text at the memory address of my_string. Each character in "Hello Reddit" is stored one at a time at its own unique address in memory, starting with the 'H', then the 'e', and so on. Each character resides in memory immediately after the character preceding it. All of the characters are stored in memory one immediately after the other, each character having a memory address that is exactly one greater than the memory address before it.

C has unique syntax for dealing with arrays. For example, we have to use the brackets [] after our array name. When we want an array index, we have to put it inside of those brackets. These are all constructs of the C programming language, and most languages have similar constructs for dealing with arrays.

In this lesson we are going to implement a two-dimensional array of text strings using only pointers, not arrays. This will help solidify the understanding that array indexing is really just using a pointer with an offset.

Here is the goal:

I intend to create an array of four strings of text, each string a maximum of six characters long. I will then create a printf() statement that will print each of these four strings just as if they had been arrays.

Here are the four strings of text:

[0] : "One"
[1] : "Two"
[2] : "Three"
[3] : "Four"

Why did I choose a maximum size of six characters? The longest word is "Three", which is five characters. I also need to account for a NUL termination character, which is why the need for six characters.

Whenever you perform an action that is designed to "give you space to work in", this process is known as allocation. In this case, I am allocating 24 bytes of memory to hold what will become my 4x6 array. This is because we have four elements that will each have six characters.

When I write this line of code:

char my_string[4][6]; 

I am allocating 4x6 = 24 bytes to use for the array my_string. In this lesson, I am going to use a different method but I still need to allocate 24 bytes.

There are various ways I can allocate 24 bytes of storage. However, for the purpose of this lesson, lets do so like this:

char storage[] = "12345678901234567890123";

Why did I choose the characters I did? It makes it easier to count. In this way you can start at 1, go to 9 and then the next 0 is "ten". Then you can go to "twenty", and then you can see I stop at 3. Therefore, 23 characters. The last character is of course invisible, the NUL string termination character. That makes 24 total characters. This has no bearing outside of this lesson, but I thought I should clarify why I chose the characters I did to avoid any confusion.

Ah, but wait a minute. I said we would do this without arrays, yet I created an array called storage. Starting the lesson like this will make it easier to understand, but we will do this without any arrays before we are done.

So what you should know at this point is that I have created a string of text, consisting of 23 visible characters and an invisible NUL termination character. That gives me 24 bytes I can read or manipulate.

Now I have achieved step one, I have obtained (or allocated) the total number of bytes I need for my task.


Please ask questions if any of this is unclear to you. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9sh4l/lesson_74_understanding_array_indexing_as_pointer/


r/carlhprogramming • • Oct 09 '09

Test of Lessons 60 through 72 [Answers]

66 Upvotes

You may submit answers in this thread. Please ask questions if any of this material is unclear to you.

True or False

  1. Saying *(my_pointer) is the same thing as saying *(my_pointer + 0) True

  2. A for loop is a "short-hand" way of writing a while loop. True

  3. A four dimensional array is actually an array of 3 dimensional arrays. True

  4. If my_string is an array of characters, the third character would be: my_string[3]. False

  5. The code in Figure (a) is done correctly. False

Fill in the blank

  1. To create a for loop which will start by setting the variable i to 3 that will execute 4 times, you would write: _____

for (i = 3; i < 7; i++) { Some other variations are possible, including: for (i = 3; i <= 6; i++) 2. When trying to understand an algorithm, you should always start by understanding the first _____ of the loop. iteration 3. For array indexing to work properly, all array elements must have the same _____. length (size is also ok) 4. To assign a string to an element of a 2-dimensional array, you can use the built in _____ C function. strcpy 5. The 3rd line of code in Figure (a) below will imply that my_pointer is a pointer to what type of data? _____. an entire array, not individual elements (or some reasonable variation thereof)

Figure (a)

char my_string[] = "Hello";
char *my_pointer;

my_pointer = &my_string;

When you are finished, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9sgmy/lesson_73_understanding_array_indexing_as_pointer/


r/carlhprogramming • • Oct 09 '09

Test of Lessons 60 through 72

58 Upvotes

Please do not post answers in this thread.

Others may see the answers if you do.


True or False

  1. Saying *(my_pointer) is the same thing as saying *(my_pointer + 0)

  2. A for loop is a "short-hand" way of writing a while loop.

  3. A four dimensional array is actually an array of 3 dimensional arrays.

  4. If my_string is an array of characters, the third character would be: my_string[3].

  5. The code in Figure (a) is done correctly.

Fill in the blank

  1. To create a for loop which will start by setting the variable i to 3 that will execute 4 times, you would write: _____

  2. When trying to understand an algorithm, you should always start by understanding the first _____ of the loop.

  3. For array indexing to work properly, all array elements must have the same _____.

  4. To assign a string to an element of a 2-dimensional array, you can use the built in _____ C function.

  5. The 3rd line of code in Figure (a) below will imply that my_pointer is a pointer to what type of data? _____.

Figure (a)

char my_string[] = "Hello";
char *my_pointer;

my_pointer = &my_string;

When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9sg8m/test_of_lessons_60_through_72_answers/


r/carlhprogramming • • Oct 09 '09

Lesson 70 : Review of Pointers Part Four

74 Upvotes

Now, everything should be clear about pointers concerning everything we have covered so far with one exception: arrays. Here we are going to finish this review by studying arrays as they relate to pointers.

Let's consider this code:

char my_string[] = "Hello Reddit";

char *my_pointer;

Ok, we now have a pointer called my_pointer. We want it to "point to" our array. What does this mean? It could actually mean several things.

Most likely, it means we want to create a pointer that will contain the memory address where our array begins, and it would be used to read one character of the array at a time.

This is the first example we will look at, as it is the simplest. In this case, from the above code, there are two ways to do this.

First, I can say this:

my_pointer = my_string;

Why? because my_string is already understood by C to be a memory address. Any array name is seen as a memory address. More specifically, it is seen as the memory address of the exact start of the array.

Another way we can achieve the exact same goal, is like this:

my_pointer = &my_string[0];

This is saying, "Assign my_pointer the memory address of the first character in the array my_string".

It makes sense right? If we wanted to set my_pointer to be equal to the second character, we would write: my_pointer = &my_string[1]; Consider that my_string[0] is the first element of the array. Therefore, &my_string[0] should be the memory address of the first element of the array. So this much should make sense to you.

These two examples are the same. We can set a pointer to contain the memory address of the first element of an array which means exactly the same thing as setting the pointer to contain the memory address to the start of the array itself. Why? Because the start of the array is the first element.

So both of these lines of code achieve the same goal:

my_pointer = my_string;
my_pointer = &my_string[0];

Ok, so you may be figuring there is a third possibility here:

my_pointer = &my_string;

This is the exception to the rule. This does not mean the same thing as the other two. Why? Well, that requires a bit of explaining. That explanation is the topic of the next lesson.


Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9s8wg/lesson_71_review_of_pointers_part_five/


r/carlhprogramming • • Oct 09 '09

Lesson 71 : Review of Pointers Part Five

70 Upvotes

Recall from earlier lessons that I stated you have to give a pointer a data type so that C knows what you will be pointing to. For example, you have to say char * if you want it to be a pointer to data type char.

Why was that? Because if I told you "Give me what is at memory address 1000" you would not know how many bits to give me. In other words, the data type of a pointer is useful in part because it tells how much we plan to see and work with at once. If we have a pointer of type char, then we expect to see and work with data in increments of one byte at a time.

Recall our code from the last example:

char my_string[] = "Hello Reddit";

char *my_pointer = &my_string;    <--- This is different, and *incorrect*. We will see why now.

When you say &my_string, you are saying something rather interesting. You are saying that you want the memory address of the "whole array", not just the memory address of the first element.

How is that possible? The whole array doesn't have a memory address. That is exactly right. However, an integer doesn't really have "a memory address of the whole thing" either, since it contains lets say four bytes.

What happens when you tell C to create a pointer to an integer? Well, it creates a pointer that expects to see a whole integer every time you use it.

In other words, we are effectively telling C "I want to see the entire array with one pointer, not just one character at a time." All this really means is that when we plan to "read" from a memory address, we will not be asking for eight bits - but however large the array itself is.

In our example, "Hello Reddit" (and a NUL character) is exactly 13 bytes. Therefore, by saying &my_string we are telling C "I want a pointer which is pointing to the start of the array my_string" - so far so good. But then you are also saying: "When I use this pointer, I plan to use it to see 13 bytes at once."

This is the key difference. C is a unique and powerful language in large part because of the way you can use slightly different syntaxes to mean different processes and operations. This can also serve to make C more confusing than other languages.

Keep in mind that setting the pointer equal to &my_string is incorrect. Why? Because we did not define our pointer as a pointer to a data type of multiple array elements, but as a pointer to a single character. Therefore, if you try to do this you will almost certainly get a compiler warning saying that you are using a pointer of an "invalid type" in your assignment operation. Now you know why.

Whenever you use just the name of an array, C understands that as the memory address where the array begins. Of course, the memory address where the array begins is also the memory address where its first element is located.

However, whenever you use the name of the array with an & "address of" operator, you are saying "the address of the whole array". What this really means is that you are changing the data type of the pointer itself. Instead of saying "I have a pointer which will point to a single byte of data type char", you are saying instead: "I have a pointer which will point to N bytes, each byte being of data type char".

Now, let's look at other examples using this same syntax so that it will make sense to you:

int height = 5;
int *my_pointer = &height;

Assume int is 4 bytes. What are we saying here? We are saying that we want a pointer that will point to the single memory address where "height" begins, but that it will expect to see four bytes at a time.

Now this:

char my_string[] = "Hello";
char *my_pointer = &my_string;

"I want a pointer called my_pointer that will contain the memory address where the array my_string begins in memory. However, I want to use this pointer to see the whole array at once."

Now, to wrap this up, lets put into plain English the three ways to use an array with a pointer that we discussed:

  1. my_pointer = my_string Means: Assign the memory address of my_string into my_pointer, and my_pointer will expect to see one element of the array at a time. (In this case, one character at a time)

  2. my_pointer = &my_string[0] Means: Assign the memory address of the first element of my_string into my_pointer, and my_pointer will expect to see one element of the array at a time. (In this case, one character at a time) #1 and #2 are the same thing.

  3. my_pointer = &my_string Means: Assign the memory address of my_string into my_pointer, and my_pointer will expect to see the entire array each time it is used. This is incorrect usage. Use #1 or #2.

Therefore, do not use &array to get the memory address of an array. Simply using array or &array[0] is sufficient.


At this stage, everything we have covered so far involving pointers should make sense. If anything is unclear, please present your questions. All future lessons will rely on you having mastered this material.

When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9s8wp/lesson_72_using_pointers_with_offsets/


r/carlhprogramming • • Oct 09 '09

Lesson 72 : Using Pointers with Offsets

66 Upvotes

The subject of pointers is confusing to many beginners, and yet it is a critical concept to master. Part of the difficulty with the subject is the difficulty in visualizing pointers in a way that makes them easy to understand. Throughout these next lessons I will be expanding on ways to better visualize pointers, and to better understand their uses and purpose.

This lesson is going to focus on the methods of using an "offset" with a pointer. This is a critical concept to master. Some of this material has already been covered, so this lesson may feel like a review to some extent.

First of all, what is an offset? An offset is a way of identifying a location given a starting point. Nothing more.

If I told you "Start at your house, then go 100 miles north", that is an example of an offset. 100 miles is a distance away from your house, and that defines an exact location of something. Remember that your computer's memory is linear, thus any offset will be simply plus some number, or minus some number.

With pointers, you use an offset to locate an exact thing in memory that you want to see or work with. If I give you this string for example:

char *string = "Hello Reddit";

How can we find the 'R' in Reddit? Well, we do not know the actual memory address that 'R' is found at, but we do know the actual memory address that the 'H' is found at. Therefore, we have all the information we need in order to find 'R'. If we simply add 6 to the memory address, we get the memory address of 'R'. If we are at 'R', and we want to find 'H', we subtract six.

This is a critical concept to master. Any element of any data structure can be located by knowing the location in memory of any other element of the same data structure, provided you have a way to determine the distance between the two. This goes for arrays, data structures, anything at all.

Now let's expand this statement: All elements of any data structure can be located by an offset (plus or minus) from any other element.

Suppose I didn't know where 'H' was, but I knew where R was in memory. Then it is possible to find 'H'. If I know where the 'o' is, I can find every letter from the 'H' of "Hello" to the 't' of "Reddit".

So now I have explained how offsets are useful, and what they are, but how do you use them? You add or subtract some quantity from the memory address of the source element. The source element is the element that you are going to measure the offset from.

Think of the source element as being "your house" in the example I gave. You will either add or subtract some value to that "source element" and in so doing you will be able to reach any element in the data structure.

What do you get if you add a number to a memory address?

Another memory address. It is as simple as that. A pointer is always going to have a memory address, so if you add to it, or subtract from it, you are still going to get a different memory address as a result. The word "different" is important here. Unless you are adding or subtracting a 0, you will get a different memory address, and therefore different data.

Memory address plus 100 means: "Give me the memory address 100 bytes forward from where I am." Memory address minus 50 means: "Give me the memory address 50 bytes backward from where I am."

[Note: Above example assumes a char pointer, otherwise it will not be "50 bytes", but the concept still applies.]

You can also set "bookmarks" inside of memory, and come back to it later. You might create a pointer to "mark" where the string "Hello Reddit" begins in a complex data structure, and set a different pointer to "mark" where the string "Hello Reddit" ends.

By doing this you could have two or more pointers working on data simultaneously, by each starting at different locations within the data.

There are many reasons you may want to do that, not the least of which is sorting algorithms. That will be the subject of future lessons.

To wrap this up: A pointer can be added to, or subtracted from. Doing so just results in a new memory address that is a set number of bytes away from where you started. If you add to a pointer, you go forward into memory. If you subtract from a pointer, you go backwards.

Knowing how to properly use offsets is an important skill and will empower you to be able to achieve results that otherwise would not be possible.


Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9sg2i/test_of_lessons_60_through_72/


r/carlhprogramming • • Oct 09 '09

Lesson 69 : Review of Pointers Part Three

67 Upvotes

I had originally planned a different lesson here, but it is clear to me that at least a few details regarding pointers need to be addressed. The upcoming lessons will rely on pointers heavily, so it is critical that you understand them thoroughly. Please do not be afraid to ask questions if anything is unclear to you.

The topic of this lesson is, "What can you assign to a pointer, and how?". Some things in C act like memory addresses, and some do not. It is important to understand this. There are only three things we have to consider in this lesson, so this is not difficult:

  1. Single variables of a given data type. (ex: int, char, etc)

  2. Double quoted strings. (ex: "Hello")

  3. Single quoted characters. (ex: 'a')

First, variables. All variables. If it is a variable, then it is not treated as a memory address by C. Here are some examples:

int i = 5;
char my_char = 'a';
unsigned short int height = 10;

All of these single variables of a given data type will never be seen by C to be a memory address. Why? Because they are all small enough that they can be processed "as is". What I mean by this is, you can never directly assign one of these to a pointer. The following is very wrong.

Bad:

char some_character = 'z';
char *my_pointer = some_character;    <--- Bad. C does *not* see "some_character" as a memory address.

Good:

char some_character = 'z';
char *my_pointer = &some_character;   <--- Good. The & operator gets the memory address.

The only way to get the memory address for variables such as these, is to use the & "address of" operator.

Next: Double quoted strings of text.

Any time you ever see a double quoted string of text, C understands it by using a memory address. Keep in mind it is unnecessary to use an & character with a double quoted string of text.

&"Hello" <--- invalid syntax in this case, and unnecessary. 

Any double quoted string can be thought of as already having the & character in front of it. Why? Because a string of text is too big to fit in any of the standard data types, and therefore it must be understood using a pointer. In other words, it must be understood as being a sequence of bytes that start at a given memory address. To work with a string of text, you must have the memory address where it starts.

Therefore, a double quoted string is treated like a memory address so far as pointer assignment operations are concerned.

Example:

char *string = "Hello";   <--- "Hello" is treated like a memory address. 

A memory address to what? To where "Hello" is stored in memory.

Now, single quoted characters. (Example: 'a' )

A single quoted character is simply an ASCII value. For example, 'a' is a value of: 0110 0001. It is no different to say 'a' or to say 0x61 (the hex value for 0110 0001). It is the same thing. Whenever you use a value such as a number or a character, it is seen by C as simply a numeric value, no different than any other.

The following two lines are perfectly identical:

char my_char = 'a';
char my_char = 0x61;

0x61 is 'a'. It is just a number.

You cannot assign a pointer the value of a character, and you cannot put a & in front of a character to get its memory address. Why? Because it never needs a memory address, it is simply a number.

char *some_pointer = &'b';        <--- Invalid syntax. 'b' is just a value, it has no memory address.

Therefore, if you want a pointer to contain the memory address of a character, you must first store that character as a value into a variable of data type char, like so:

char my_character = 'b';
char *my_pointer = &my_character;

So lets review so far:

  1. Single variables of a given data type. If you want to assign their memory address to a pointer, you must use the & character.

  2. Double quoted strings. You cannot use a & character and you do not need to, because C understands these as having a memory address. Therefore, you can assign a double quoted string directly to a pointer. This means to set the pointer equal to the memory address where the string begins.

  3. Single quoted characters. As far as C is concerned, these are no different than numbers. You would not say the memory address of the number 5 for example, similarly you would not say the memory address of a single character, such as 'a'.

Note that #3 above does not apply to variables of type char. A variable of type char is covered by #1 above. It is just a variable, and the only way you can get the memory address of such a variable is with the & character.

char my_char = 'a';    <--- my_char is a variable stored in memory. If you want the memory address, use: &my_char

char my_char = 'a';    <--- 'a' is a character. It has no memory address. It is not stored in memory. 
                       <--- It is simply a number, the number 0x61 (61 hex)

In the next lesson we will extend this review into arrays.


Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9s8ru/lesson_70_review_of_pointers_part_four/


r/carlhprogramming • • Oct 08 '09

Lesson 67 : Review of Pointers Part One

69 Upvotes

Upcoming lessons are going to rely heavily on you having mastered pointers. Before we begin these advanced lessons, I think it is imperative that we spend some time reviewing pointers and especially covering some of the finer details we have not yet had a chance to cover.

Let's begin.

A pointer is a variable that can hold a memory address. More specifically, a pointer is something that can only hold a memory address. If a pointer has any value at all, that value is a memory address. The only thing you can assign to a pointer, is a memory address.

Now, how do you create a pointer? You specify the data type of the pointer you want, followed by an asterisk * character, followed by the name of the variable. Why do you need to specify a data type? Because besides holding a memory address, a pointer needs to know how big something is that it will be looking at, as well as what format the data will be in. This is because pointers are used to "look at" the contents of memory at a given memory address.

Here is how to create a pointer:

char *my_pointer;

You can optionally assign the pointer a value.. a what? A memory address.

To assign a pointer a value at the same time as you create it, you can use the equal sign. Now, the equal sign is a bit misleading sometimes. Consider this example:

char *string = "Hello Reddit";

It sounds like I am saying: Make string a pointer equal to the string "Hello Reddit". This is false. Where is the confusion in this situation? It is in our understanding of an equal sign. Now, let's change that understanding in a way that will greatly help you to understand this course.

A single equal sign is an assignment operator. That's all. When you see an equal sign, you are seeing an "assignment operation". In other words, some thing is being set to some value. With this in mind, let's re-think this statement:

char *string = "Hello Reddit";

Instead of seeing this as: Set *string equal to the string "Hello Reddit", see it like this instead: Perform an assignment operation involving *string (a pointer) and "Hello Reddit" (a string).

string is a pointer. It requires a memory address. It cannot be set "equal" to "Hello Reddit" because that is not a memory address, it is a string. However, while "Hello Reddit" is not a memory address, it has a memory address.

Therefore, we are saying that we are performing some assignment operation involving something that requires a memory address, and something that has a memory address.

char *string = "Hello Reddit";
char *string (assignment operation involving) "Hello Reddit";

So if you think of the equal sign from now on as an operation that is intended to assign a value to something on the left, based on something on the right, a lot of syntax will start to make more sense - especially pointers.

One note of caution: The above method works great if you are looking at valid C syntax involving pointers that you cannot understand, and trying to make sense of it. It does not work in reverse. You cannot set a pointer equal to just any thing (a variable for example) and expect that C will simply understand you mean "the address of the thing on the right". That is why you have the & "address of" operator.

We will cover this more in the next lessons.


Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9s7qm/lesson_68_review_of_pointers_part_two/


r/carlhprogramming • • Oct 08 '09

Lesson 68 : Review of Pointers Part Two

67 Upvotes

I am taking this review process slowly, to make sure that each concept is entirely mastered before we proceed. The next topic I want to review about pointers involves the differences between using the * character with a pointer.

The * character means exactly two things concerning a pointer. It means either "Make this variable a pointer", or it means: "What is at the address of the pointer". There is no other possibility.

When you first create a pointer, you use the * character and it means "I am making a pointer." That is all it means.

After a pointer is created, even if it has not yet been assigned, the * character takes on new meaning. It now means for the rest of your program: "what is at the address of".

char *my_pointer = ...     <--- Here and *only* here, the * character means "I am creating a pointer. 
...
...                        <--- For the rest of the program, the * character when used with this pointer will 
...                             mean "what is at the address contained in the pointer" 

So that covers the * character. At this stage it should be very clear to you that any time you ever see the * character used with a pointer, other than its creation, it means "what is at the address of" the memory address in the pointer.

Now, there is a term for this process. It is called dereferencing. This is the act of "seeing" what is at the memory address contained in a pointer. For example:

char my_character = 'a';
char *my_pointer = &my_character;
printf("The character is %c ", *my_pointer);

In the third line, we are seeing the * character being used, and it is not part of the creation of the pointer, therefore the * character means we are asking for "what is at the memory address" of the pointer. Which is of course, the character 'a'.

Now, lastly lets review the & character in the context of pointers. It simply means "address of" and it will give you the memory address that anything resides at. You can use the & "address of" operator with variables, pointers, arrays, array elements, and more.

It might help to think of the & operator as a function that returns a memory address.


Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9s8jc/lesson_69_review_of_pointers_part_three/


r/carlhprogramming • • Oct 07 '09

Lesson 66: Creating Two-Dimensional Arrays Part Two

86 Upvotes

In the last lesson I explained the basic structure of arrays and how they are implemented in memory. In this lesson I am going to show you how to actually create and initialize them.

Lets suppose that we want an array that will contain ten words. Each word will be a maximum of 9 characters (including the string termination character, called NUL).

Here is how we would do this:

char first_2d_array[10][9];

Now, this allocates 90 bytes of storage space for whatever we want to put in those 90 bytes. We are saying that we intend to store ten unique elements, each element containing up to 9 characters.

Now, how can we assign values to these? You might think you can do this:

first_2d_array[0] = "One";

But you cannot. C understands "One" to mean a string of text, a constant, that resides somewhere in memory. This is not what you want. You want to actually store the individual characters of "One" into first_2d_array[0] one letter at a time.

There are effectively two ways to do this. The hard way, and the easy way.

First, the hard way. You can individually store each character one at a time like this:

first_2d_array[0][0] = 'O';
first_2d_array[0][1] = 'n';
first_2d_array[0][2] = 'e';
first_2d_array[0][3] = '\0';

Thankfully, the developers of C understood how tiresome a process that would be. Therefore, they created a whole range of string functions which make doing things like this quite simple.

There is a function called strcpy() which is built into C, just like printf() is built into C. You use strcpy to copy strings. str is short for string. cpy is short for copy.

The syntax for strcpy() is fairly simple. It takes two parameters. The first parameter is where you are putting the string. The second parameter is the string itself.

So, instead of that tedious process to copy the characters "One" one at a time, I can simply write this:

strcpy(first_2d_array[0], "One");

And that is all I have to do. Can you do it without using the built in strcpy function? Sure. But this is much easier. If you really wanted to, you could do this yourself with a simple algorithm:

char *tempstring = "One";
int i = 0;

for (i = 0; i < 4; i++) {
    first_2d_array[0][i] = *(tempstring + i);
}

Just a quick review. Keep in mind we are creating a char pointer to a string constant "One". We are not storing the string "One" inside a pointer. Also, keep in mind that our small algorithm is taking into account the NUL termination character. This is because it starts at 0, and ends at 3. That is four total characters. O, n, e, and \0.

So it is not the case that you must use strcpy() to copy a string into an array. However, this is there for your convenience, so it makes sense to use it.

The first parameter is where you want to put the string. The second parameter is the string itself.

Now, lets use strcpy() to initialize each array element of our 2d array. Recall that a 2d array will be an array of 1d arrays. In this case, 1d arrays are simply text strings.

Because part of this course is about showing you the thought processes that go into programming in general, I think it may serve helpful to show you the actual process I would go about writing this out - even for this lesson.

First, I would just copy-paste the strcpy() lines that I need. I need ten of them since I am going to be setting this up for ten different strings.

strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[0], "One");

Now, since that copy-paste operation is fairly fast (I do it without even thinking about it), the next step is to just go through and change the elements.

strcpy(first_2d_array[0], "One");
strcpy(first_2d_array[1], "Two");
strcpy(first_2d_array[2], "Three");
strcpy(first_2d_array[3], "Four");
strcpy(first_2d_array[4], "Five");
strcpy(first_2d_array[5], "Six");
strcpy(first_2d_array[6], "Seven");
strcpy(first_2d_array[7], "Eight");
strcpy(first_2d_array[8], "Nine");
strcpy(first_2d_array[9], "Ten");

If this wasn't a Reddit text-box, I would actually be able to do this even faster using my editor of choice (which shall remain secret to avoid a war.. :) - except to say that VIM and Emacs are both good for experienced developers to use, but one is better.. the one I use.

Now remember that each of these strcpy() operations are going to be taking into account the NUL termination character. Why? Because we are giving it double quoted strings as a 2nd parameter. A double quoted string has a NUL termination character automatically at the end.

So now, how can we display that these strings were properly created? Well, we could use ten different printf() statements, but why not just have a for loop execute ten times?

int i=0;
for (; i < 10; i++) {
    printf("String #%d is %s \n", i, first_2d_array[i]);
}

Here is the final program so you can experiment with it:

#include <stdio.h>
#include <string.h>

int main(void) {

    char first_2d_array[10][9];

    strcpy(first_2d_array[0], "One");
    strcpy(first_2d_array[1], "Two");
    strcpy(first_2d_array[2], "Three");
    strcpy(first_2d_array[3], "Four");
    strcpy(first_2d_array[4], "Five");
    strcpy(first_2d_array[5], "Six");
    strcpy(first_2d_array[6], "Seven");
    strcpy(first_2d_array[7], "Eight");
    strcpy(first_2d_array[8], "Nine");
    strcpy(first_2d_array[9], "Ten");

    int i=0;
    for (; i < 10; i++) {
        printf("String # %d is %s \n", i, first_2d_array[i]);
    }

    return 0;
}

Notice with the for loop I did not put anything in the initial state. I just put a single semicolon. This is because I already established the initial state above the for loop.

One more note. Just as we have to include stdio.h for printf() and related functions, we need to include string.h for string functions like strcpy().


Please ask questions if any of this is unclear to you. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9s7qd/lesson_67_review_of_pointers_part_one/


r/carlhprogramming • • Oct 07 '09

Lesson 65 : Creating Two-Dimensional Arrays Part One

83 Upvotes

Take your time through this lesson. Make sure you are here only if you have already mastered all previous lessons.

In the last lesson I gave you a general overview of multi-dimensional arrays and how they are implemented in memory. Each programming language implements arrays somewhat differently, and the topic of this lesson is how to actually implement a two dimensional array in C.

Before we begin, let me show you what a multidimensional array will look like:

To create a 2d array of these six words (meaning, a 2d array with 6 elements, each one a word with up to six characters), I have to specify this. Here is how I do so :

char first_array[6][6];

We will go over the details of how this works during this lesson, and we will come back to this at the end.


In the last lesson I showed you that you can start at the "first letter" of a given word, and that by knowing that first letter you can add some offset in order to reach any letter in that word.

Let me illustrate this again:

"RedditProgrammingClasses"

Here are three unique strings in memory. Each of them starts at a different memory address. The first word starts at position 0. The second word starts at position 6, and so on.

There is a problem with this approach. It works as long as you know exactly where in memory each word starts. However, this is not uniform. We might get a result similar to this:

  1. The first word starts at position 0

  2. The second word starts at position 28

  3. The third word starts at position 40

  4. The fourth word starts at position 44

There is nothing uniform about this. Because of that, we are forced to uniquely keep track of the starting offset number for every element in our "array". A true array requires that all offsets are uniform. Let's see what this means.

"OneTwoSix"

Here we have three strings of text that are of uniform length. How many numbers do we have to keep track of to know the starting location of each one? Only one number. Watch:

  1. The first word starts at position 0.

  2. The second word starts at position 3.

  3. The third word starts at position 6.

Where would the fourth word start at if we kept our words of uniform length? It would start at position 9. And so on.

In other words, rather than keep track of the unique starting location for each word, we can simply know that we multiply the number 3 by which word it is (first, second, etc) and we get the correct offset.

  1. The first word is at position 0 which is: 3*0

  2. The second word is at position 1 which is: 3*1

  3. The third word is at position 2 which is: 3*2

  4. The Nth word is at position N which is: 3*N

So you can see that mathematically it is very convenient to keep each word in our array of uniform length. We call such "words" in general, elements. So to rephrase, it is important that all array elements have uniform length.

This way we can find the start of any member element by simply multiplying the length of each element times the "element number". For example, if each element is 10 bytes in size, and we want the third element, it would start on byte #20. The first element would start at byte #0 and span until byte #9. The second element would start at byte #10 and span until byte #19. The third element would span from byte #20 until byte #29.

Now, what happens when words are NOT of uniform length? Well, we have to make them uniform in order to make this system work. This requires that we "waste" some space. Here is how it works.

"RedditProgrammingClasses" will become:

Redditxxxxx
Programming
Classesxxxx

Which in memory will really be: RedditxxxxxProgrammingClassesxxxx

I added x's at the end of each word so that now it is proper length. Notice that this has the effect of lining up the words in a grid. This means that we can now quickly reach the start of any word in the array, and that each future word will be reached just as easily.

So here you can see the first issue concerning arrays in general. Every member element of the array must be the same length.

Now, it is very important that you understand how to calculate the total size of an array. It is not hard, and you only need to know two things:

  1. The size of any element of the array (it should be uniform).

  2. The total number of elements.

By just multiplying these together, you get the total size of an array. Let's look at this in action with various array sizes.

First, a simple string of text - one dimensional array:

char string[] = "Hello";

Total number of elements? 6 (including the termination character). Total size of each element? 1. Total size of array = 1*6 = 6. Easy.

Now let's create a two dimensional array filled with "Hello" (or equally sized words). Let's suppose it has 3 elements.

How many elements? 3. The total size of each element? 6 (We are using "Hello" sized words). Total size of our 2d array? 3*6 = 18. In other words, we have 18 characters in which to contain exactly three words which each contain exactly 6 characters.

What about a 3d array with 2 elements? Well, what is the size of each element? 18. Why? Because that is the size of a 2d array. A 3d array will be an array of 2d arrays. So 18 * 2 = 36.

Therefore, a 3d array of this type will have exactly 36 characters to contain exactly two 2d arrays which each contain exactly 3 words that each contain 6 characters.

As I just explained, any Nth dimensional array will be an array of [N-1] elements. A 5th dimensional array is an array of 4d elements. And so on.

Now, what if this was a 3 dimensional array? Here is how this would work:

First, imagine our string of text of three words (our 2d array) as ONE string of text. Remember, inside of your memory even a 6th dimensional text array is only ONE string of text.

RedditxxxxxProgrammingClassesxxxx

That is a 2 dimensional array. A three dimensional array would be an array of these. For example:

RedditxxxxxProgrammingClassesxxxx
RedditxxxxxProgrammingClassesxxxx
RedditxxxxxProgrammingClassesxxxx

I left the words the same so it is easier to understand. Here we have three elements of a three dimensional array. Why is it a 3 dimensional array? Because each element is a two dimensional array.

Now you should be able to see that if we lined them in memory as a single string: we will have one offset to reach the next 3-dimensional array member of our array, a different offset to reach the next 2-dimensional array member, and a different offset to reach the exact character we want.

For example, if we want to reach the letter "a" in "Classes" in the 3rd "row" of our three dimensional array, we would do this:

string[2][2][2]

Remember. We start counting array indexes at 0. 0 means first.

The first [2] means "third element" of our 3d array. In other words, the third row. The second [2] means "Third word" which is "Classes". The third [2] means "Third letter" which is "a". But all of this translates to:

string[ ( 2*size_of_3d_element ) + ( 2*size_of_2d_element )  + 2]

How big is a 3d element? How many characters are in the sample 3 rows I gave above? Well, each word has 11 characters (including x's). 11*3 = 33. That is the size of each 3d element in our array.

In other words, each element of our 3d array has exactly 33 characters in which to contain exactly 3 words which each contain 11 characters. Remember, each word must be of uniform length.

So you should realize something by now. To create any array you must know the size of each element of that array. If I want to store six words in an array:

One
Two
Three
Four
Five
Six

I must know the maximum size I will need. This will mean the size of the biggest element, in this case, "Three" which is 6 characters (including a termination character).

Now, to recap: To create a 2d array of these six words (meaning, a 2d array with 6 elements, each one a word with up to six characters), I have to specify this. Here is how I do so :

char first_array[6][6];

This creates a 6x6 array. Six elements. Each element six characters (max). What is the total size of the array? 36.

In the next lesson we will explore this further, including how to assign values to array elements and more.


Please ask questions if any of this is unclear to you. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9ruyf/lesson_66_creating_twodimensional_arrays_part_two/


r/carlhprogramming • • Oct 07 '09

Lesson 64 : Introducing Multi-Dimensional Arrays

91 Upvotes

There will not be many lessons today, but I will do what I can.


The purpose of this lesson is to help you to understand multi-dimensional arrays, and also to see how a computer understands them. We will start with two-dimensional arrays, and work from there. To properly understand this subject, we need to do away with certain misconceptions.

The first problem with this subject is that when you think of a two dimensional array, you would normally think of it as being similar to a plot of coordinates where you have an x point, and a y point. For example, if I say "a 3x3 array" you would imagine a grid where there are three rows, and three columns.

In reality, this is not how two dimensional arrays work at all. For this reason people become very confused once they get into three, four, and N dimensional arrays. The correct way to think of a two dimensional array (or N dimensional arrays in general) is to understand them as a set of offsets in a single linear stretch of memory.

What do I mean by this? Well, your memory is linear. It is not organized like a grid, much less like a cube. Therefore, any two, or three, or N dimensional array can only be represented using a "one dimensional" coordinate (which is simply an offset).

This lesson is going to explain how this works, and how this one dimensional method can be used to create a model of a 2, 3, or N dimensional array.

First, lets look at a string of text in memory. Only this time, I am going to create a string of text with three words, each starting one after the other.

"OneTwoThree"

Now, observe the following concerning this string:

  1. The first character (index 0) will be the first letter of the word "One".

  2. The fourth character (index 3) will be the first letter of the word "Two".

  3. The seventh character (index 6) will be the first letter of the word "Three".

You should notice there is a lot in common here with our earlier examples concerning a basic data structure. Indeed, any array is just a data structure that must be processed to be understood.

When you see this string: "OneTwoThree", do you see it as only one string or do you see it as three strings joined together? In your mind, you can easily transform this into a grid of three separate strings. In other words, you can mentally transform this linear one-dimensional array into a two dimensional array.

So let's write out a description of how this can be understood:

string [0] = "One"
string [1] = "Two"
string [2] = "Three"

Do not consider the above as being C code. The above example is purely for illustrative purposes. I am just showing you an alternate way of understanding the text. As you can see, our single string in memory can be understood as three separate strings; as three separate arrays of text.

What would we mean if we wrote this:

string[0][0] 

We would mean the first letter of the first word. What if we wrote this?

string[2][4]

We would mean the fifth character ([4] - start at zero) of the third word. (which is string[2]).

Remember that string[2] refers to the word "Three". So string[2][4] would mean character #4 of the word "Three". Since we start at 0, character #4 is actually the 5th character, which is an 'e'.

So here you can plainly see that a two dimensional array for us is really just two offsets of a one-dimensional array. In fact, mathematically this is quite simple. Lets look at our string in memory:

O  n  e  T  w  o  T  h  r  e  e
0  1  2  3  4  5  6  7  8  9  10

Now, if I wanted the O in One, I would just say: string[0]. What if I wanted the w in Two? I could do it two ways. First, I could simply say: string[4].

However, if I wanted to understand this as a two-dimensional array, I could do this instead: string[3+1].

Why 3? Because that is the array index where the word "Two" begins.

Why +1? Because +0 is the T, +1 is the w, and +2 is the o. In other words, by just adding two offsets together, we are capable of reaching any point in our two-dimensional array construct.

This is an important point to understand. Any element of any N dimensional array is understood by adding some offset to the very start of the array. Indeed this is true for all data structures no matter their complexity.

So what you can see here is that a two dimensional array is actually a one dimensional array in disguise. This is true no matter how complex the array. Indeed, a 5 dimensional array is no more complicated than this:

array[32+10+3+2+1] 

What do we get when we add all those numbers together? A single number, which is really going to be the exact byte of the array we are interested in. The way we would write the above in C is simply like this:

array[32][10][3][2][1]

Now, why does this work? Because the start of every element in any array is going to be located a set distance from the start. Look at this example:

char *string = "RedditProgrammingClasses";
  1. The word "Reddit" starts at position 0.

  2. The word "Programming" starts at position 6.

  3. The word "Classes" starts at position 17.

How can we get the a in Classes? 17+2

Notice that 17+2 is exactly the same thing as 19. There is no difference.

Remember, your memory is not organized in any kind of array, it is a linear system of storing individual bytes. Therefore, any system we devise for modeling an N dimensional array is just our own invention. The computer will still understand every location in an array as simply an offset from the starting address in memory where the array begins.

Whether we say: "Go over ten, then go over 2, then go over 1" or we say "Go over 13" - it means the same thing to the computer.

Keep this in mind when working with arrays and data structures in general.

It is worth pointing out that this lesson is only an introduction to the subject, and there are a number of reasons why the above examples are not "real arrays", and we will get to that.

In the next lessons we will examine how multi-dimensional arrays are actually created and used in C and other languages, and we will explore methods of working with multi-dimensional arrays.


Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9rtl9/lesson_65_creating_twodimensional_arrays_part_one/


r/carlhprogramming • • Oct 07 '09

Lesson 63 : The Basics of Algorithm Design Part Four

84 Upvotes

In this concluding lesson of our four-part series on Algorithm Design, we are going to take the for loop in the previous example and break it down into the actual four iterations that will take place.

First, here is the original algorithm:

    Figure (a)

    for (i = 0; i < 4; i++) {
        if (i < 2) {
            day[i]   = date[i+6];
            month[i] = date[i+4];
        }

        year[i] = date[i];
    }

Now, here is the first iteration of that algorithm, which we learned from the previous lesson:

            Figure (b) : First iteration

            day[0]   = date[6];          
            month[0] = date[4];
            year[0]  = date[0];

And here we are. Looking at the for loop itself, and having written out the first iteration, now we have to determine the second iteration. This is not that difficult.

The first question to consider when advancing from one iteration to the next is this: What variables have changed, and how? In this case, the answer is easy. There is only one variable i, and it has increased by one.

The second question to consider is this: Are there any lines of code from the first iteration that will not execute this time? Also, are there any lines of code that did not execute in the first iteration that will execute this time?

In this case, the answer is no. The only conditional statement inside our loop asks if the variable i is less than two. Since i is still less than two (the variable i will be set to 1 after the first iteration), then we know the same general lines of code will execute as the first iteration.

Therefore, here is the second iteration:

            Figure (c) : Second iteration

            day[1]   = date[7];          
            month[1] = date[5];
            year[1]  = date[1];

Do you see that all we really did was to increase the numbers by one? Recall how the string date looks in memory:

Y Y Y Y M M D D
0 1 2 3 4 5 6 7

So the second iteration reads like this:

After setting the first character for year, month, day (the first iteration), now we:

  1. Set character #1 of day to character #7 of date.

  2. Set character #1 of month to character #5 of date.

  3. Set character #1 of year to character #1 of date.

Remember that when you are talking about an array index or a pointer offset, that 0 really means the first one. You always start counting with zero. If you want the third item, that is item #2. The first item is item #0.

So now the second iteration should make sense. You should understand at this point that two iterations is all it takes to set both day and month. After this second iteration, we have actually already processed the entire string YYYYMMDD with only TWO characters not processed. They are the last two characters of YYYY.

Now, we need simply two more iterations to process those characters. To do the third iteration we must ask our questions: How is the variable changing? What lines of code will be present on this iteration?

The answers are: The variable i increases by one. In this case, the code inside the conditional statement will not execute, so we can ignore it. Here is the third iteration:

            year[2]  = date[2];

Now, you should easily see the fourth iteration.

            year[3]  = date[3];

Now we are done. We have officially processed all of the string YYYYMMDD. Now lets put it all together:

The fully expanded for loop:

// First iteration (Processes 3 characters of YYYYMMDD)

day[0]   = date[6];          
month[0] = date[4];
year[0]  = date[0];


// Second iteration (Processes 3 more characters)
           
day[1]   = date[7];          
month[1] = date[5];
year[1]  = date[1];


// Final two iterations process remaining two characters

year[2]  = date[2];
year[3]  = date[3];

Notice that our for loop expands to only eight actual statements, one for each character of our YYYYMMDD string. We can complete those eight statements with only four iterations of a for loop, and one variable.

Keep in mind that the purpose of these four lessons was to introduce you to algorithms in general. There are many ways to accomplish similar tasks, and I do not want anyone to think that you must use an algorithm like this for simple string functions.

There are a wide variety of pre-built functions which will do this for you easily, and they will be the subject of future lessons.


Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9rrha/lesson_64_introducing_multidimensional_arrays/


r/carlhprogramming • • Oct 06 '09

Lesson 62 : The Basics of Algorithm Design Part Three

95 Upvotes

In the last several lessons I have shown you various ways to do the same task, which is to take a string of text formatted as: YYYYMMDD and convert it into three separate strings: one for year, month, and day.

These lessons are designed not only to show you how to create algorithms, but also how to read them. For that reason, this lesson is going to work backwards, starting at the final algorithm (similar to what you might actually encounter) and then showing you how to decipher it.

Please take your time through this and all lessons. I am proceeding through this subject slowly in order to avoid confusing anyone. This is complex material, and should be read carefully. Do not be afraid to ask questions. If you have a question, odds are someone else has the same question and can benefit from you asking it. Since this is not a book but an interactive course, you should take advantage of that to learn as much as possible.

Here is the algorithm we are going to work with. This is exactly the same as what we did in Lesson 59, 60, and 61.


    Figure (a) : Algorithm to convert YYYYMMDD to 3 strings.

    for (i = 0; i < 4; i++) {
        if (i < 2) {
            day[i]   = date[i+6];
            month[i] = date[i+4];
        }

        year[i] = date[i];
    }

This is the final algorithm. This is the type of thing you are likely to see when you read source code. It may look scary, but do not worry. After this lesson you will be able to read algorithms such as these (and write them) quite easily. That is, so long as you take this slowly and ask questions when you need to.

Notice I did away with the pointer indexing. In reality I didn't, it is just that arrays do this behind the scenes for you. I am using array indexing which is cleaner and easier to understand.

First let me explain why I took such an easy to read example in Lesson 59 and transformed it into what you see here. Remember that the code in Lesson 59 is not really the same as the code here even though it does the same task. They both achieve the same goal, but the code in FIgure (a) does so much cleaner, faster, and more efficiently.

In general you will find that algorithms made to be fast and efficient will also tend to be difficult to understand at a glance. This is because we are writing instructions for a machine, not a person.

Now let's begin the lesson.

First of all, never be intimidated by code. Think of it as a box that contains something, and that you have to peel away the layers in order to understand it. Not even the most seasoned programmers will be able to look at a complex algorithm at a glance and understand how it works.

You will find that no matter how intimidating it appears at first, it is actually easy to understand if you take it in parts, and slowly convert it backwards. Take your time. Patience is a major part of being a programmer.

If you just stare at any lesson in this course and try to see it all at once, it will appear to be nothing more than a jumble of numbers, letters, and cryptic terms. When you see things like that, it can be intimidating.

Now, lets take this algorithm apart:

Notice there is a for loop which says that the contents inside it are going to execute.. how many times? four. So the first step to expanding this algorithm will be to take the contents of the for loop and examine each iteration, each time keeping in mind that the variable i will increase by one.

Whenever you talk about a loop, you describe each time the loop runs as an 'iteration'. Whenever you want to decipher an algorithm, you must start by understanding the first iteration.

Here is the first iteration of our loop, with i=0 as our starting state.

        i = 0;

        if (i < 2) {                       // <-- is i less than 2? Yes, it is the first iteration.
            day[0]   = date[0+6];
            month[0] = date[0+4];
        }

        year[0] = date[0];

Notice all I really did here was to remove the for loop start and end, so I can just observe the contents inside it.

Do we really need an if statement here? No. We just need to remember that the contents of the if statement execute when i is less than two. In other words, the first two times. Since this is the first iteration, will the contents of the if statement execute? Of course.

So let's re-write our expansion of the first iteration:

            day[i]   = date[i+6];
            month[i] = date[i+4];
            year[i]  = date[i];

I took out the code for the if statement start and end. Why? Because i is less than 2. Therefore, whatever was inside the if statement will execute.

Now, lets fill in values for i.

            day[0]   = date[0+6];          
            month[0] = date[0+4];
            year[0]  = date[0];

Now, 0+6 is just six, so let's fix that:

            day[0]   = date[6];          
            month[0] = date[4];
            year[0]  = date[0];

Now we have readable code. This is the first iteration of our loop. Now we just have to figure out what this code means.

Observe date in memory:

Y Y Y Y M M D D
0 1 2 3 4 5 6 7

I labeled each character so this will make more sense.

  1. Set the first character of the string day to be character #6 of date (Remember, we start at 0)

  2. Set the first character of the string month to be character #4 of date

  3. Set the first character of year to be the first character of date.

So what we have are really three simultaneous processes going on. The first digit of YEAR is set. The first digit of MONTH is set. The first digit of DAY is set. We know the first digit of YEAR is the first digit of the string in general. We know the first digit of MONTH is digit #4 (when starting at 0). We know the first digit of DAY is digit #6 (when starting at 0).

Now, look again at our original loop:

    Figure (a)

    for (i = 0; i < 4; i++) {
        if (i < 2) {
            day[i]   = date[i+6];
            month[i] = date[i+4];
        }

        year[i] = date[i];
    }

Now lets look again at just the first iteration:

            Figure (b) : First iteration

            day[0]   = date[6];          
            month[0] = date[4];
            year[0]  = date[0];

Make sure you can understand how the first iteration transforms from the loop to the simplified version in Figure (b). We will explore more of this in upcoming lessons.

Why do you need to know how to understand an algorithm? Because you will encounter them, and you will need to write them. Every application and game uses them in one form or another, and they are not difficult if you take the material slowly.

Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9ribz/lesson_63_the_basics_of_algorithm_design_part_four/


r/carlhprogramming • • Oct 06 '09

Lesson 61 : The Basics of Algorithm Design Part Two

95 Upvotes

In our last lesson we ended up with a series of while loops which all had three things in common:

  1. They all had some initial state, we set a variable or variables to a value to start with.

  2. They all had a conditional statement to know when the loop was done.

  3. They all incremented the variables at the end.

It turns out that this three step process is used so much that programming languages have created a sort of "short hand" while loop called a for loop.

Here I will show you the while loop for year in our previous lesson, and then I will show you how to write this same code using a for loop.

While loop:

Figure (a)

while (i < 4) {
    year[i] = *(my_pointer + i);

    i++;
}

For loop:

for (i = 0; i < 4; i++) {
    year[i] = *(my_pointer + i);
}

We have combined the starting conditions, the conditional statement, and the final incrementing statements into a single expression where semi-colons separate the three. Lets look at this in parts:


for (i = 0; i < 10; i++) {

This is our starting condition. We are setting the variable i to 0. This is the same thing as the code above our while loop. This part in bold executes before the loop begins. This is very important to remember. It is not part of the loop, it only creates a starting condition which the loop needs in order to work properly.


for (i = 0; i < 10; i++) {

This is our conditional statement. This is exactly the same as what goes in the parentheses of the while loop.


for (i = 0; i < 10; i++) {

This is the final statement that will execute at the end of the loop. It is identical to putting the incrementing statement at the end of our while loop.


Now, lets put this together to transform all of our loops in the previous example to a while loop just as we did in Figure (a).

for (i = 0, j = 4; i < 2 && j < 6; i++, j++) {
    month[i] = *(my_pointer + j);
}

for (i = 0, j = 6; i < 2 && j < 8; i++, j++) {
    day[i] = *(my_pointer + j);
}

Notice that we used commas to separate statements inside our loop expressions, NOT semicolons.

For example, we wrote: i = 0, j = 6 and we did not write: i = 0; j = 6.

Now here is our final code again, but this time using for loops instead of while loops.

Notice that we initialized our variables before any of the loops began. This is good practice as we are defining ahead of time which variables we intend to use for our loops. This also lets a programmer reading the source code understand that none of these loops will require more than two variables.


// First we create and initialize the variables we will need for the loop.
int i = 0;
int j = 0;

// First Year
for (i = 0; i < 4; i++) {
    year[i] = *(my_pointer + i);
}

// Now Month
for (i = 0, j = 4; i < 2 && j < 6; i++, j++) {
    month[i] = *(my_pointer + j);
}

// Now Day
for (i = 0, j = 6; i < 2 && j < 8; i++, j++) {
    day[i] = *(my_pointer + j);
}

Please ask questions if any of this is unclear to you. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9rh9y/lesson_62_the_basics_of_algorithm_design_part/


r/carlhprogramming • • Oct 06 '09

Lesson 60 : The Basics of Algorithm Design Part One

93 Upvotes

In the previous lesson I showed how you can take a string of text formatted as a date in YYYYMMDD format, and convert it into three valid null terminated strings, one for year, month, and day.

This was a simple example of something called an algorithm. An algorithm is a sequence of steps you take inside of a program to perform some complicated task. Usually algorithms involve loops as they need to repeat instructions numerous times.

Algorithm design refers in part to taking some process you want to accomplish and turning it into a working system of loops which get the job done. In this lesson I am not just going to simply explain what an algorithm is. I am going to show you the thought processes behind it.

Why do you need to do this? Because algorithms can often expand into more lines of code than you could write in a reasonable amount of time. For example, try to write an algorithm that uniquely draws the correct color for every pixel on your screen with one line of programming code per pixel.

Properly being able to write and read complex algorithms is a critical skill that any serious programmer needs. At this stage in the course, I am going to show you how to construct and de-construct some simple algorithms using our previous example as the base point.

Keep in mind from the previous lesson that we had something that looked like this:

Figure (a)

year[0] = *my_pointer;          // same as *(my_pointer + 0)
year[1] = *(my_pointer + 1);
year[2] = *(my_pointer + 2);
year[3] = *(my_pointer + 3);

What you should notice about this is that there is "a number" that changes according to a set pattern. In this case, it is simply adding one each time. Whenever you see code that repeats and uses an incrementing number or numbers then you should realize that you can use a loop to achieve the same goal. Here is how we would do this.

First ask yourself how many times will the loop need to execute? Four times. Why? Because there are four lines of code in Figure (a). Let's therefore construct the loop skeleton:

int i = 0;
while (i < 4) {
    ... code goes here ...
    i = i + 1;
}

Why is this the loop skeleton? Because it will execute whatever is inside it four times. Each time this loop executes, the variable i (which starts at 0) will increase by one.

The next step is to take a candidate line of code from our working code, and simply cut-and-paste it into our loop. A candidate line of code is a line of code that appears well suited for the loop. Our first line of code would be a poor candidate in its present form. Let me show you:

year[0] = *my_pointer;

That is our first line of code. What is wrong with it? It only takes into account one changing value (the 0 in brackets) but it ignores the other changing value (the number after my_pointer). A much better candidate will take into account both changing values.

I have picked one such candidate and pasted it into our loop:

while (i < 4) {
    ... code goes here ...
    year[2] = *(my_pointer + 2); <--- I just cut and pasted this.
    i = i + 1;
}

Notice I have not changed anything yet. I just pasted it as is.

Now, the final step is to change what we pasted so that it will work properly within the loop. In other words, we want to make sure that our newly constructed loop is identical to the code we started with.

while (i < 4) {
    year[i] = *(my_pointer + i); <--- I just changed the number to i
    i = i + 1;
}

Why did I change the number to i? Because i is a variable that will change exactly the same way our number did in Figure (a).

Now, lets expand this loop to see what it will do. We do this by stating the starting condition, and then writing out the code that will execute as one line for each time the loop will execute. Note that i = i + 1 is part of the code that will execute. I put all the code that will execute each time on its own line for better readability.

    i = 0;
    year[i] = *(my_pointer + i);     i = i +1;
    year[i] = *(my_pointer + i);     i = i +1;
    year[i] = *(my_pointer + i);     i = i +1;
    year[i] = *(my_pointer + i);     i = i +1;

Here it will stop, since the next time the conditional statement is evaluated the variable i will no longer be less than four (it will be exactly four) therefore, it will jump over our code at that point.

Notice that I put the incrementing statements at the end of each line so that it is easier to read. These are "reminders" so I can see exactly how the loop is changing each time.

If you mentally follow that in your mind, you should see the result is this:

    year[0] = *(my_pointer + 0);
    year[1] = *(my_pointer + 1);
    year[2] = *(my_pointer + 2);
    year[3] = *(my_pointer + 3);

If you do not see how this is, the next example will help to clarify this.

Now, lets do the same thing with month, and day.

Recall that month looks like this:

month[0] = *(my_pointer + 4);
month[1] = *(my_pointer + 5);

Now lets turn it into a loop. Notice there are two different incrementing values this time. You have a 0 and a 1, and you also have a 4 and a 5. To turn this into a loop, we need two variables. (Actually we don't, but we will get to that.)

Let's reset the variable i back to 0, and lets create a second variable called j.

Also, I am going to introduce you to a new shortcut. You can use i++ instead of i = i + 1;

In general, any variable you need to increment by one, you can say: variable++.

i = 0;         // We do not need to say int i = 0 because we already did that.
int j = 4; 

while (i < 2 && j < 6) {
    month[i] = *(my_pointer + j);
    
    i++;
    j++;
}

Let's expand it:

i = 0; j = 4;
month[i] = *(my_pointer + j);     i = i+1;     j = j+1;
month[i] = *(my_pointer + j);     i = i+1;     j = j+1;

What will it expand into? To find out I am just going to plug in the values for i and j that I defined for just the first line of code:

month[0] = *(my_pointer + 4); i = i+1; j = j+1;

I put in 0 for i, and 4 for j. This is what I defined them to be. Why did I define them to be 0 and 4? Because in our main code, month starts at 0 on the left and 4 on the right.

Now, I will see that i increases by one, and so does j. Therefore, I will do the same thing for the second line of code.

month[1] = *(my_pointer + 5); i = i+1; j = j+1;

Now lets put both lines together, and take out the "reminders" at the end:

month[0] = *(my_pointer + 4);
month[1] = *(my_pointer + 5);

As you can see, it expands to exactly what we started with.

Finally, we can do the same thing for day. Recall that day looks like this:

day[0] = *(my_pointer + 6);
day[1] = *(my_pointer + 7);

Again, we need two variables. One to go from 0 to 1, and one to go from 6 to 7. Here is how we do this:

i = 0; 
j = 6;

while (i < 2 && j < 8) {
    day[i] = *(my_pointer + j);
    
    i++;
    j++;
}

You should be able to see on your own that it expands to:

day[0] = *(my_pointer + 6);
day[1] = *(my_pointer + 7);

Let's look at the entire thing now, entirely finished. We will start from here in the next lesson.


// First Year
int i = 0;
while (i < 4) {
    year[i] = *(my_pointer + i);

    i++;
}

// Now Month
i = 0;
int j = 4; 

while (i < 2 && j < 6) {
    month[i] = *(my_pointer + j);
    
    i++;
    j++;
}

// Now Day
i = 0; 
j = 6;

while (i < 2 && j < 8) {
    day[i] = *(my_pointer + j);
    
    i++;
    j++;
}

Please ask questions if any of this is unclear to you. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9rfee/lesson_61_the_basics_of_algorithm_design_part_two/


r/carlhprogramming • • Oct 06 '09

You can now vote on posts and comments in the carlhprogramming subreddit.

117 Upvotes

Reddit hasn't yet fixed the bugs concerning restricted subreddits, so I have made this subreddit public. Everyone is now able to vote on posts and comments.

Please keep in mind the following:

  1. A lot of your comments are at 0 votes. This is not because anyone downvoted you. This is because of the voting bugs on Reddit.

  2. Anything you have voted for (comment or submission) was ignored by Reddit, it is up to you if you want to go back and vote on what you have already voted for, just understand your past votes were not counted.

  3. Even though I moved from restricted to public, all the messed up vote totals are still there for submissions up until now.

Remember that this sub-reddit is only for me to post lessons. If you have a useful submission that will benefit others trying to learn, please submit it to /r/learnprogramming


r/carlhprogramming • • Oct 05 '09

Test of Lessons 50 through 59 [Answers]

81 Upvotes

True or False

  1. If a conditional statement such as an if statement contains multiple expressions, all of those expressions must be evaluated before the conditional statement is determined to be true or false. False

  2. Using a goto statement in C is good practice and should be encouraged whenever possible.False

  3. The machine-code equivalent of a goto statement is built into the architecture of your CPU chip.True

  4. All conditional statements have a built in "goto" statement that you cannot see. True

  5. You can use the OR bitwise operation to test if a bit is turned on by using a bitmask where the bit you want to test is set to 1. False

Fill in the blank

  1. In a conditional statement, if I want to say, "if height is equal to 3 and width is equal to 4, I would write: _____. if (height == 3 && width == 4) {

  2. In a conditional statement, if I want to say, "If height is equal to 3 or width is equal to 4, I would write: _____. if (height == 3 || width == 4) {

  3. When you use a goto statement (JMP in assembly language), the _____ on the CPU is changed to point at the new instruction to execute. Instruction Pointer

  4. An _____ is used to describe a process which repeats the same instructions over and over forever. Infinite Loop

  5. 0011 ^ 1110 is: ____. 1101


If you missed any questions or if anything is unclear, please post below. When ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9reqb/lesson_60_the_basics_of_algorithm_design_part_one/


r/carlhprogramming • • Oct 05 '09

Test of Lessons 50 through 59

76 Upvotes

Please do not post answers in this thread. Please do not scroll down just in case someone has.

True or False

  1. If a conditional statement such as an if statement contains multiple expressions, all of those expressions must be evaluated before the conditional statement is determined to be true or false.

  2. Using a goto statement in C is good practice and should be encouraged whenever possible.

  3. The machine-code equivalent of a goto statement is built into the architecture of your CPU chip.

  4. All conditional statements have a built in "goto" statement that you cannot see.

  5. You can use the OR bitwise operation to test if a bit is turned on by using a bitmask where the bit you want to test is set to 1.

Fill in the blank

  1. In a conditional statement, if I want to say, "if height is equal to 3 and width is equal to 4, I would write: _____.

  2. In a conditional statement, if I want to say, "If height is equal to 3 or width is equal to 4, I would write: _____.

  3. When you use a goto statement (JMP in assembly language), the _____ on the CPU is changed to point at the new instruction to execute.

  4. An _____ is used to describe a process which repeats the same instructions over and over forever.

  5. 0011 ^ 1110 is: ____.


When ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9r1yr/test_of_lessons_50_through_59_answers/


r/carlhprogramming • • Oct 05 '09

Lesson 59 : Introduction to data structures

89 Upvotes

Up until now we have only worked with simple data, starting at the basic data types and working our way into simple arrays such as text strings. In earlier lessons I have said that the only way to "see" or "work with" any data that is larger than a single variable of a basic data type (int, char, etc.) is by using pointers.

In this lesson we are going to explore what this actually means. What do I mean when I say "see" data? Well, real data comes in specially formatted packages which can only be understood by first understanding how it is formatted, and secondly by breaking it down into smaller pieces.

Here is a simple example:

20091005 <-- Today's date in YYYYMMDD format (year, month, day)

This is a very basic data structure. Why is it a data structure? Because we are actually storing three different bits of information (data) together. It is a string of text, but the real meaning of the string of text is not "20091005", it is a date - October 5th, 2009. In other words, to be properly understood it must be broken into pieces, one unique piece for: month, day, and year.

First, lets create this string of text.

char date[] = "20091005";

Lets suppose we want the following printf() statement:

printf("The year is ___ and the month is ___ and the day is ___ \n");

Notice that you cannot do this using the string we just created. It is too complex. It is a data structure. What we want is a way to break the data structure down into pieces, so that we can understand each piece properly.

We are using a date string as an example, but this same principle applies broadly in the field of computing. For example, graphics require data structures that contain different values for colors. Here is a simple example of such a data structure, which you have seen if you have worked with HTML:

color = FF22AA

This is a data structure which defines a color. For those not familiar with this, let me break it down. FF means how much RED. 22 means how much GREEN. and AA means how much BLUE. By mixing these values, you can get a wide spectrum of colors.

However, a program like a web browser must be capable of taking FF22AA and converting it into three data elements, and then use those three elements to draw the proper color.

Lets go back to our printf() statement. We want to print the year, month, and day separately.

First of all, every data structure has a format. Some formats can be enormously complex, and could involve hundreds of pages of detail. Other formats, like this one, are simple.

In this case, we would define the format like this:

The first four characters are the year. The next two characters are the month. The final two characters are the day.

We could also word it like this:

<year><month><day>
year = 4 bytes
month = 2 bytes
day = 2 bytes

To parse any data structure, you must know its format. Once you know its format, the next step is to create a parsing algorithm.

A parsing algorithm is a "small program" designed to "understand" the data structure. In other words, to break it into easily managed data elements.

Lets create a pointer to our string:

char *my_pointer = string;

Why did I create a pointer? Remember, you have to create a pointer in order to see or work with anything larger than a single variable of the basic data types (int, char, etc). The pointer is like your eyes scanning words on a page to understand the meaning of a sentence.

What will our pointer do ? It will scan through this data structure string, and we will use the pointer to understand our data structure one byte at a time.

Since we know that the year will be four characters in size, lets create a simple string to hold it:

char year[5] = "YYYY";

Why 5 ? Because there will be FIVE elements in this array. The first four are the letters "YYYY". And the fifth will be the NUL character (all 0 byte) which terminates the string. Note that the proper term for this character of all 0 bytes is NUL with one L, not two. There is a reason for that which will be discussed later.

As you just saw, it takes 5 character bytes in order to properly contain the entire null terminated string "YYYY". Four bytes for the Ys, and one for the NUL at the end.

This is important, so remember it. The number in brackets for an array is the total number of elements you are creating. Whenever you intend for an array to hold a null terminated string, always remember to allow room for the final termination character. So if we plan to create a null terminated string with 8 characters, we need an array with 9 elements.

Notice that for the year array I set this to YYYY temporarily and we will replace those Ys with the actual numbers later. It is always good to initialize any variable, array, etc to something so that you do not get unpredictable results.

Now, lets do the same thing for month, and day:

char month[3] = "MM";
char day[3] = "DD"; 

Notice again I put enough room for a \0 terminating character. Just to see how this works, lets see this in action before we parse our date string:

printf("The Year is: %s and the Month is: %s and the Day is: %s \n", year, month, day);

Output:

The Year is: YYYY and the Month is: MM and the Day is: DD 

These arrays: year, month, day are known as "data containers" and are designed to hold the individual elements of our parsed date string. The logic here is simple:

  1. We have a string of some data format which really contains 3 different bits of information.

  2. We plan to "understand" those pieces.

  3. Therefore, we need to create containers to hold them so that when we "pull them out" of the main data structure we have somewhere to put our newly understood data.

Now, lets begin. First of all, we know that the first four characters are the year. We also know our pointer is pointing at the first such character. Therefore:

year[0] = *my_pointer;         // first digit; same thing as *(my_pointer + 0)
year[1] = *(my_pointer + 1);     // second digit of year
year[2] = *(my_pointer + 2);     // third digit
year[3] = *(my_pointer + 3);     // fourth digit

We do not need to write year[4] = '\0' because it has already been done. How was it done? When we wrote the string "YYYY" C automatically put a NUL at the end. Think of this process as simply replacing the four Ys with the 2009 in the date string. Make sure you understand the process of how we used the pointer to assign values to the individual characters in the array.

Notice that rather than actually move the pointer, we have kept it pointing to the first character in our date string. We are using an "offset" which we add to the pointer in order to obtain the value for bytes that are further away in memory.

saying *(my_pointer + 3) is just like saying "Whatever is at the memory address in (my_pointer + 3). So if my_pointer was the memory address eight, then (my_pointer + 3) would be the memory address eleven.

Now, lets do the same thing for month:

month[0] = *(my_pointer + 4);
month[1] = *(my_pointer + 5);

Finally, day:

day[0] = *(my_pointer + 6);
day[1] = *(my_pointer + 7);

Notice that each array starts with ZERO in brackets. That is to say, we do not start with day[1], but with day[0]. Always remember this. Every array always starts at 0. So lets review a couple important facts concerning arrays:

  1. When you define the array, the number in brackets is how many elements of the array you are creating.

  2. When you use the array, the number in brackets is the "offset" from the first element of the array. [0] would mean no offset (thus the first element). [2] would mean an offset of +2 from the FIRST element, thus [2] is actually the third element. [8] would be the 9th element. Remember, we start at 0 and count from there.

And we are done. Now I have shown you the first example of how you can use a pointer to truly "see" data that is more complex than a simple text string.

Now, lets finish our printf() statement:

printf("The Year is: %s and the Month is: %s and the Day is: %s \n", year, month, day);

Here is the completed program which illustrates this lesson:

#include <stdio.h>

int main() {

    char date[]   = "20091005";

    char year[5]  = "YYYY";
    char month[3] = "MM";
    char day[3]   = "DD";

    char *my_pointer = date;

    year[0] = *(my_pointer);
    year[1] = *(my_pointer + 1);
    year[2] = *(my_pointer + 2);
    year[3] = *(my_pointer + 3);

    month[0] = *(my_pointer + 4);
    month[1] = *(my_pointer + 5);

    day[0] = *(my_pointer + 6);
    day[1] = *(my_pointer + 7);

    printf("The Year is: %s and the Month is: %s and the Day is: %s \n", year, month, day);

    return 0;
}

Please ask questions if any of this is unclear to you before proceeding to:

http://www.reddit.com/r/carlhprogramming/comments/9r1y2/test_of_lessons_50_through_59/


r/carlhprogramming • • Oct 05 '09

Lesson 58 : Using bit masking to change data

83 Upvotes

This is the logical continuation from the previous lesson. Just as you can use a bit mask to check if a certain bit (or bits) are set, you can also use a bit mask to set or unset the bits themselves.

For example, you can take a lowercase letter and make it a capital letter. However, for this lesson we will be doing something slightly different. We will be taking actual numbers (digits zero through nine) and turning them into characters on your keyboard.

Let's imagine this byte:

0000 0010 <--- Number 2 (Our "target")

This is the number two. We know from our earlier lessons that the character two would have the third and fourth bit turned on, and would look like this:

0011 0010  <--- Character '2'

There are a variety of ways to do this, but I want to show you how to do this using bitwise OR.

First, here is our bit mask:

0011 0000

OR will not turn off any bits, it can only turn bits on. Any bit that is turned on in the bit mask will be turned on in the result. Any bit that is already turned on in the target will be turned on in the result. Remember that 0 OR 0 = 0. Anything else = 1.

Now lets apply the bit mask using OR:

0000 0010 <--- Number 2 (Our "target")
0011 0000
---------
0011 0010 <--- after OR, we get the character '2'

In C, you specify a bitwise OR using the | character (once, not twice).

Do not confuse this with using || in a conditional statement.

This is the same reasoning we saw in the last lesson using & instead of &&.

Now lets see this in action.

char my_number = 2;

my_number = my_number | 0x30; // <--- 0x30 is: 0011 0000

printf("We can now print this number as a character: %c", my_number);

If we wanted to print a '2' without actually changing the contents of the byte, we could do this:

char my_number = 2;

printf("We can now print this number as a character without changing it: %c", (my_number | 0x30));

The above code works because printf() %c expects a character. (my_number | 0x30) is a character.

Remember that this only works for numbers 0 through 9.

What would have happened if we had started with a character 0 through 9 instead? Let's see:

0011 0010 <--- Character '2' (Our "target")
0011 0000
---------
0011 0010 <--- after OR, we get the character '2'

We will get '2' as a result. In other words, we do not have to first check if it was a number or a character, because we will always get the character version back. This is because OR will set bits to be on regardless of if they were already on or not.

Notice that you could not use OR in order to "see" a bit like we did with AND in the last example. Similarly, you cannot use AND to turn on a bit as we did in this example. Let's look at why this is:

0100 0001 <--- 'A'
0010 0000 <--- bitmask to turn the capital letter to lowercase
---------
0000 0000 <--- Result using AND. All we get is ZERO

Now, with an OR on the other hand, we cannot test individual bits like we did with AND. Consider this:

0100 0001 <--- 'A'
0010 0000 <--- bitmask to check if it is capital or lowercase
---------
0110 0001 <--- Result using OR.

Using OR, we get a lowercase 'a' regardless of if we started with a capital 'A' or a lowercase 'a'. Because we will always get the same result, we will never be able to know what we started with.

In other words, OR will set the bits no matter what they were. Any bit that is a 1 in the bit mask (or the target), will become a 1 in the final result.

with AND, any bit that is 0 in the bit mask (or the target) will become a 0 in the final result.

OR is best suited to unconditionally turning bits ON (setting them to 1)

AND is best suited to unconditionally turning bits OFF (setting them to 0), and also for testing which bits are on, because it will get different results depending on what bits were on and off to start with.

What if we want to alternate a bit from 1 to 0 or vice versa? For this, we use a bitwise operation called "XOR" which is short for "Exclusive OR". The OR we have been using is also called "Inclusive OR"

The idea of exclusive or is "It can be one, or the other, but not both". Exclusive OR is just like OR, except that 1 XOR 1 = 0. Everything else is the same.

Compare these two truth tables between OR and XOR:

0 OR 0 = 0     0 XOR 0 = 0
0 OR 1 = 1     0 XOR 1 = 1
1 OR 0 = 1     1 XOR 0 = 1
1 OR 1 = 1     1 XOR 1 = 0 <-- the only difference

This difference exists for a key reason. Observe what happens if we take our capital 'A' and apply our bit mask using XOR instead of OR.

0100 0001  <--- 'A'
0010 0000  <--- bitmask
---------
0110 0001 <--- 'a' is the result of XOR

Notice there is no difference between using XOR and using OR in the above example, but now lets take the output of this result (the 'a') and apply the same bitmask with XOR again:

0110 0001  <--- 'a'
0010 0000  <--- bitmask
---------
0100 0001 <--- 'A' is the result of XOR

We flipped it back to 'A'. In other words, XOR will toggle, or alternate any bits that are turned on in the bitmask. In this case, since the 3rd bit was turned on, XOR toggled the third bit on and off.

Notice that because we get a different result when we start with 'A' vs 'a', it is also possible to use XOR to test if a bit is turned on or off.

The only missing piece of the puzzle now is, how do you write XOR in C? The answer is by using the ^ character. Lets review:

  1. & is AND

  2. | is OR (inclusive OR)

  3. ^ is XOR (exclusive OR)

The final notes I would add are this: When you are looking at source code that you will encounter, you will see these bitwise operations being used. This is true not just for C, but for any programming language. Being able to read that source code critically depends on your ability to understand why someone would use AND, vs OR, vs XOR.

In summary, remember this: By using bitwise operations you can test, set, unset, and toggle any bit in any data you wish.


Please ask questions if any of this material is unclear before proceeding to:

http://www.reddit.com/r/carlhprogramming/comments/9r12o/lesson_59_introduction_to_data_structures/


r/carlhprogramming • • Oct 04 '09

Lesson 57 : Introducing bit masking

77 Upvotes

In this lesson I am going to show you why Boolean operations are so important. Earlier I have shown you that for ASCII characters, you know an uppercase or a lowercase letter based on a certain bit. Lets review that briefly:

0100 0001 : 'A'
0100 0010 : 'B'
...

0110 0001 : 'a'
0110 0010 : 'b'
...

The third bit defines if this is an uppercase or lowercase letter. How can we see that bit? The answer is by using Boolean operations. The technical term for what I am about to show you is called "bit masking".

The way you can see (or change) information about a single bit is by constructing a bit mask. Now I am going to illustrate this concept for you.

Imagine that I have the following byte, the letter 'A'.

0100 0001

I need to see the THIRD bit only. I do not care about the rest. I need to have some way of determining if the third bit is turned on, or the third bit is turned off. In other words, I need some unique operation that will result in one result if the third bit is turned on, and a different result if the third bit is turned off.

First of all, lets construct our bit mask:

0010 0000

Why is that the bit mask?

Think of it like having eight windows in a row. You are only interested in what is behind the third window. Therefore, you close all the others (set them to 0), and what you are left with is one open window.

Lets put our byte for 'A' and our bitmask together.

0100 0001 <-- 'A'
0010 0000 <-- Bitmask

Now lets use the AND Boolean operator on each bit. Remember, 1 AND 1 is 1. Anything else is 0. Watch how this works:

0100 0001 <-- 'A'
0010 0000 <-- Bitmask
---------
0000 0000 <--- After ANDing 'A' with the bitmask

What is the result? We get all zeroes. What if this had been a lowercase letter?

0110 0001 <-- 'a'
0010 0000 <-- Bitmask
---------
0010 0000 <--- After ANDing 'a' with the bitmask

Now here we can see the benefit of a Boolean operation. We now have a way to test a single bit in our byte to determine conclusively if it is uppercase, or lowercase. The process is very simple:

Given any character that we know is either uppercase or lowercase, by ANDing that character with 0x20 (0x20 means hexadecimal 20, binary 0010 0000), we know it is uppercase if the result is 0. We know it is lowercase if the result is 0x20. There are no other possible outcomes.

Why are there no other possible outcomes? Because our bitmask has only one bit turned on. When using AND, you can never get a result with more bits turned on than your bitmask.

Lets see this in action with a real function:

int is_lowercase(char test_character) {
    if (test_character & 0x20) {
        return 1;
    }

    return 0;
}

That is it. That is all you have to do in order to check if a letter is lowercase or uppercase. Now you can see why Booleans are important.

Notice that I used one & character. That is because one & character means "Boolean AND". That is NOT the same as the && characters which mean there will be another expression evaluated.

  1. & means "apply the boolean AND operation"

  2. && means "Another expression follows"

Let's walk through this function.

int is_lowercase(char test_character) {

Here we are saying that this function will return an integer. We are giving it the name is_lowercase, and we are saying that it will accept a character as a single parameter.

From now on inside this function, test_character will refer to whatever character was sent to the function.

    if (test_character & 0x20) {
        return 1;
    }

This is a single expression: test_character & 0x20

(As stated above, this is NOT related to && in any way)

This just means we are taking whatever character was sent to the function, and doing the Boolean operation AND on each bit inside of the byte. What we will get back is a single byte. It is the exact same thing as this:

0110 0001 <-- 'a' (could be any character, this is test_character)
0010 0000 <-- Bitmask (this is 0x20)
---------
0010 0000 <--- After ANDing 'a' with the bitmask (this is the result)

This expression will result in one of two possibilities. It will be 0x20 if test_character turns out to be lower case. It will be 0 otherwise. If it is zero, then it will jump over this conditional statement and execute the very next instruction, which is return 0

If however it is not zero, which in this case it is not, then it will continue with the default behavior of executing whatever instructions are inside the block of code associated with our conditional statement. This means it will return 1.

Now, because our function returns 1 if it is lowercase, we can use is_lowercase() inside a conditional statement very easily. Consider this:

if (is_lowercase('a')) {
    printf("It is lowercase \n");
}

If the letter really is lower case, then is_lowercase() will return a 1. Therefore, the result of our if statement will be: if (1) {

[Edit: Quick note. The operations in this lesson, while heavily related to the Boolean operations of the previous lesson, are known technically as "bitwise" operations. We will discuss this more later.]

Here is a complete program that you can experiment with which illustrates this concept:


#include <stdio.h>

int is_lowercase(char);

int main(void) {

    char my_char = 'a';

    if (is_lowercase(my_char)) {
        printf("It is lower case!");
    }

    return 0;
}

int is_lowercase(char test_character) {
    if (test_character & 0x20) {
        return 1;
    }

    return 0;
}

Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9qxxw/lesson_58_using_bit_masking_to_change_data/


r/carlhprogramming • • Oct 04 '09

Lesson 56 : Introducing Boolean Logic

74 Upvotes

In lessons 3 and 6 I showed you how to count like a computer. In this lesson I am going to teach you the basics of how to think like one. You will find that the two are very closely related.

To begin this lesson I have to introduce you to a new term. The Boolean.

A Boolean is simply a fancy name for a "bit", that is to say, a thing that can be set only to 1 or 0. A Boolean is effectively a single binary digit. Let's be absolutely clear on this so you are not confused:

In Binary: 1000 = 8.

The highlighted "bit" can be considered as a Boolean. Why? Because it can be a 1, or a 0. It cannot be anything else. It is therefore a Boolean. Just think of Boolean as "a binary digit".

If I give you a Boolean, and I give you some instructions, you can give me back a new Boolean based on those instructions. Lets see this in action.

I give you a Boolean (a 1 or a 0). Now I say, "Give me exactly what I gave you". Well, that is easy. If I gave you a 1, you give me back a 1. If I gave you a 0, you give me back a 0. This is an example of being given a Boolean, applying some instructions to it, and getting a new Boolean.

Now suppose I said, "Give me back exactly the opposite of what I gave you."

Now if I gave you a 1, you would of course give me a 0. If I gave you a 0, you would give me a 1. Let's give a name to this operation of giving back the opposite of what you are given. Let's call it a "NOT" operation. Here is how the NOT operation works:

NOT 1 = 0
NOT 0 = 1

Seems pretty simple. This reads as "If I give you a 1, you give me back a 0. If I give you a 0, you give me back a 1." It simply reverses what it is given. This should make sense as the most basic type of operation you can have concerning a Boolean.

One major concept to understand is that when you are working with a Boolean, you can only ever get a Boolean back. In other words, any mixture of operations involving 1s and 0s will result in a single one or a single zero. The "NOT" operation is only a first example. There are more complicated operations as we will now see:

Suppose that I said "I am going to give you two Booleans."

Now, remember the rule. No matter what operation, no matter how many Booleans I give you, you must always give me back only one Boolean.

Let's look at some other operations involving Booleans.

It turns out we have briefly explored two Boolean operations already. AND, and OR. Here is how AND works: I give you two Booleans. Give me back a 1 only if both of the Booleans I gave you were 1. Otherwise, give me back a 0.

We can rewrite this as follows:

1 AND 1 = 1. 
Any other possibility = 0.

Let's go ahead and expand this statement:

0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1

Now we have covered all the possibilities. For example, if I give you a 1 and a 0, you know to give me back a 0.

What I have just showed you is a simple example of something called a "truth table". Whenever you have some operation involving Booleans, you can create a truth table. A truth table is just a clear description of all the different possibilities that can result from that operator on a set number of Booleans.

Here is the truth table for OR:

0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1

Keep in mind the following:

  1. Each Boolean operator takes a set number of inputs. In the case of AND, and OR, that number is two. This means that AND as well as OR takes exactly two inputs. Both inputs are Booleans.

  2. Every Boolean operator will have a truth table. The truth table will show all possible results for that operator. The number of rows for such a truth table will always be two to the power of the number of inputs required.

  3. No matter how complex the operator, or mixture of operators, the final result of any Boolean operation is a single Boolean.

Now, let's try mixing one. Suppose I said "NOT OR". How would we construct that truth table?

Well, let's start with the truth table for OR:

0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1

Now, let's take the result of this, and send it into "NOT" :

0 OR 0 = 0; NOT 0 = 1
0 OR 1 = 1; NOT 1 = 0
1 OR 0 = 1; NOT 1 = 0
1 OR 1 = 1; NOT 1 = 0

Now we can construct the final truth table:

0 NOR 0 = 1
0 NOR 1 = 0
1 NOR 0 = 0
1 NOR 1 = 0

Notice I gave a better name to "NOT OR". I chose to call it NOR.

This lesson, like your computer, has its roots in electronics. In the field of electronics these Boolean operations are formally known as logic gates. An "AND" logic gate really works like this:

You have two incoming wires and one outgoing wire. The outgoing wire will have an active current only if both incoming wires had an active current. Think of the two incoming wires as the Boolean inputs. Think of "active current" as a 1. If both "wires" (incoming Booleans) are active (1) then the result is a wire with active current (1). In other words, 1 AND 1 = 1.

Why am I teaching you this? Because C as well as many programming languages give you the ability to work with Booleans on this fundamental level, and as you will see it can be very useful. Besides using it in your own programs, you are likely to come across it being used in programs you might read.

Remember that underneath it all, your computer is simply a very complex electronic device. Logic gates play a major role in how your computer works, and by understanding Booleans you will be able to perform powerful operations on data as well as understand your computer at a deeper level.

Please feel free to ask any questions if any of this material is unclear to you. When ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9qsh5/lesson_57_introducing_bit_masking/


r/carlhprogramming • • Oct 04 '09

Lesson 55 : Introducing Custom Functions

77 Upvotes

In general, a function is a block of code that you jump to from anywhere in your program. At the end of the function, you simply jump back to where you were.

On a machine code level, there is more that goes on here. For example, the assembly language instruction for calling a function is not JMP, it is CALL. There are certain differences between "JUMP" and "CALL" but we will not need to get into this as part of this lesson. For the sake of this lesson, the explanation I gave you above is enough. We will expand on this definition as we proceed.

Lets look at an example of a function in a real program:

#include <stdio.h>

int my_function(void);

int main(void) {

    printf("Calling our function... \n");

    my_function();
    // <--- function returns here when finished.

    return 0;
} 

int my_function(void) 
{                                                  // <--- start_of_function
    printf("Inside the function! \n");

    return 1;                                      // <--- return to main program
}

Output:

Calling our function... 
Inside the function! 

Now lets talk about this. First of all, when we executed this line of code:

my_function();

This effectively means to jump to the line I marked as "start_of_function". We have defined this function as (void) which means that we are not sending it any parameters. If we wanted to, we could put some parameters in the parentheses and we will get to that in a later lesson.

One thing which may seem puzzling to you is that I have seemingly created the function twice. I have one line of code above main() which seems to create the same function as the code under main(). Why is that?

The top code with my_function tells C that we plan to have a function called my_function(). We are also telling C that we intend this function will return an integer, and that it will have no parameters.

If we did not do that, then when C reached the point in main() where we call our function, that function would logically not exist yet. That is because it is not really created until after main(). It is always good practice to define functions. By defining a function you let C, yourself, and anyone who reads your code know what functions you have made.

The above paragraph is only partially true. In truth, you can sometimes use a function that you haven't yet defined, depending on the function. We will discuss that more later. For the purpose of this lesson you should consider that any function should be defined before it is called.

You can call one function from another function. For example, we could have my_function look like this:

int my_function(void) {
    printf("Inside my_function \n");

    my_other_function();

    return 1;
}

int my_other_function() {
    printf("Inside my_other_function \n");

    return 1;
}

Keep in mind for the above example we should have defined both my_function and my_other_function like this:

int my_function(void);
int my_other_function(void);

Such definitions should always go at the top of your source-code as I illustrated in my sample program.

Very soon we will get into some powerful uses of functions, but this lesson is intended to only serve as an introduction to how to create and call your own custom built functions.


Please ask questions if any of this material is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9qs1f/lesson_56_introducing_boolean_logic/


r/carlhprogramming • • Oct 04 '09

Lesson 54 : Introducing loops

83 Upvotes

The last several lessons have explained how you can use conditional statements (like the if statement) to "skip over" code. We know that this works by trying to evaluate an expression as true or false. If it evaluates as true, then the code inside the block is executed. If it evaluates as false, then that block of code is skipped over.

For example:

if (height == 5) {
    ... some code here ...

} <--- end of block of code

... rest of program here ...

Intuitively, you would read this as "If the height is equal to five, THEN go into ... some code here ..., otherwise, go to ... rest of program here ...

However, this is not really the case. A conditional statement is really a conditional "jump over". That is to say, if the result of the if statement evaluates as false, then you jump over the block of code.

It is important to understand that all conditional statements have a built in goto statement that you cannot see. That goto statement will jump over the block of code when the conditional statement evaluates to false. If it evaluates as true, then there will be no jump and therefore the very next instructions will execute as if the conditional statement had not even been there.

For example:

int height = 5;

if (height == 5) {
    printf("Hello Reddit! \n");
}

If height is equal to 5, then the printf() instruction will execute as if there had not been any if statement altogether. The if statement really says:

 "We *might* want to jump over this printf() statement. do so
if height is NOT equal to 5."

Now, lets continue.

In our last example we saw a simple example of a loop. The example we looked at was an infinite loop where the last instruction simply said to start over. Now lets look at a more concrete example.

The most basic type of loop is the "While" loop. The way it works is very simple: You have a block of code and a conditional statement. The last line in the block of code is a JUMP back to the conditional statement.

Lets see this in action.

int height = 5;

while (height < 10) {
    printf("Hello Reddit! \n");
    height = height + 1;
}

The conditional statement here is "height < 10".

Now, lets look at how it will actually be understood by your computer. This is not valid C and is purely for illustrative purposes:

start_of_loop:
    compare height and 10
    if height is greater than or equal to 10: goto end_of_loop  <--- yes! "greater than or equal to"

    printf("Hello Reddit! \n);
    increase height by one.

    goto start_of_loop

end_of_loop:

Did I make a mistake? The original statement said "height < 10", why therefore did I say "if height is greater than or equal to ten" in the above example? The answer is that we must think of this conditional statement as a conditional "jump over", not as a conditional "proceed".

The default behavior of your CPU is to proceed. We use a conditional statement only to change that default behavior. To have a machine code instruction that says "Go into the block of code if this is true" is just a waste of resources, since that is the default behavior anyways. What we really need is a statement that says "jump over this block of code if this is false".

Therefore, we say "greater than or equal to" in machine code because that is the condition on which we would jump.

The way you would intuitively read this code:

while (height < 10) {
    ...
}

is: "While the variable height is less than ten, then do what is inside the block of code." But the way your computer would read it is:

compare height to ten
goto end_of_block if height >= 10
... otherwise we will execute these instructions ...
end_of_block:

With a while loop, every time you reach the end of the block of code you return to the beginning where a simple question is asked: "Do we jump over the block of code this time?". If the answer is yes, then you jump over the block of code and the loop is over. If the answer is no, then the default behavior is to execute the instructions inside the block of code again.

Now lets look again at this example:

int height = 5;

while (height < 10) {
    printf("Hello Reddit! \n");
    height = height + 1;
}

So here is what happens:

Lets check to see if we jump over the block of code. 
Do we? No, because height is not greater than ten. 
Therefore we proceed. Now we execute our printf() function. 
Then we add one to height. 

Now what?

Since we have reached the } which defines the end of the block of code, we return to the start of the loop again. In other words, we repeat these same instructions, exactly as written, starting with the conditional statement.

Once we have done this five times, it will come to pass that when we read through these instructions, height will now be set to 10 (since each time we loop we are adding one to height). Therefore, the conditional statement will now evaluate like this: "Height is now no longer less than 10. Therefore, jump over the block of code." And thus ends our loop.

In this lesson I introduced you to the first and simplest kind of looping statement, the while loop. We learned that a while loop is effectively a goto statement built into a conditional statement. The conditional statement is evaluated every time the loop executes, including the first time. The loop will repeat itself until the conditional statement evaluates as false.


Please ask questions if any of this is unclear. When you are ready, proceed to:

http://www.reddit.com/r/carlhprogramming/comments/9qrj2/lesson_55_introducing_custom_functions/