>>>>> diamond
The way we're going to do this is we're going to look at one implementation that makes use of string functions, and would be something that we would expect you would be able to do after 106B and last class's concepts.

The below function is fully implemented and works, so let me just compile and show you. PICHU. Okay, great.

Before we look at the body of this diamond function, I want you to look at the parameter type of str. Why is this a pointer? Because remember C passes in arrays not by value, like every other type, but as an address. So really, str here is a pointer to the first location. I'll draw it here. Note that I can change the parameter type to be an array, like so, but C will still only pass in the address, not the entire array, and so these two parameter types are equivalent. This equivalence between arrays and pointers isn't always true, which we'll see in the second half of lecture.

Anyway, back to the body. In line 26 I save the length of the string because remember, most string functions run in O(N), so this is more efficient. Then to print out the top half of the diamond, I use a loop, where the index i is the number of characters from string that I want to print out. I create a separate substr buffer, of size i+1 so I can hold the null terminator, I copy in the first i characters into this substr buffer using strncpy, I null terminate, and then I printf. For the bottom half of the diamond, my outer for loop has an index i representing the index offset from which I want to start printing my character. So first, in the inner loop, I print out i - 1 spaces, then I print out the substring starting from str + i. So if i is, say, 2, the inner loop will print out j = 0, j = 1, so two spaces, then print out characters starting from address str + 2, aka c, h, u, null terminator. Great.

The next implementation I'm going to show you is something that is more "C-programmer-esque", in that it makes use of a single buffer. I'll keep the original implementation and call this diamond_long. Since I'll need to go through the string a whole bunch of times, probably, I'm going to save the length as before, but now I'll call it len. Yeah, after you do a lot of C programming, you'll learn that C programmers LOVE variables that are three characters long, and functions that are like less than seven characters long. Anyway.

 25 void diamond(char *str) {
 26     int len = strlen(str);

I'm still going to split my problem into top and bottom half, but now I'm just going to straight up use one buffer, and keep editing this buffer regardless of what I'm printing out. Yeah. So I create buffer that can contain my entire string including the null terminator, first. There's garbage stored at these areas when things start off. *mark*

 27 
 28     char buf[len+1];
 29     // print top half of diamond
 36     // print bottom half of diamond

Then for the top half, I'm going to do the following approach. Copy in the next character I need, then null terminate. Then print out buf. So first p goes in, then a null terminator. On the next iteration, I'll copy i over the null terminator, and put in another null terminator. Crazy, right? Anyway I'll type in this implementation and talk a bit more about it. *type* Run this. Okay, cool! The reason why this works is because printf prints characters until we get to a null terminator, so it doesn't matter what garbage we have in the uninitialized part of buf.

 30     for (int i = 0; i < len; i++) {
 31         buf[i] = str[i];
 32         buf[i+1] = '\0';
 33         printf("%s\n", buf);
 34     }
 35 
Okay, now the bottom half! I'm going to go back through the buffer and replace each character with a space character, and print out the buffer from the beginning each time. *type* What this is going to look like in our diagram is like so. Yeah, that's cool.
 37     for (int i = 0; i < len - 1; i++) {
 38         buf[i] = ' ';
 39         printf("%s\n", buf);
 40     }
 41 }

Wait, when we run this, we actually get a blank line here...but when we ran this earlier it wasn't like that. What gives? Oh, I see, the last iteration here will copy a space into the last character, the 'u' space, so we'll just print out a line of all spaces. Meh, we don't actually want that though, so what I'll do is modify this loop to go up to len - 1. And now we get the right thing. Woohoo! Pat on back.


>>>>> pig
Okay, so what is pig latin? According to wikipedia, pig latin is a made-up language formed from English by transferring the initial consonant or consonant cluster of each word to the end of the word and adding a vocalic syllable, so chicken soup would be translated to ickenchay oupsay . Pig Latin is typically spoken playfully, as if to convey secrecy.

I have some examples at the top, so be turns into ebay, <>, and one, which starts with a vowel, actually adds a "way" at the end.

The current unsolved version of our code has a converted buffer, line 25, and for each passed in argument, will call pig_latin, which will copy the piglatinified word into converted, which it prints out. Our version just copies the input into the output, like so. *type* But the solution does the following, just so you can see this.

Okay, so how do we get started? I'm just going to think about the non-vowel case, explained by "be" and "trash" input. If we go to our diagram, what should be copied into out first? Well, we start (1) copying in everything starting from the first vowel, so the e in this case, or the a in the trash case. We copy this to the end. Then, (2) we need to append all the consonants we skipped, so like, copy in the tr here, and then finally (3) we need to add on the pig-latin ending, "ay".

The following code achieves this. First, we need to find the first vowel after all the consonants. How do we do this? Well, two versions. We could either strspn with an accept string of all consonates, or we could do a strcspn with a reject string of all vowels! I'll do the latter, like so.

 18     int n = strcspn(in, "aeiou");

In the be case, n would be just 1. In the trash case, n would be t, r, this would be 2. In the one case, n would be 0! we'll come back to this case soon.

Now, I'll copy in the substring starting from our first vowel. Since we used strcspn earlier, the string starting from the first vowel is at the address of in + n. *type* remember, in is a character pointer that holds some address, and adding n to this will increment by n character sizes, or n bytes. 

 23         strcpy(out, in + n); /// &in[n]

This also happens to add a null terminator to the end. No worries though, because if we want to now concatenate on the initial constants, strncat will start copying over starting at the null terminator anyway. So we do actually want the null terminator, look at that! Let's strncat the n consonants we had at the beginning of our string.

 24         strncat(out, in, n);

Is out going to be null terminated? Let's check the manual for strncat. So yes, it is going to be, and this is different from how strncpy works! Kinda confusing, but do keep it in mind. Anyway, our last step is to add on the "ay" at the end, so we'll just call a normal strcat.

 25         strcat(out, "ay");

Okay! Let's make sure everything works. Yup, be is good, trash is good...but one isn't oneway. So how would we do this? Let's separate that into a separate case. If our word started with a vowel, then strcspn will return 0, because there are 0 characters before the first character reject string of vowels. *type* If this is the case, we just want to copy in the entire string as is, and then explicitly append "way", instead of "ay". *type*

 19     if (n == 0) {
 20         strcpy(out, in);
 21         strcat(out, "way");

Let's try it out. Yup, woohoo, that works!

Now there's one final case, which isn't super defined in language pig latin but is defined in the language of programmers, which is how to pronounce words that don't have any vowels in them at all. Let's take uh...str. Stray? That's not...that's not quite right. This is my interpretation, but since pig latin adds a syllable onto the end, "str" should be considered its own syllable. So I'm going to just say that str should become strway. *draw*

Yeah, so what is this case closest to? The initial vowel case. So I'll just add my conditional to say that if we have zero consonants or if we have all consonants, we need to explicitly add way on the end.

 19     if (n == 0 || n == strlen(in)) {


Alright, give yourself a pat on the back! This was quite a difficult example because of the following reasons. (1), this function is different from the functions we've written so far. Instead of returning something, it was passed in an output buffer, which it modified. (2), it showed a use case of the strcspn function, which you might not have thought was useful, but is incredibly useful especially when you want to access substrings, or offset your string pointer a bit. (3), it made use of the idiosyncracies of strcat and strncat which both add a null terminator at the end, and so there was no need to explicitly null terminate anything. This last point is especially perilous, and so I would say when in doubt, first look things up in the manual, and if there's no clause there, then set your null terminator.

I want to note one other thing here, which is that often a programming convention is that if we're not modifying a particular buffer parameter, it's good to add the const keyword. So here, we don't modify in, we just read it, so we can mark this as const. This doesn't really change how my parameters passed in, but it's nice as the programmer to indicate this. Do you always have to do this? Well, not if you are just passing things in by values, but for pointers it's nice to indicate something special.




>>>>> arr_ptr

Let's start with exercise1. This is the only one run in main. Huh, why is this thing commented out? Let's uncomment it for now and see what happens when we compile and run. make...Oh crap, so this is a compile time error. Let's see, it says...<>. So what happened here is that str is an array that points to lcoal memory, and because it is an array, we cannot update the address of this array! Boo! Let's comment out the offending line and see what happens. Okay, this is waht we expect, because we didn't update the dadress anymore.

Exercise 2. Now we have created a string "Hello2" and have a pointer str that points to it, we then update the pointer address, modify to add a u, then print it out. Let's make and run...aaaand segfault. Why? Oh, because we tried to modify the character at index 1, here that would be the first l because we updated str in line 27, but the string was created as a string literal, in the read-only data segment. So this is us modifying memory we shouldn't, and we get a segfault.

Exercise 3. Let's comment it in, run make, execute the program...finally! We get something. Why is this oneo kay? Ah, because we declared the string to be onthe estack, which is always the case when we use it as part of array initialization, and then we added apointer to array after that. So str gets updated to point to ello3, then the first l gets modified to be u instead.

Finally exercise 4? You can imagine what's going to happen: line 41 creates a string literal, located in read-only memory, str points to this location, str gts updated in line 43, fine, but then you can't modify read-only memory so line 44 will throw a segfault. Let's check...Yup.



>>>>> skip spaces
It's easiest to look at a diagram to see what's going on. Let's do a quick walkthrough of the code without focusing on all the pointers. In main, we create a string Hello World, print it out, then call skip_spaces. skip_spaces finds the number of leading spaces using strspn, then increments the string by that number of bytes. Then back in main, we print out the new string. Let's run this to make sure it works.

Okay, so what does the memory model look like? In main, we have a pointer, str, which when instantiated in line 24 is pointing to the starting character of hello world. Remember that because str is initialized as a pointer, the string literal is stored in the read-only data segment, so str cannot modify the contents. But you can update str to point to anywhere, because it's not an array. str's stored address is currently 0xf0, but we want it to point to this 'h', aka we want to update str's stored address to be 0xf5. *draw*

How does strptr achieve this? Well, str has some address, right? strptr points str. So if str is 0xe8, then strptr will store 0xe8. This happens in line 26, where we pass in the address of str as the value to store in our parameter strptr. 

Then every time we need to access the original string, i.e. a char *, we dereference the double pointer to get a single strptr, like in line 19. In line 20, we update the address stored by saying, here, you know the address stored at 0xe8? Let's update this stored address to be num_spaces more. That's what this dereferencing is doing.

So what would happen if we just had a single pointer as a parameter? Well, C's pass by value would kick in. We'd really just have another pointer to the original string, and updating that pointer would not change the str pointer at all. It would look something like this. This is why we use double pointers.


>>>>> verify password
Right, so what does this code look like? I've pulled up an implementation here. Main takes in one argument which is the password, and then compares it against this array of strings, called bad_substrings.

Note that the function signature of verify_password takes in the length of the bad_substrings array. This is because the array type is an array of character pointers, so you can't actually call strlen or something on it. But because we're trying to iterate over its contents, we'll need some way to know when to stop. Passing in the array length explicitly is one good way to achieve this.

So the function goes through and checks if the i-th substring is strstr in password. Wait, what is strstr? We didn't explicitly go over it, so let's check the man page. *check*. It says, the strstr function finds the <>. And if we check the return value, it says it returns NULL if the substring is not found. Okay, seems simple enough. It's always a good idea to double check the man pages for interesting descriptions of strings. For example there's a man page for this string function called strfry. Yep, they went there. frying the stirng means it returns a randomly swapped around anagram of string. But that's not important right now.

Back to our code, the string password is our haystack, and we want to find our needle bad_substrings[o]. If it exists, then the pointer is not null. So if the result of the strstr search is not null, we say that a bad substring was found, and we should return false, because this is not a valid password. At the end of looking for each bad substring, if we didn't find any of them, then this is a good password, so if we exited the for loop then we want to return true.

A few comments about this function. Since we are not modifying the password, only reading its contents, it's fine to pass this in as a read-only str, i.e., declare the parameter as const. We could do the same thing for the bad_substrings array too if we really wanted to, but it turns out the syntax for this const is a bit trickier. Feel free to think about it a bit and play around the code to see what compiles.

Another comment is that depending on how many parameters you have, and how long the names of each of the parameters are, you'll sometimes see C code that stylistically separates the parameters, one per line, like this. this is fine, but remember if you just had a whole bunch of short parameters, this would look kind of silly.

And a final note, line 42, we kinda just passed in a magic number here, 2, which is the number of elements in bad_substrings. We generally wouldn't want this, but also in the regular case we would load in bad_substrings from something else, so we would have two variables representing the substrings and the length, respectively. We'll learn more about how to get the length of an arbitrarily sized, locally declared array next lecture.