Strings and structs
Arrays, functions and pointers14 min readLesson 4 of 4
4.1 A string is a char array
C has no built-in string type. A string is a sequence of characters stored in a char array, followed by a null character '\0' that marks the end.
char name[] = "Ada"; creates four bytes: 'A', 'd', 'a', and '\0'. The array size is four, and the visible text is three characters.
char name[10] = "Ada"; reserves ten bytes and fills the unused ones with zeros. The size must leave room for the terminator, so three characters need at least four bytes.
The null character is not the digit '0'. '\0' has the value 0, and '0' has the value 48 on an ASCII system.
#include <stdio.h>
#include <string.h>
int main(void)
{
char name[] = "Ada";
printf("name: %s\n", name);
printf("length: %zu\n", strlen(name));
printf("size: %zu\n", sizeof name);
return 0;
}Output
name: Ada length: 3 size: 4
4.2 Reading and printing strings
printf prints a string with %s and stops at the first null character. puts prints the string and adds a newline.
For input, scanf("%s", name) skips leading white space and stops at the next white space, so it cannot read a full name with a space in it. It also has no length limit, which makes it unsafe with a small buffer.
fgets(name, sizeof name, stdin) reads one line up to the size you give it and keeps the newline when there is room. It accepts spaces and cannot overflow the buffer. A NULL return means the input ended or failed, so check it before using the text.
#include <stdio.h>
#include <string.h>
int main(void)
{
char line[40];
printf("Enter a phrase: "); /* sample input: C is fun */
if (fgets(line, sizeof line, stdin) == NULL) {
return 1;
}
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
line[len - 1] = '\0';
}
printf("You wrote: %s\n", line);
return 0;
}Output
Enter a phrase: You wrote: C is fun
4.3 Length, copy, and compare
strlen counts characters up to the terminator. strcpy(dest, src) copies the characters and the terminator, so dest must have room for both. strncpy takes a size but does not always add a terminator, so it needs an explicit write when the source is too long.
strcmp(a, b) returns 0 when the strings are equal, a negative value when a sorts before b, and a positive value when it sorts after. The sign matters; the exact number does not.
None of these operations have an operator form. a == b compares addresses when a and b are arrays or pointers, and a = b does not compile for arrays because an array name is not assignable.
#include <stdio.h>
#include <string.h>
int main(void)
{
char a[] = "apple";
char b[] = "apples";
char copy[10];
strcpy(copy, a);
printf("copy: %s\n", copy);
printf("equal: %d\n", strcmp(a, a) == 0);
printf("a before b: %d\n", strcmp(a, b) < 0);
return 0;
}Output
copy: apple equal: 1 a before b: 1
4.4 Structs group values
A struct is a type you define as a group of named members, and each member can have a different type.
struct Point { int x; int y; }; declares the type. The semicolon after the closing brace is required, and leaving it out is a common early mistake.
struct Point p = {3, 4}; creates a variable and initialises its members in order. The . is the member operator: p.x reads a member and p.x = 7 writes it.
Struct variables can be assigned to each other, and the assignment copies every member. They cannot be compared with ==; compare the members you care about instead.
#include <stdio.h>
struct Point {
int x;
int y;
};
int main(void)
{
struct Point start = {3, 4};
struct Point finish = start;
finish.x = 10;
printf("start: %d,%d\n", start.x, start.y);
printf("finish: %d,%d\n", finish.x, finish.y);
return 0;
}Output
start: 3,4 finish: 10,4
4.5 Structs that hold text
A member can be an array, including a char array for text. The struct then owns that storage, so copying the struct copies the characters. struct Student b = a; gives b its own text.
Use strcpy or snprintf to put new text into the member, and check that the source fits: strcpy(s.name, "Ada") needs a destination at least four bytes long.
An array of structs holds several records, and a loop reaches each one. That is the shape a table of records takes in memory.
#include <stdio.h>
struct Student {
char name[20];
int score;
};
int main(void)
{
struct Student students[3] = {
{"Ada", 91},
{"Grace", 88},
{"Linus", 74}
};
for (int i = 0; i < 3; i++) {
printf("%-6s %d\n", students[i].name, students[i].score);
}
return 0;
}Output
Ada 91 Grace 88 Linus 74
4.6 Pointing at a struct
A function that receives a large struct by value copies every member. Passing a pointer avoids the copy, and the parameter should be const when the function only reads:
void print_item(const struct Item *item)
Inside the function, item->name reaches a member through the pointer. The arrow operator -> combines the dereference and the member access: p->score means (*p).score.
For small structs, such as a pair of int values, passing by value is fine and keeps the code simpler.
#include <stdio.h>
struct Item {
char name[16];
int price;
};
void print_item(const struct Item *item)
{
printf("%s costs %d\n", item->name, item->price);
}
int main(void)
{
struct Item pen = {"Pen", 25};
print_item(&pen);
return 0;
}Output
Pen costs 25
4.7 Practice
- Declare
char name[20] = "Ada";and print the text, the length fromstrlen, and the size fromsizeof. - Define
struct Bookwith achar title[40]member and anint year. Create one book, then print both members. - Put three books in an array, loop over the array, and print the title of the oldest one.
- Change a title through the struct variable with
strcpy, then print it again and check the result.
