はじめに
この記事について
「C言語の基礎を学ぼう」をテーマに、自身の知識 + α をアドベントカレンダーにまとめます。
25日間でC言語をマスターしよう - Qiita Advent Calendar 2025 - Qiita
こんな方を対象としています
-
コンピュータがプログラムをどのように動かしているか知りたい/知らない方
-
プログラミングをしてみたい方
-
C言語初心者の方
キーワード
-
文字列
-
文字列リテラル
説明
文字列
文字列の扱い方には2通りの方法があります。
-
char型の配列
-
文字列リテラル(定数)をポインタで管理
#include <stdio.h>
int main(void) {
// 宣言と同時に配列を初期化する場合、要素数を省略可能
char str1[] = "test1";
char *str2 = "test2";
printf("%s\n", str1); // アドレスを渡す
printf("%s\n", str2); // アドレスを渡す
return 0;
}
test1
test2
文字列リテラルをポインタで管理した場合、 読み取り専用 となります。
文字列を変更することはできません。
文字列をchar型の配列で表現したとき、配列がどのようになっているかを見てみましょう。
#include <stdio.h>
int main(void) {
char str[] = "test";
int i = 0;
// 配列全体のサイズを要素1つのサイズで割る = 要素数
int e = sizeof(str) / sizeof(str[0]);
printf("要素数:%d\n", e);
for (i = 0; i < e; i++) {
printf("%c\n", str[i]);
}
return 0;
}
要素数:5
t
e
s
t
test という4文字の文字列を配列に入れた際、配列のサイズは 5 になります。
表示上は何も見えていませんが、配列の5要素目には ヌル文字( \0 ) が格納されます。
配列の要素数を明示的に指定する場合は、 文字列の長さ + 1 を確保するように気を付けましょう。
文字列は 先頭からヌル文字までのメモリの値 です。
プログラムで確認してみます。
#include <stdio.h>
int main(void) {
char str1[] = "test1";
char str2[] = {'t', 'e', 's', 't', '2', '\0'};
char str3[] = {'t', 'e', '\0', 's', 't', '2', '\0'};
printf("%s\n", str1);
printf("%s\n", str2);
printf("%s\n", str3); // ヌル文字まで表示
printf("%s\n", &str3[3]); // 途中のアドレスから表示
return 0;
}
test1
test2
te
st2
練習
1. シーザー暗号
任意のアルファベット大文字のみの文字列をシーザー暗号文にしてみよう。
STORY
VWRUB
ポイント
シーザー暗号とは下記のような暗号です。
-
文字列を1文字ずつ分けて考えます。
-
文字をアルファベット表で任意の数ずらします。
-
例えば3ずらす場合、A → D 、B → E 、C → F 、・・・、Z → C、となります。
ASCIIコードの性質を利用して、文字を数値に変換してから計算するとよいです。
解答例
#include <stdio.h>
int main(void) {
char str[] = "STORY";
int i;
printf("%s\n", str);
for (i = 0; str[i] != '\0'; i++) {
str[i] = 'A' + ((str[i] - 'A' + 3) % 26);
}
printf("%s\n", str);
return 0;
}
STORY
VWRUB
2. UUID
ランダムなUUIDを作成しよう。
7e14cc24-368b-6bfc-5e59-341d01507e668
ポイント
UUIDとは下記の形式です。
-
形式:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
-
xは16進数( 0 ~ f )が入ります。
16進数を文字列に変換する際には sprintf が便利です。
printf と同様の引数に加え、先頭に格納先のアドレスを指定します。
sprintf(格納先charのアドレス, "任意の文字列"....)
ランダムな値は疑似乱数を利用します。
#include <stdio.h>
#include <stdlib.h> // srand()、rand()に必要
#include <time.h> // time()に必要
int main(void) {
int r;
// 疑似乱数生成準備としてシード値を設定する。
// シード値にはプログラム実行時刻を用いることが多い。
// (実行ごとにランダムにするため)
srand((unsigned int)time(NULL));
// rand()で乱数を生成する。
// 生成した値を加工して欲しい乱数にする。
r = rand();
printf("%d", r);
return 0;
}
解答例
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(void) {
char uuid[37];
int i;
srand((unsigned int)time(NULL));
for (i = 0; i < 37; i++) {
if (i == 8 || i == 13 || i == 18 || i == 23) {
uuid[i] = '-';
} else if (i == 37) {
uuid[i] = '\0';
} else {
sprintf(&uuid[i], "%x", rand() % 16);
}
}
printf("%s", uuid);
return 0;
}
7e14cc24-368b-6bfc-5e59-341d01507e668
UUIDは、重複しないIDとしてよく使われます。
おわりに
文字列は配列であるという考え方は大切です。文字列の配列を作成する場合は2次元配列のような形式になります。
文字列を変更しようとしてうまくいかない場合は、文字列リテラルになっていないか確認するとよいです。
参考文献
↓↓↓ はじめてプログラミングを学んだときに読んだ本です ↓↓↓
詳細(プログラミング入門 C言語)|プログラミング|情報|実教出版