Skip to main content

Strings

Exercises

Question 13.1

The following function calls supposedly write a single new-line character, but some are incorrect. Identify which calls don't work and explain why.

  1. printf("%c", '\n');
  2. printf("%c", "\n");
  3. printf("%s", '\n');
  4. printf("%s", "\n");
  5. printf('\n');
  6. printf("\n");
  1. putchar('\n');
  2. putchar("\n");
  3. puts('\n');
  4. puts("\n");
  5. puts("");

Before we move onto checking the correct functions, I'll first put the function signature for printf, putchar, and puts:

  • printf's signature: int printf(const char * __restrict, ...) __printflike(1, 2);
  • putchar's signature: int putchar(int c);
  • puts's signature: puts(const char *s);

Now, this is my expected results with their reasonings:

  1. printf("%c", '\n'); → This function call should work as intended - it should print out a new-line character - as %c format specifier expects a character and any character inside a single quote, '\n' in this example, is a character (note that 'ab' is not a character as it has a and b inside the single quote).
  2. printf("%c", "\n"); → This function call should not work, since the format specifier expects a character (%c) but was given a string. Although it may seem like "\n" is just a character, but since the new-line character is wrapped around double quotes, there is an extra character at the end, the null character (\0).
  3. printf("%s", '\n'); → This function call is a bit tricky, as the format specifier requires a string (%s), but a character is given ('\n'). If I had to guess, this should not work.
  4. printf("%s", "\n"); → This function call should work as well since the format specifier requires a string and a string is provided.
  5. printf('\n'); → This function call should not work, as the first argument of printf requires a const char * (ignoring __restrict as I'm not aware of what it does), instead a character is passed as the first argument.
  6. printf("\n"); → This function will work - should be obvious.
  7. putchar('\n'); → This function will work as putchar expects a character and a character is given to it. If you're confused as to how it works even though putchar expects an int type as it's argument, remember that a character is indeed an integer, of 8 bits (Recall how the character 'A' has ASCII code of 65, ' ' has ASCII code of 32, and so on).
  8. putchar("\n"); → This function call should not work as the argument provided to putchar is a string. Since putchar only takes integer as an argument, it will not work as "\n" not only has the new-line character, but a null character after it as well.
  9. puts('\n'); → This function call will not work as puts require a const char * as its argument, but a character was provided.
  10. puts("\n"); → This function call will work as a string (char *) is provided. NOTE: This does work, but puts also adds a new-line character after the string has been printed, so it probably prints two new-line character.
  11. puts(""); → This function call will work as described in above. Since puts automatically adds a new-line character at the end, this will work too.

Now, after testing:

  1. printf("%c", '\n'); → Works as intended.
  2. printf("%c", "\n"); → Printed a random character instead of a new-line character.
  3. printf("%s", '\n'); → Segmentation Fault.
  4. printf("%s", "\n"); → Works as intended.
  5. printf('\n'); → Segmentation Fault.
  6. printf("\n"); → Works as intented.
  7. putchar('\n'); → Works as intended.
  8. putchar("\n"); → Printed a random character instead of a new-line character.
  9. puts('\n'); → Segmentation Fault.
  10. puts("\n"); → Works as intented and described it's caveat.
  11. puts(""); → Works as intended.

Question 13.2

Suppose that p has been declared as follows:

char *p = "abc";

Which of the following function calls are legal? Show the output produced by each legal call, and explain why the others are illegal.

  1. putchar(p);
  2. putchar(*p);
  3. puts(p);
  4. puts(*p);

Before testing it out, I assume the following effects happen during the function call. This is under the fact that p is a pointer to a character variable that points to a string literal "abc" and the null character (\0). When the indirection operator is applied, it returns the first character in the string literal, i.e. 'a'.

  1. putchar(p); → Since we are supplying putchar with a pointer to a character, it does not have the type int, rather it has the type char *. So this should be illegal.
  2. putchar(*p); → As *p returns a character, more precisely, it returns 'a', which is 97 in ASCII value, this function call should be legal.
  3. puts(p); → Since puts expects a const char *, and p is a char * variable, this function call should be legal.
  4. puts(*p);*p, as described earlier, returns a character, not a pointer to a character, so this function call should be illegal.

Now, after testing:

  1. putchar(p); → Printed a random character.
  2. putchar(*p); → Works as intended.
  3. puts(p); → Works as intended.
  4. puts(*p); → Segmentation Fault.

Question 13.3

Suppose that we call scanf as follows:

scanf("%d%s%d", &i, s, &j);

If the user enters 12abc34 56def78, what will be the values of i, s, and j after the call? (Assume that i and j are int variables and s is an array of characters.)

From my initial assumption, the call to scanf will work as follows:

  • It reads 1, 2, and a. Since a is not a digit, scanf puts a back in the input stream and stores 12 into i
  • It reads a, b, c, 3, 4, and ' ' (space). Since space is encountered and there is no space in the format specifier, the scanf will stop reading more characters from the input stream (even though there are more characters in the input string) and stores "abc34" in the string s.

After testing:

I forgot that scanf will ignore all the leading spaces. Since the space between 4 and 5 in the input is present, and scanf will stop reading for s when it encounters a space and when it reads for the next variable, i.e. j, it first encounters the space that was left behind by the last scan of scanf. As stated, scanf will ignore the leading space until it finds a non-space character - digit in the case - and stores it in the variable.


Question 13.4

Modify the read_line function in each of the following ways:

  1. Have it skip white space before beginning to store input characters.
  2. Have it stop reading at the first white-space character. Hint: To determine whether or not a character is white space,call the isspace function.
  3. Have it stop reading at the first new-line character, then store the new-line character in the string.
  4. Have it leave behind characters that it doesn't have room to store.
#include <stdio.h>
#include <ctype.h>

#define STR_LEN 100

int read_line_1 (char *char_arr);
int read_line_2 (char *char_arr);
int read_line_3 (char *char_arr);
int read_line_4 (char *char_arr, int arr_size);

Question 13.5

(a) Write a function named capitalize that capitalizes all letters in its argument. The argument will be a null-terminated string containing arbitrary characters, not just letters. Use array subscripting to access the characters in the string Hint: Use the toupper function to convert each character to upper-case.

(b) Rewrite the capitalize function, this time using pointer arithmetic to access the characters in the string.

#include <stdio.h>
#include <ctype.h>

#define STR_LEN 100

char *capitalize (char *s);

int main (void) {

Question 13.6

Write a function named censor that modifies a string by replacing every occurrence of foo by xxx. For example, the string "food fool" would become "xxxd xxxl". Make the function as short as possible without sacrificing clarity.

#include <stdio.h>

#define STR_LEN 100

void censor (char *char_array);

int main (void) {

char c_arr[STR_LEN + 1];

Question 13.7

Suppose that str is an array of characters. Which one of the following statements is not equivalent to the other three?

  1. *str = 0;
  2. str[0] = '\0';
  3. strcpy(str, "");
  4. strcat(str, "");

The last option, i.e. option (d) is the not equivalent to other three. As str is an array of character, str will be a pointer to the first character in the array (str[0]), and storing 0 is equivalent to storing the null character (the ASCII code for the null character is 0). Option (b) works in similar manner as option (a). For option (c), as str copies "", which is an empty string literal that has one character - the null character - even though the string is empty (see #8 of Chapter's note), so strcpy will copy a string with only a null character to str. For option (d), if there is already some characters in the string str, then strcat will append the null character at the end of the str array.


Question 13.8

What will be the value of the string str after the following statements have been executed?

strcpy(str, "tire-bouchon");
strcpy(&str[4], "d-or-wi");
strcat(str, "red?");

From the inital observation of the code, it seems like the sequence of execution will be like this:

  • When the first strcpy is called, the character array (string) str is modified by adding the string tire-bouchon in the string.
  • When the second strcpy is called, since the first agrument - a char * which stores the string in the second argument - is a pointer to the element 4 of the character array str, the string is presumably modified starting from that position. From the previous call to strcpy, str[4] holds the '-' character, and &str[4] gives the pointer/address to that character. Since strcpy is a function that is defined in string.h header, the implementation might be diffferent. The one I could think of is:
    • The strcpy function might only modify the characters that is the length of the src (the second argument), which is of length 7 in this case. This either modifies the array and will have "tired-or-win". It's also possible that the null character at the end of src will be copied to the dst (the first argument). In this case, the array will have the string stored as "tired-or-wi"
  • The strcat will append the "red?" string to the str function. The final output I could think of is: "tired-or-wired?"

The expected output matched the program's output.


Question 13.9

What will be the value of the strings after the following statements have been executed?

strcpy(s1, "computer");
strcpy(s2, "science");
if(strcmp(s1,s2) < 0)
strcat(s1, s2);
else
strcat(s2, s1);
s1[strlen(s1)-6] = '\0';

The flow of the program will be as follows:

  • The first call to strcpy will assign the string "computer" to s1.
  • The second call to strcpy will assign the string "science" to s2.
  • The strcmp function is called with first argument as s1 ("computer"), and the second as s2 ("science"). Since the character 'c' is numerically less than the character 's' (in ASCII code), the result of strcmp will be less than 0. So the block inside the if statement is executed.
  • The call to strcat will append the content of s2 into the string s1, effectively making the string s1 contain "computerscience"
  • The last statement has the use of strlen(s1) which will return 15. 15 - 6 will result in 9. So, the effective result will be s1[9] = '\0'. As the element 9 points to the character 'c' and replaces it with the null character.

If s1 is printed, it should output "computers".

The expected output matched the program's output.


Question 13.10

The following function supposedly creates an identical copy of a string. What's wrong with the function?

char *duplicate(const char *p)
{
char *q;

strcpy(q, p);
return q;
}

Although the code seems to work properly, there are indeed a few issues with the program fragment. Firstly, the pointer variable q is declared but not initialized. This means that q can currently point to any object in the memory. Even worse, q is assigned the string containing in p, so we don't know where q is modifying the object at. Also, since q is a pointer variable declared inside the function duplicate, the duration of the variable is automatic, i.e. after the function block ends, the variable is no longer available.

After making a program, the warning generated were regarding the uninitialized variable q that was on the line return q as the line char *q is a declaration, not an initialization. When executing the code, I observed the "trace trap" or SIGTRAP error.


Question 13.11

The Q&A section at the end of this chapter shows how the strcmp function might be written using array subscripting. Modify the function to use pointer arithmetic instead.

#include <stdio.h>

#define STR_LEN 100

int my_strcmp (char *first_string, char *second_string);

int main (void) {

char str_1[STR_LEN + 1];

Question 13.12

Write the following function:

void get_extension(const char *file_name, char *extension);

file_name points to a string containing a filename. The function should store the extension on the filename in the string pointed to by extension. For example, if the filename is "memo.txt", the function will store "txt" in the string pointed to by extension. If the file name doesn't have an extension, the function should store an empty string (a single null character) in the string pointed to by extension. Keep the function as simple as possible by having it use the strlen and strcpy functions.

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

#define FILE_NAME_LEN 100
#define EXTENSION_LEN 5

void get_extension (const char *file_name, char *extension);

int main (void) {

Question 13.13

Write the following function:

void build_index_url(const char *domain, char *index_url);

domain points to a string containing an Internet domain, such as "knking.com". The function should add "http://www." to the beginning of this string and "/index.html" to the end of the string, storing the result in the string pointed to by index_url. (In this example, the result will be "http://www.knking.com/index.html"). You may assume that index url points to a variable that is long enough to hold the resulting string. Keep the function as simple as possible by having it use the strcat and strcpy functions.

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

#define DOMAIN_LEN 20
#define IURL_LEN 50

void build_index_url (const char *domain, char *index_url);
void s_build_index_url (const char *domain, char *index_url);

Question 13.14

What does the following program print?

#include <stdio.h>

int main(void)
{
chars[] = "Hsjodi", *p;

for(p = s; *p; p++)
--*p;
puts(s);
return 0;
}

The following program is a bit tricky, in a sense that the expression --*p is not something that I'm familiar with.

Apart from that, the following sequences will occur during the program execution:

  • s holds the string "Hsjodi", along with a null character. Also, a character pointer variable p is declared.
  • In the for loop, the first expression will assign a pointer to the first element in s to the pointer variable p. the second expression - which is the conditional expression - is just *p, which will be 0 only when it encounters the null character '\0', and terminates from the for loop, and the third expression, which increments the pointer variable p which will point to another element in the string s.
  • Inside the loop, only one operation is performed, which is the --*p. From what I can guess, as -- is right associative and will check for an lvalue, but since *p is after the -- operator, this will decrement the value stored in *p. (Although this does not seem to work like we used the *p++, where p is incremented and then only indirection operator is applied.) So, if *p holds the character 'H', the operation will modify the character to 'G'. In similar manner, the other characters in the strings will be 'r', 'i', 'n', 'c', and 'h'.
  • puts will output the string "Grinch".

The expected output matched the program's output.


Question 13.15

Let f be the following function:

int f(char *s, char *t)
{
char *p1, *p2;
for (p1 = s; *pl; pl++) {
for (p2 = t; *p2; p2++)
if (*pl == *p2) break;
if (*p2 == '\0') break;
}
return pl - s;
}
  1. What is the value of f("abcd", "babc")?
  2. What is the value of f("abcd", "bcd")?
  3. In general, what value does f return when passed two strings s and t?

Lets start with the third question. For (c), the following things will happen when f is invoked,

  • The pointer to character variable p1 and p2 points to s and t respectively.
  • s is the expression for outer loop and t is the expression for inner loop.
  • Both loops will not terminate unless the null character is encountered (break is not taken into consideration)
  • The inner loop only has one conditional statement. If the object that is pointed by p1 is same as the object that is pointed by p2, the flow will break out of the inner loop. If not, the loop continues till it encounters the null character.
  • Before re-iterating the outer loop, the program checks if *p2 is a null character, if yes, then it will break out of outer loop.
  • If not, the loop is re-iterated. this will make p point to the next element in the string s and the steps are re-done.

In essence, what this program does is, it checks the characters in the string s, to the characters in t. If the character in s is present is present in t, then the program checks for the next character in the string and in the string t. If the program fails to find the character in the string t, then the loop terminates and the difference of the p1 - s is returned. This difference represents the characters that are present in the string t. For instance, if s contains "abc", and t contains "cbs", then the function will return 0 as it fails to encounter the character 'a' in the string t. If the string s contains "abc" and string t contains "cxa", then the function will return 1 as it can locate c, but not x - which terminates the loop from the outer loop's if statement.

Moving on to the previous two questions:

  1. The function will locate 'a', 'b', and 'c' from the string "babc", but fails to locate 'd'. Before the return statement is executed, p1 will be a pointer that points to 'd'. So p1 - s will be 3.
  2. The function will not locate 'a' in the string "bcd". So, the function will return 0.

Question 13.16

Use the techniques of Section 13.6 to condense the count_spaces function of Section 13.4. In particular, replace the for statement by a while loop.

#include <stdio.h>

#define STR_LEN 100

int count_spaces (const char *message);

int main (void) {

char message[STR_LEN + 1];

Question 13.17

Write the following function:

bool test_extension(const char *file_name,
const char *extension);

file_name points to a string containing a file name. The function should return true if the file's extension matches the string pointed to by extension, ignoring the case of letters. For example, the call test_extension("memo.txt", "TXT") would return true. Incorporate the "search for the end of a string" idiom into your function. Hint: Use the toupper function to convert characters to upper-case before comparing them.

#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>

#define FILE_NAME_LENGTH 100

bool test_extension (const char *file_name, const char *extension);

int main (void) {

Question 13.18

Write the following function:

void remove_filename(char *url);

url points to a string containing a URL (Uniform Resource Locator) that ends with a file name (such as "http://www.knking.com/index.html"). The function should modify the string by removing the file name and the preceding slash. (In this example, the result will be `"http://www.knking.com".) Incorporate the "search for the end of a string" idiom into your function. Hint: Have the function replace the last slash in the string by a null character.

#include <stdio.h>

#define URL_LEN 100

void remove_filename (char *url);

int main (void) {

char url[URL_LEN + 1];

Programming Projects

Project 13.1

Write a program that finds the "smallest" and "largest" in a series of words. After the user enters the words, the program will determine which words would come first and last if the words were listed in dictionary order. The program must stop accepting input when the user enters a four-letter word. Assume that no word is more than 20 letters long. An interactive session with the program might look like this:

Enter word: dog
Enter word: zebra
Enter word: rabbit
Enter word: catfish
Enter word: walrus
Enter word: cat
Enter word: fish

Smallest word: cat
Largest word: zebra

Hint: Use two strings named smallest_word and largest_word to keep track of the "smallest" and "largest" words entered so far. Each time the user enters a new word, use strcmp to compare it with smallest_word; if the new word is "smaller," use strcpy to save it in smallest_word. Do a similar comparison with largest_word. Use strlen to determine when the user has entered a four-letter word.

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

#define WORD_SIZE 20

int main (void) {

char word[WORD_SIZE + 1];
char smallest_word[WORD_SIZE + 1];

Project 13.2

Improve the remind.c program of Section 13.5 in the following ways:

  1. Have the program print an error message and ignore a reminder if the corresponding day is negative or larger than 31. Hint: Use the continue statement.
  2. Allow the user to enter a day, a 24-hour time, and a reminder. The printed reminder list should be sorted first by day, then by time. (The original program allows the user to enter a time, but it's treated as part of the reminder.)
  3. Have the program print a one-year reminder list. Require the user to enter days in the form month/day.
#include <stdio.h>
#include <string.h>

#define MAX_REMIND 50 /* maximum number of reminders */
#define MSG_LEN 60 /* max length of reminder message */
#define DATE_LEN 6
#define TIME_LEN 6
#define DATE_TIME_LEN 15

Project 13.3

Modify the deal.c program of Section 8.2 so that it prints the full names of the cards it deals:

Enter number of cards in hand: 5
Your hand:
Seven of clubs
Two of spades
Five of diamonds
Ace of spades
Two of hearts

Hint: Replace rank_code and suit_code by arrays containing pointers to strings.

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <string.h>

#define NUM_RANKS 13
#define NUM_SUITS 4

Project 13.4

Write a program named reverse.c that echoes its command-line arguments in reverse order. Running the program by typing

reverse void and null

should produce the following output:

null and void

#include <stdio.h>

int main (int argc, char **argv) {

for (int i = argc - 1; i > 0; i--) {
printf("%s ", *(argv + i));
}

printf("\n");

Project 13.5

Write a program named sum.c that adds up its command-line arguments, which are to be integers. Running the program by typing

sum 8 24 62

should produce the following output:

Total: 94

Hint: Use the atoi function to convert each command-line argument from string form to integer form.

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[]) {
int sum = 0;

// if the argument is a floating point in the form of string, i.e. "12.24", the atoi("12.24") will convert it to 12.
for (int i = argc - 1; i > 0; i--) {
sum += atoi(argv[i]);

Project 13.6

Improve the planet.c program of Section 13.7 by having it ignore case when comparing command-line arguments with strings in the planets array.

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

void make_title_case (char *char_arr);

int main (int argc, char **argv) {

Project 13.7

Modify Programming Project 11 from Chapter 5 so that it uses arrays containing pointers to strings instead of switch statements. For example, instead of using a switch statement to print the word for the first digit, use the digit as an index into an array that contains the strings "twenty", "thirty", and so forth.

#include <stdio.h>

int main (void) {

int num;

const char *twenty_to_ninety[] = {"Twenty",
"Thrity",
"Forty",

Project 13.8

Modify Programming Project 5 from Chapter 7 so that it includes the following function:

int compute_scrabble_value(const char *word);

The function returns the SCRABBLE value of the string pointed to by word.

#include <stdio.h>
#include <ctype.h>

#define WORD_LEN 100

int compute_scrabble_value (const char *word);

int main (void) {

Project 13.9

Modify Programming Project 10 from Chapter 7 so that it includes the following function:

int compute_vowel_count(const char *sentence);

The function returns the number of vowels in the string pointed to by the sentence parameter.

#include <stdio.h>
#include <ctype.h>

#define STR_LEN 100

int compute_vowel_count (const char *sentence);

int main (void) {


Project 13.10

Modify Programming Project 11 from Chapter 7 so that it includes the following function:

void reverse_name(char *name);

The function expects name to point to a string containing a first name followed by a last name. It modifies the string so that the last name comes first, followed by a comma, a space, the first initial, and a period. The original string may contain extra spaces before the first name, between the first and last names, and after the last name.

#include <stdio.h>
#include <ctype.h>

#define NAME_LEN 100

void reverse_name (char *name);

int main (void) {

Project 13.11

Modify Programming Project 13 from Chapter 7 so that it includes the following function:

double compute_average_word_length(const char *sentence);

The function returns the average length of the words in the string pointed to by sentence.

#include <stdio.h>
#include <ctype.h>

#define MSG_LEN 100

double compute_average_word_length (const char *sentence);

int main (void) {

Project 13.12

Modify Programming Project 14 from Chapter 8 so that it stores the words in a two-dimensional char array as it reads the sentence, with each row of the array storing a single word. Assume that the sentence contains no more than 30 words and no word is more than 20 characters long. Be sure to store a null character at the end of each word so that it can be treated as a string.

#include <stdio.h>
#include <ctype.h>

#define WORD_COUNT 30
#define CHARACTER_COUNT 20

int main (void) {

char sentence[WORD_COUNT][CHARACTER_COUNT];

Project 13.13

Modify Programming Project 15 from Chapter 8 so dial it includes the following function:

void encrypt(char *message, int shift);

The function expects message to point to a string containing the message to be encrypted; shift represents the amount by which each letter in the message is to be shifted.

#include <stdio.h>
#include <ctype.h>

#define MSG_LENGTH 80

void encrypt (char *message, int shift);

int main (void) {

Project 13.14

Modify Programming Project 16 from Chapter 8 so that it includes the following function:

bool are_anagrams(const char *word1, const char *word2);

The function returns true if the strings pointed to by word1 and word2 are anagrams.

#include <stdio.h>
#include <ctype.h>
#include <stdbool.h>

#define WORD_LEN 100

bool are_anagrams (const char *word1, const char *word2);

int main (void) {

Project 13.15

Modify Programming Project 6 from Chapter 10 so that it includes the following function:

int evaluate_RPN_expression(const char *expression);

The function returns the value of the RPN expression pointed to by expression.

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#define STACK_SIZE 120
#define EXPRESSION_SIZE 100

// external/global variable declaration and initialization.
int contents[STACK_SIZE] = {0};

Project 13.16

Modify Programming Project 1 from Chapter 12 so that it includes the following function:

void reverse(char *message);

The function reverses the string pointed to by message. Hint: Use two pointers, one initially pointing to the first character of the siring and the other initially pointing to the last character. Have the function reverse these characters and then move the pointers toward each other, repeating the process until the pointers meet.

#include <stdio.h>

void reverse (char *message);

#define MSG_SIZE 50

int main (void) {

char message[MSG_SIZE];

Project 13.17

Modify Programming Project 2 from Chapter 12 so that it includes the following function:

bool is_palindrome(const char *message);

The function returns true if the string pointed to by message is a palindrome.

#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>

#define MSG_SIZE 50

bool is_palindrome (char *message);

int main (void) {

Project 13.18

Write a program that accepts a date from the user in the form mm/dd/yyyy and then displays it in the form month dd, yyyy, where month is the name of the month:

Enter a date (mm/dd/yyyy): 2/17/2011
You entered the date February 17, 2011

Store the month names in an array that contains pointers to strings.

#include <stdio.h>

void display_date (const char *date);

int main (void) {

int month, day, year;
char date_format[11];