C言語入門|11章の練習問題

11章では、C言語における 文字列の本質(C文字列文化) を学びました。
特に重要なのは、

  • C の文字列は char 配列+終端文字¥0(ヌル文字) の組み合わせであること。
  • 文字列操作関数はすべて ¥0 が現れるまで処理する こと。
  • 文字列リテラルと char 配列では 書き換え可否が異なる
  • ヒープ上に必要なサイズぴったりの文字列バッファを確保する。
  • ポインタをずらすと、違う場所から文字列として扱えること。
  • calloc / malloc の違いと、free の扱い方。

これらは C言語の実務でも頻繁に登場し、理解できると一気に中級者へステップアップできます。

ここでは、文字列操作命令の挙動や、配列とヒープのメモリ構造を図表を使ってわかりやすく解説し、その後で「練習問題+解答例と解説」を4問出題します。

C文字列の特徴 — まずはイメージで理解しよう

char 配列のメモリイメージ(例:hello)

インデックスASCIIアドレス(例)
a[0]'h'1041000
a[1]'e'1011001
a[2]'l'1081002
a[3]'l'1081003
a[4]'o'1111004
a[5]0 (終端)01005

ポイント

  • 必ず最後に¥0 が付く
  • 文字列の長さは「¥0を含まない文字数」
  • 配列のサイズ=長さ+1文字分の¥0 が必要

文字列操作命令(標準関数)の概要

関数名書式何をする?¥0 の扱い
strlenstrlen(s)文字列 s の長さを返す¥0 はカウントしない
strcmpstrcmp(a, b)文字列 a と b を比較し、同じなら 0¥0 が出るまで比較
strcpystrcpy(dst, src)src を dst にコピー¥0 までコピー
strcatstrcat(dst, src)dst の末尾に src を連結連結後に新しい ¥0 を付加

11章の練習問題

【練習11-1】(標準文字列関数の理解)

次の空欄(ア)〜(エ)に入る語句を答えてください。

関数名動作¥0 の扱い
strlen文字列の長さを調べる¥0 は(ア)
strcmp2つの文字列を(イ)比較する¥0 まで比較
strcpy(ウ)¥0 までコピー
(エ)文字列を連結する末尾に ¥0 を付ける

【解答例】

(ア)含めない
(イ)辞書順で
(ウ)文字列全体をコピーする
(エ)strcat

【練習11-2】(strlen・strcmp・動的メモリ・連結)

次の文字列があるとします。

char a[] = "C Lang ";
char b[] = "Practice";

以下の動作を行うプログラムを作成してください。

  1. a と b の長さを表示する。
  2. a と b が同じ内容か比較する。
  3. 連結後の文字列を格納できるちょうどの領域をヒープに確保する。
  4. a のあとに b を連結してヒープ領域に格納する。
  5. メモリを解放する。

【解答例】(フルコード)

プロジェクト名:11-15-1 ソースファイル名: sample11-15-1.c

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

int main(void)
{
    char a[] = "C Lang ";
    char b[] = "Practice";

    printf("len(a) = %lu\n", strlen(a));
    printf("len(b) = %lu\n", strlen(b));

    if (strcmp(a, b) == 0) {
        printf("同じ内容です\n");
    } else {
        printf("内容は異なります\n");
    }

    size_t size = strlen(a) + strlen(b) + 1;
    char* c = malloc(size);

    strcpy(c, a);
    strcat(c, b);

    printf("連結結果:%s\n", c);

    free(c);
    return 0;
}

【練習11-3】(ポインタをずらして部分文字列を書く)

次のプログラムについて設問に答えてください。

プロジェクト名:11-15-2 ソースファイル名: sample11-15-2.c

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

int main(void)
{
    char x[] = "ABCDEFG";
    char y[] = {10, 11, 12, 13, 14, 15};
    char* heap = calloc(20, sizeof(char));
    char* p = heap + 6;

    p[0] = 'X';
    p[1] = 'Y';

    free(heap);
    return 0;
}

【条件】
x の先頭アドレスは 4000、y は 5000、heap は 6000 とする。

(1)行番号 12 まで実行されたとき、x・y・p の「使用中領域/使用可能領域」を番地で示してください。

【解答】

配列・ポインタのメモリイメージ(表)

変数使用中領域使用可能領域
x4000〜4007(ABCDEFG¥0)5000〜5005
y5000〜5005(値 10〜15)5000〜5005
p(= heap+6)使用中:6006〜6007('X','Y')heap の確保範囲:6000〜6019

(2)9行目を char* p = y + 4; として文字列 p を表示した場合の動作を答えてください。

【解答】

y は文字列ではなく単なる数値配列なので、
p を文字列として printf("%s", p); のように扱うと、
¥0 が存在しないため暴走し、未定義動作になる

【練習11-4】(strcpy と部分書き換えの注意点)

次のプログラムを読んで問に答えてください。

プロジェクト名:11-15-3 ソースファイル名: sample11-15-3.c

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

int main(void)
{
    char s[] = "computer";
    char* t = s + 4;

    strcpy(t, "BOX");

    printf("%s\n", s);
    return 0;
}

(1)出力結果を答えてください。

(2)このコードの危険性を説明してください。

【解答例】

(1)出力結果

compBOX

【理由】
t は s[4]('u' の位置)を指すため、そこから "BOX" を上書き。

(2)危険性

  • strcpy は終端 ¥0 までコピーするため、
    元の配列サイズを超えて書き込む可能性があり、バッファオーバーフローが起こる
  • 今回はたまたま収まったが、実務では非常に危険。