Let’s make a program that copies a string in C using VSCode. I’ll be including a new library that I haven’t come across yet, which is <ctype.h>.
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
string s = get_string("s: ");
string t = s;
if (strlen(t) > 0)
{
t[0]= toupper(t[0]);
}
printf("s: %s\n", s);
printf("s: %s\n", t);
}
This is actually the second version of the program thus far, and I’ll explain why. The if (strlen(t) >0) line was actually a late addition to the program, as well as the extra curly bracings around the toupper line.
This is because the input, if it was ‘hi’, would have generated the output ‘hi, Hi, Hi’ because I am incorrectly trying to copy addresses from existing addresses. It doesn’t understand that I am trying to capitalize the first letter of the variable T more than once because I haven’t explicitly decried as much.
Technically, copying strings would be much easier in a language such as Python, but because C takes everything so literally, you have to spell it out every function or command down to the soup and nuts, or else it won’t work. That is partially the reason why C has such abysmal syntax.
Let’s add another header file called <stdlib.h> which will allow me to manage my own memory using malloc. Let’s also change string s and string t to char *s and char *t because, in C, strings are really just getting the address of a sequence of values.
Adding the line char *t = malloc(strlen(s) + 1); allows us to do two things: one, allocate memory, and two, allocate memory in a way that is not hardcoded.
If I wanted to allocate enough memory for ‘hi!’ we would need not 3 bytes, but 4 because of the null character, but because I do not necessarily know what the user will input, we don’t want to hardcode a defined number of bytes to be allocated, lest the user write something that is stored in more than 4 bytes of memory.
Next, these lines of code…
for (int i = 0; i < strlen(s) + 1; i++)
{
t[i] = s[i];
}
… copies each character from s to the corresponding position in string t.
And these lines…
if (strlen(t) > 0)
{
t[0] = toupper(t[0]);
}
…checks to see if t has a greater string length than 0. If it does, it recognizes that t is not an empty string, so it capitalizes the first letter of that string according to the toupper function.
Thus, our new code looks like this:
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char *s = get_string("s: ");
char *t = malloc (strlen(s) + 1);
for (int i = 0; i < strlen(s) + 1; i++)
{
t[i] = s[i];
}
if (strlen(t) > 0)
{
t[0] = toupper(t[0]);
}
printf("s: %s\n", s);
printf("s: %s\n", t);
return 0;
}
Now, with the input of ‘hi!”, the output is as follows:
s: hi!
s: hi!
s: Hi!
However, there are 2 more steps that we can take to ensure efficient, bug-free code. First, we can simply reduce that entire for loop to strcpy(t, s) because C already understands, through the <stdlib.h> library, that a function for copying strings already exists.
Then, we need to add free(t); to the end of our code, after return 0; (which I actually forgot to include until just now). This, while many programs and languages do this intuitively, explicitly hands back the bytes we’ve allocated back to the program. While this program here would have continued to run again and again just fine, other programs may not have, so ensuring that we free that data protects us from segmentation faults.
x—x
Come back next time!
Judson