C Programming: Palindrome

CS 3410 Spring 2019


Due: Thursday, Feb 28th, 2019 at 11:59:00 PM. Submit your palindrome.c and your compiled binary file testpalindrome.out on CMS.


Overview

For this assignment, you will write a C program that inputs a string from the command line and outputs whether or not the string is a palindrome. A palindrome is a word, phrase, number, or other sequence of characters which reads the same backward or forward. You can assume that the input does not contain any white spaces. Submit the C file on CMS. Your program must compile and run on the course provided VM and /or the CSUG computers.

Specifications

NOTE: On all problem sets in CS3410, submit code which adheres to the C99 standard (for gcc, compile with -std=c99).

To compile the C program you should use the following command, where palindrome.c is the C source file and testpalindrome.out is the executable file.code

$ gcc -Wall -std=c99  palindrome.c  -o testpalindrome.out
If you haven't seen this before, gcc is a compiler that takes your code from C and produces a file of machine code that can be executed (known as an executable). You will learn a lot more about this process later in the couse. If you're curious about gcc try looking at the man page! (man gcc). -Wall means that all warnings will be turned on by the compiler. So when you compile your code, anything that seems a little "off" to the compiler will be shown to you. This doesn't mean they will necessarily cause a problem for you, but they could, and they're good to be aware of. Similar to this is -Werror which turns all warnings into errors. The difference between an error and a warning is that an error will stop your code from being compiled, but a warning will compile and just alert you. There are lots of combinations of flags you can use for compiling, and the command we give you for future projects may look different.

You can run the program with the command:

$ ./testpalindrome.out
You should now see the fruits of your labor! Running the program this way calls your main function. If you've programmed in Java, this is the same concept. We won't do it in this project, but there are ways to pass in arguments on the command line: ./test arg1 arg2 .

For example:

$ ./testpalindrome.out
Input a String: racecar
racecar is a palindrome

$ ./testpalindrome.out
Input a String: Racecar
Racecar is not a palindrome

$ ./testpalindrome.out
Input a String: a
a is a palindrome

$ ./testpalindrome.out
Input a String: whales
whales is not a palindrome

You may assume the input string is less than 100 characters long. The empty string is a palindrome. You may not use any functions from the string.h library. stdio.h is the only library you can use.

Skeleton Code

A skeleton has been provided. Do NOT change any of the print statements. You can change the variable names.

The #include statements always go at the top of the C file as shown in the skeleton code. This makes any functions defined in the given .h file available for use in your .c file. A .h file can include function declarations (but no implementation) and structs (we'll get into those during another assignment). stdio.h is a prebuilt library with functions available for use, similar to string.h or stdlib.h. You can look up the functions in these packages using the man command (for manual) man strlen on the command line. You can also include a .h file that you wrote, but the format to do so is slightly different. In the provided code we used the greater than and less than symbols around the name of the .h file. When you include your own you use quotes: #include "myfile.h".

These packages, along with the man pages, will be very useful for you in this assignment as well as in your future C projects. For this you will need to read input from stdin, and in the future you may need to read from a file. Some functions you might want to know about include (but are not limited to): scanf, fgets and read(). Take a look at the man pages for these to decide what will work best for this assignment.

#include <stdio.h>

int main()
{
    char str[100];
    int i, length=0, flag=0, start, end;

    printf("Input a string: ");


    // Read in input from the command line

    // Find the length of the string.
    // Hint: How do you know when a string ends in C?

    // Check if str is a palindrome.


    if(flag==1)
        printf("%s is not a palindrome.\n", str);
    else
        printf("%s is a palindrome.\n", str);

    return 0;
}

C Basics

Book Resource

If you have a problem or question, you can have a look at Professor Anne Bracy's book: All of Programming. Many common questions are also the top result on your favorite search engine.

Syntax

Most of you probably have not used C before so we want to give you some basic C syntax and a brief overview of some parts of C that will be extra new. A lot of it won't be relevant for this assignment, but we want you to know it's there.

C uses something called pointers. When you create a pointer you store an address in your variable, and that address is a place in memory where there is some other value you care about. You define a pointer using the asterisk in the type: int *pointer or void *pointer. A void pointer means something of any type can be stored in that location in memory, as compared to the int pointer that must point to an int. This difference comes up when we want to do something called dereferencing.

Dereferencing is when you take a pointer and get the value stored at that location in memory. If you have an int pointer (or a char pointer, or a long pointer etc) you can do this no problem, because C knows what type to expect out of it. BUT you cannot dereference a void pointer, because there could be absolutely anything at that address. We derefence pointers using the asterisk. Now you may be thinking, "That's really confusing! You refer to a pointer with an asterisk and you apply an asterisk to a pointer to get a value? How do you tell the difference?" That's a great question, I'm so glad you asked!! There will be an example at the end of this section that should help make it more clear, but in short: an asterisk in the type when you create the variable tells you it's a pointer, while an asterisk later in the code, next to just the variable name itself means dereferencing.

The next piece is the address-of operator. You can think of this as the opposite of dereferencing: The address-of operator is applied to a value and returns the address of that value, meaning it creates a pointer. This is done using the ampersand. The last thing you'll need to know before we wrap this all up into one giant super fun example is that you can have multiple asterisks on a pointer, meaning it is actually a pointer to a pointer! Meaning the value stored in the first pointer is really just another address, and then at that address is the value.

Pointers are probably the most confusing part of C so don't worry if you don't understand! You won't need to worry too much about them for this project, but if you have questions you can bring them to office hours. Memory can also make a lot more sense if you visualize it, so try drawing yourself a picture to go with this example (or ask a TA to draw one for you).

Example time! This is just a few lines of C code without any function declarations, so if you copy-paste this into a .c file it won't run on it's own, but if you want to try it out, mimic the skeleton code above and run it! (side note: comments in C are done with double slash: //)


void *ptr; //creates a void pointer, but doesn't put anything in it
int *num; //same as above, but an int pointer
int five = 5; //we'll use this in a second
double three = 3.0;

ptr = &three; //Used the address-of operator to get the address of the variable three and store it in ptr
num = &five; //similar to the above line

printf("value of num: %d\n", *num); //dereferences num and gets the value at the address (will print 5)

//we can't derefence ptr because it is a void *
//we can still print the address itself if we wanted to
printf("address stored in ptr: %p\n", ptr); //print values will vary

Strings

Fact: strings are actually arrays of characters (type char *str). Strings are delimited by the null character \0 in an array. Therefore, string arrays are actually char **arr, which is an array of character arrays. You can instantiate a string in the following way: char string[] = "this is a string";. But that's not the only way! You can specify the size of the array: char str[100]= "here's another string";. You can use the curly brackets: char hello[] = {'W', 'o', 'r', 'l', 'd', '\0'}. Note that if you choose this option it's on you to include the null terminator character. You can access fields this way: char letter = string[3]; In this example letter would now be equal to s.

Remember all the way at the beginning of this section when we said the type of the string was char *str? But then all those examples we just went through didn't have any asterisks at all? That's because of the square bracket syntax []. If you use the square brackets you are creating an array containing your string, and if you use the asterisk you are creating a pointer to the address of your string. Here's an example: char *ptr = "these are words";. It's ok if that difference is a little confusing because don't have to worry too much about pointers for this assignment (read: don't worry about them at all). We just want you to have a brief explanation. The main difference you might come across in the assignment is if you use sizeof (keep reading the next paragraph for those deets).

The null character is important to keep in mind when calculating the length of your string, because the size of the char array does not equal the length of the string and the null character is one of the reasons. There is a function that you can not use for this assignment (because we want you to learn more, not because we're trying to torture you) called strlen() in the string.h library. This function returns the number of characters from the start of the char array up to and not including the null terminator character.

The sizeof operator is available to you for this project, but be careful using it! sizeof will return the actual amount of space the string takes up in memory, which will include the null terminator. Note: You can not always just get the string length from the size of the array. Think about the str array we created earlier. The string we put in there only had a length of 21, but the size of array is still 100. And going back to our other syntax, using the asterisk instead of the square brackets, sizeof will definitely not give you anything close to the length of the string. Using the example from the last paragraph, the length of the string pointed to by ptr is 15. However, the size of ptr is 8, the same as sizeof(void*) because ptr itself is just a pointer, not an array. Again, don't worry too much about pointers for this assignment.