munotes®

Characters and Character Strings

Chapter Twelve

Syllabus topic 1, "Introduction: Algorithms, History of C, Structure of C Program. Program Characteristics, Compiler, Linker and preprocessor, pseudo code statements and flowchart symbols, Desirable program characteristics. Program structure. Compilation and Execution of a Program, C Character Set, identifiers and keywords, data types and sizes, constants and its types, variables, Character and character strings, typedef, typecasting"

Pages 55 to 59 of 222

In one line

A character is a one-byte integer holding a character's code, and a character string is a run of characters in consecutive memory ending with a character whose value is zero, which is how every part of C tells where the string stops.

Why strings work this way in C

C has no string type. This is a deliberate and slightly shocking design decision, and it follows from what the language was for. A string type would need a length stored somewhere, a decision about how long a string may be, and code to manage it, and C was built to be small enough to fit a 1970s machine and close enough to the hardware to write an operating system.

So C provides the minimum that works: a string is just characters in memory, one after another, and the end is marked by a zero byte. Nothing keeps a length. Nothing checks a bound. Every string function in the library works by walking forward until it meets that zero.

Everything good and everything dangerous about strings in C comes from that one sentence.

A character is a small integer

#include <stdio.h>

int main(void)
{
    char grade = 'A';
    char digit = '7';

    printf("grade as a character: %c\n", grade);
    printf("grade as a number   : %d\n", grade);
    printf("digit '7' is the number %d\n", digit);
    printf("'7' turned into 7 is %d\n", digit - '0');
    printf("the next letter after %c is %c\n", grade, grade + 1);
    return 0;
}
grade as a character: A
grade as a number   : 65
digit '7' is the number 55
'7' turned into 7 is 7
the next letter after A is B

A char is one byte and holds an integer. When you print it with %c the library shows you the character that code stands for; with %d it shows the code. Both are the same byte.

digit - '0' is the standard way of turning the character '7' into the number 7, and it works because the standard guarantees the ten digit characters have consecutive values. It has nothing to do with ASCII in particular, and it is the conversion atoi and scanf do internally.

grade + 1 has type int, not char, because C widens a char to an int before doing arithmetic on it. Printing it with %c narrows it back. Chapter 14 is the rule that does this.

Reading and writing one character

Three ways, and they are not interchangeable.

char c;
scanf("%c", &c);        /* takes the very next character, spaces included  */
scanf(" %c", &c);       /* the leading space skips whitespace first        */
c = getchar();          /* takes the next character; returns int, not char */
putchar(c);             /* writes one character                           */
munotes.in55

Characters and Character Strings

The space in " %c" is the fix for the commonest bug in first-semester C. After reading a number with scanf("%d", &n), the newline you pressed is still waiting in the input, so the next "%c" reads the newline instead of what the user types. A leading space in the format tells scanf to skip any amount of whitespace first.

getchar returns an int, not a char, and that is not an oversight. It has to be able to return every possible character and the separate value EOF for "there is no more input", and a char has no spare value to use. Store it in an int.

A string is an array of characters with a zero on the end

#include <stdio.h>

int main(void)
{
    char name[] = "Anita";

    printf("the string is %s\n", name);
    printf("it needs %zu bytes\n", sizeof name);
    printf("byte by byte: ");
    for (int i = 0; i < (int) sizeof name; i++) {
        printf("[%d]=%d ", i, name[i]);
    }
    printf("\n");
    printf("as characters: ");
    for (int i = 0; i < (int) sizeof name - 1; i++) {
        printf("%c", name[i]);
    }
    printf("\n");
    return 0;
}
the string is Anita
it needs 6 bytes
byte by byte: [0]=65 [1]=110 [2]=105 [3]=116 [4]=97 [5]=0
as characters: Anita

Five letters, six bytes. The sixth is the zero the compiler added, and printing the bytes as numbers is the clearest way to see it. name[5] is 0, and it is not the character '0', whose code is 48. The difference between '\0' and '0' is worth a mark on its own.

char name[] = "Anita"; lets the compiler count for you, and it counts the terminator. Writing the size yourself is where mistakes live.

The four ways to declare a string, and which is which

char a[] = "Anita";       /* array of 6, contents copied in, changeable   */
char b[20] = "Anita";     /* array of 20, first 6 used, rest set to zero  */
char c[6] = "Anita";      /* exactly fits: 5 letters and the terminator   */
const char *d = "Anita";  /* a POINTER to the literal, NOT changeable     */

char e[5] = "Anita"; compiles in C and gives you an array of five characters with no terminator. It is not a string, and passing it to printf("%s") walks off the end of the array. Count the terminator.

The difference between a and d is the one that matters and it is not cosmetic:

  • a is an array. The characters are yours, in your memory, and a[0] = 'B'; is legal.
  • d points at the string literal itself, which the program may have put in read-only memory. d[0] = 'B'; compiles without const and may crash.
munotes.in56

Characters and Character Strings

The rule: if you mean to change the text, use an array. If you only mean to read it, use const char *.

What goes wrong, and it is always the terminator

1. The array is one byte too small. char s[5] = "Anita"; has no room for the zero. char s[6] or char s[] is correct.

2. strlen and sizeof are different numbers. strlen counts the characters up to the terminator. sizeof gives the size of the array, terminator and all unused bytes included.

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

int main(void)
{
    char a[] = "Anita";
    char b[20] = "Anita";

    printf("a: strlen %zu, sizeof %zu\n", strlen(a), sizeof a);
    printf("b: strlen %zu, sizeof %zu\n", strlen(b), sizeof b);
    return 0;
}
a: strlen 5, sizeof 6
b: strlen 5, sizeof 20

3. scanf("%s") stops at the first space. Reading "Anita Desai" with %s gives you "Anita" and leaves the rest waiting. Use fgets when the input has spaces in it.

4. scanf("%s") has no idea how big your array is. It writes as many characters as arrive. scanf("%19s", name) for a char name[20] is the safe form: 19 characters and the terminator.

5. A string cannot be compared with ==. if (a == b) compares two addresses, not two texts, and is almost always false. strcmp is chapter 37.

6. A string cannot be copied with =. a = b; does not compile for arrays. strcpy is chapter 37.

Reading a whole line safely

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

int main(void)
{
    char line[40];

    if (fgets(line, (int) sizeof line, stdin) != NULL) {
        line[strcspn(line, "\n")] = '\0';     /* cut the newline off */
        printf("you typed [%s], %zu characters\n", line, strlen(line));
    }
    return 0;
}
Anita Desai
you typed [Anita Desai], 11 characters

fgets takes the size of the array, so it cannot overflow it, and it keeps the newline you pressed. The strcspn line finds the position of the first newline and puts a terminator there, which is the ordinary way of trimming it. If there was no newline, strcspn returns the length and the assignment is harmless.

What this does NOT mean

A string is not a data type. There is no string in C. There is an array of char with a convention about its last byte, and a library of functions that all obey that convention.

'\0' is not '0', and neither is "0". '\0' has the value 0. '0' has the value 48. "0" is two bytes: 48 and then 0.

sizeof is not the length. For char b[20] = "Anita";, sizeof b is 20 and strlen(b) is 5.

A char is not guaranteed to be signed. Whether plain char can hold a negative value is left to the implementation. It matters only if you store numbers in a char, in which case write signed char or unsigned char.

munotes.in57

Characters and Character Strings

gets does not exist any more, and never use it. It reads a line with no idea of the size of your array. It was removed from the language in C11, and any book that shows it is describing a language that no longer has it. fgets is the replacement.

Quick revision

  • A char is one byte holding an integer: the character's code.
  • %c prints the character, %d prints the code.
  • c - '0' converts a digit character to its value, guaranteed by the standard.
  • A string is characters in consecutive memory ending in '\0'.
  • "Anita" occupies 6 bytes. n characters need n + 1.
  • char s[] = "..." lets the compiler count, terminator included.
  • char s[] is changeable memory of yours; const char *s points at a literal you must not change.
  • strlen counts to the terminator; sizeof gives the whole array.
  • getchar returns int, so that EOF is distinguishable.
  • scanf(" %c", &c): the leading space skips the leftover newline.
  • scanf("%s") stops at whitespace and does not know your array size. Use %19s or fgets.
  • Never gets. It was removed from the language.

Test yourself

1. How many bytes does char s[] = "Hello"; occupy, and what is in the last one?

Six. The last holds '\0', the value zero, which marks the end of the string.

2. What is the difference between 'A', "A" and '\0'?

'A' is a character constant of type int with the value 65 in ASCII. "A" is a string literal: two bytes, 65 and 0. '\0' is the null character, value 0.

3. char s[5] = "Hello"; compiles. Why is it wrong?

There is no room for the terminator, so s holds five characters and is not a string. Any library function or %s conversion will read past the end of the array.

4. Why does getchar return an int?

So that it can return any character value and also the distinct value EOF, which means there is no more input. A char has no value left over to mean that.

5. After scanf("%d", &n), why does scanf("%c", &c) seem to read nothing?

Because the newline from the Return key is still in the input, and %c reads it. Write scanf(" %c", &c): the leading space skips whitespace first.

6. Your program must read a full name with a space in it. Which input function, and why not the other?

fgets, because scanf("%s") stops at the first whitespace character and would read only the first word.

munotes.in58

Characters and Character Strings

7. For char b[20] = "Anita";, what are strlen(b) and sizeof b?

5 and 20.

What can be asked on this, and how to answer it

"What is a string in C? How is it stored?" A string is an array of characters terminated by the null character '\0'. It is stored as the characters in consecutive bytes with the terminator after the last one, so n characters need n + 1 bytes, and there is no separate length: every library function finds the end by looking for the terminator.

"What is the significance of the null character?" It marks the end of a string. Every string literal gets one automatically, and the library's string functions depend on it. Without one, a function walks past the end of the array, which is undefined behaviour.

"Distinguish between a character and a string." A character is one byte in single quotation marks holding a code; a string is an array of characters in double quotation marks with a terminator. 'A' is one byte's worth of value; "A" is two bytes.

"Distinguish between strlen and sizeof for a character array." strlen is a library function that counts characters up to the terminator, at run time. sizeof is a compile-time operator giving the size of the whole array including the terminator and any unused space.

"Write a program to read a string and display it." Use fgets with sizeof of the array, trim the newline, and print with %s. Say in one line why not gets: it has no bound and was removed from the language in C11.

munotes.in59

The rest of this subject

These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.

Report or request
Done!