英字の大文字や小文字を手作業で判定して変換する必要はありません。
toupper
とtolower
を使えば、文字や文字列を簡単に変換できます。
本記事では1文字の基本から文字列全体の変換方法、初心者がつまずきやすい注意点までを丁寧に解説します。
特にunsigned char
へのキャストと文字列リテラルの扱いは必ず押さえましょう。
toupperとtolowerの基本
大文字・小文字を変換する関数
toupper
は小文字を大文字に、tolower
は大文字を小文字に変換する標準関数です。
変換対象が英字でない場合や、すでに目的の大文字/小文字である場合はそのままの文字が返ります。
以下が関数のプロトタイプです。
int toupper(int c);
int tolower(int c);
返り値はint
型ですが、実質的には1バイトの文字が入っています。
引数はEOF
またはunsigned char
に変換可能な値でなければなりません。
負のchar
をそのまま渡すのは未定義動作のため、常に(unsigned char)
にキャストしてから渡すのが安全です。
次の表に要点を整理します。
関数 | 役割 | 変換対象 | 返り値 |
---|---|---|---|
toupper | 小文字→大文字 | 英字小文字のみ | 変換後の文字(非対象はそのまま) |
tolower | 大文字→小文字 | 英字大文字のみ | 変換後の文字(非対象はそのまま) |
使う前にincludeする
これらの関数は<ctype.h>
で宣言されています。
入出力のために<stdio.h>
もよく一緒に使います。
#include <ctype.h> // toupper, tolower
#include <stdio.h> // printf
ロケールの影響: これらの関数はロケールに依存しますが、標準的な「C」ロケールではASCII英字のみが対象です。
UTF-8の日本語は変換されません(後述します)。
1文字を変換する基本の使い方
1文字の変換は、渡して返ってきた値をchar
にキャストし直すだけです。
必ず(unsigned char)
にキャストしてから渡します。
#include <ctype.h>
#include <stdio.h>
int main(void) {
char a = 'a';
// 安全のため(unsigned char)にキャストしてから渡す
char up = (char)toupper((unsigned char)a);
char z = 'Z';
char low = (char)tolower((unsigned char)z);
printf("a -> %c (ASCII %d)\n", up, (int)up);
printf("Z -> %c (ASCII %d)\n", low, (int)low);
return 0;
}
a -> A (ASCII 65)
Z -> z (ASCII 122)
1文字の変換例
文字から大文字に変換する
小文字1文字を大文字に変換する最小例です。
#include <ctype.h>
#include <stdio.h>
int main(void) {
char c = 'b';
char upper = (char)toupper((unsigned char)c);
printf("before: %c, after: %c\n", c, upper);
return 0;
}
before: b, after: B
文字から小文字に変換する
大文字1文字を小文字に変換する例です。
#include <ctype.h>
#include <stdio.h>
int main(void) {
char c = 'Q';
char lower = (char)tolower((unsigned char)c);
printf("before: %c, after: %c\n", c, lower);
return 0;
}
before: Q, after: q
英字以外はそのまま
数字や記号、空白などは変換対象ではないため、そのまま返ります。
#include <ctype.h>
#include <stdio.h>
int main(void) {
char d = '9';
char q = '?';
char sp = ' ';
char d_u = (char)toupper((unsigned char)d);
char d_l = (char)tolower((unsigned char)d);
char q_u = (char)toupper((unsigned char)q);
char q_l = (char)tolower((unsigned char)q);
char sp_u = (char)toupper((unsigned char)sp);
char sp_l = (char)tolower((unsigned char)sp);
printf("'9' -> toupper: %c, tolower: %c\n", d_u, d_l);
printf("'?' -> toupper: %c, tolower: %c\n", q_u, q_l);
printf("' ' -> toupper: %c, tolower: %c\n", sp_u, sp_l);
return 0;
}
'9' -> toupper: 9, tolower: 9
'?' -> toupper: ?, tolower: ?
' ' -> toupper: , tolower:
文字列を大文字・小文字に変換する
for文で末尾(‘\0’)まで処理
文字列はchar
の配列で、末尾にヌル文字'\0'
がつきます。
先頭から'\0'
に出会うまでループして各文字を変換します。
判定は不要で、毎回toupper
/tolower
を呼べば充分です。
#include <ctype.h>
#include <stdio.h>
// その場で大文字化する関数
void str_to_upper_inplace(char *s) {
// s[i] が '#include <ctype.h>
#include <stdio.h>
// その場で大文字化する関数
void str_to_upper_inplace(char *s) {
// s[i] が '\0' になるまでループ
for (size_t i = 0; s[i] != '\0'; ++i) {
// 未定義動作を避けるため(unsigned char)で受け渡し
s[i] = (char)toupper((unsigned char)s[i]);
}
}
// その場で小文字化する関数
void str_to_lower_inplace(char *s) {
for (size_t i = 0; s[i] != '\0'; ++i) {
s[i] = (char)tolower((unsigned char)s[i]);
}
}
int main(void) {
char s1[] = "AbcD! 123";
str_to_upper_inplace(s1);
printf("UPPER: %s\n", s1);
char s2[] = "AbcD! 123";
str_to_lower_inplace(s2);
printf("lower: %s\n", s2);
return 0;
}
' になるまでループ
for (size_t i = 0; s[i] != '#include <ctype.h>
#include <stdio.h>
// その場で大文字化する関数
void str_to_upper_inplace(char *s) {
// s[i] が '\0' になるまでループ
for (size_t i = 0; s[i] != '\0'; ++i) {
// 未定義動作を避けるため(unsigned char)で受け渡し
s[i] = (char)toupper((unsigned char)s[i]);
}
}
// その場で小文字化する関数
void str_to_lower_inplace(char *s) {
for (size_t i = 0; s[i] != '\0'; ++i) {
s[i] = (char)tolower((unsigned char)s[i]);
}
}
int main(void) {
char s1[] = "AbcD! 123";
str_to_upper_inplace(s1);
printf("UPPER: %s\n", s1);
char s2[] = "AbcD! 123";
str_to_lower_inplace(s2);
printf("lower: %s\n", s2);
return 0;
}
'; ++i) {
// 未定義動作を避けるため(unsigned char)で受け渡し
s[i] = (char)toupper((unsigned char)s[i]);
}
}
// その場で小文字化する関数
void str_to_lower_inplace(char *s) {
for (size_t i = 0; s[i] != '#include <ctype.h>
#include <stdio.h>
// その場で大文字化する関数
void str_to_upper_inplace(char *s) {
// s[i] が '\0' になるまでループ
for (size_t i = 0; s[i] != '\0'; ++i) {
// 未定義動作を避けるため(unsigned char)で受け渡し
s[i] = (char)toupper((unsigned char)s[i]);
}
}
// その場で小文字化する関数
void str_to_lower_inplace(char *s) {
for (size_t i = 0; s[i] != '\0'; ++i) {
s[i] = (char)tolower((unsigned char)s[i]);
}
}
int main(void) {
char s1[] = "AbcD! 123";
str_to_upper_inplace(s1);
printf("UPPER: %s\n", s1);
char s2[] = "AbcD! 123";
str_to_lower_inplace(s2);
printf("lower: %s\n", s2);
return 0;
}
'; ++i) {
s[i] = (char)tolower((unsigned char)s[i]);
}
}
int main(void) {
char s1[] = "AbcD! 123";
str_to_upper_inplace(s1);
printf("UPPER: %s\n", s1);
char s2[] = "AbcD! 123";
str_to_lower_inplace(s2);
printf("lower: %s\n", s2);
return 0;
}
UPPER: ABCD! 123
lower: abcd! 123
配列の文字列をその場で変換
書き換え可能なchar
配列なら、同じ配列の中で直接変換できます。
代表的な例を示します。
#include <ctype.h>
#include <stdio.h>
int main(void) {
// 文字列リテラルから初期化したchar配列は書き換え可能
char s[] = "Hello, C Language! 123";
for (size_t i = 0; s[i] != '#include <ctype.h>
#include <stdio.h>
int main(void) {
// 文字列リテラルから初期化したchar配列は書き換え可能
char s[] = "Hello, C Language! 123";
for (size_t i = 0; s[i] != '\0'; ++i) {
s[i] = (char)toupper((unsigned char)s[i]); // その場で大文字化
}
printf("%s\n", s);
return 0;
}
'; ++i) {
s[i] = (char)toupper((unsigned char)s[i]); // その場で大文字化
}
printf("%s\n", s);
return 0;
}
HELLO, C LANGUAGE! 123
別バッファに書き込んで変換
元の文字列を残したい場合は、別のバッファに書き込む方法が安全です。
バッファサイズを受け取って、あふれないように終端'\0'
を付けます。
#include <ctype.h>
#include <stdio.h>
#include <string.h>
// srcを小文字化してdstへコピーする安全な関数
void str_to_lower_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return; // バッファが無ければ何もしない
size_t i = 0;
for (; src[i] != '#include <ctype.h>
#include <stdio.h>
#include <string.h>
// srcを小文字化してdstへコピーする安全な関数
void str_to_lower_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return; // バッファが無ければ何もしない
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
// unsigned charにキャストしてからtolowerを呼ぶ
dst[i] = (char)tolower((unsigned char)src[i]);
}
dst[i] = '\0'; // 必ず終端を付ける
}
int main(void) {
const char *src = "AbC-XYZ 123";
char dst[64]; // 十分な大きさのバッファを用意
str_to_lower_copy(src, dst, sizeof(dst));
printf("src: %s\n", src);
printf("dst: %s\n", dst);
return 0;
}
' && i + 1 < dst_size; ++i) {
// unsigned charにキャストしてからtolowerを呼ぶ
dst[i] = (char)tolower((unsigned char)src[i]);
}
dst[i] = '#include <ctype.h>
#include <stdio.h>
#include <string.h>
// srcを小文字化してdstへコピーする安全な関数
void str_to_lower_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return; // バッファが無ければ何もしない
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
// unsigned charにキャストしてからtolowerを呼ぶ
dst[i] = (char)tolower((unsigned char)src[i]);
}
dst[i] = '\0'; // 必ず終端を付ける
}
int main(void) {
const char *src = "AbC-XYZ 123";
char dst[64]; // 十分な大きさのバッファを用意
str_to_lower_copy(src, dst, sizeof(dst));
printf("src: %s\n", src);
printf("dst: %s\n", dst);
return 0;
}
'; // 必ず終端を付ける
}
int main(void) {
const char *src = "AbC-XYZ 123";
char dst[64]; // 十分な大きさのバッファを用意
str_to_lower_copy(src, dst, sizeof(dst));
printf("src: %s\n", src);
printf("dst: %s\n", dst);
return 0;
}
src: AbC-XYZ 123
dst: abc-xyz 123
大文字化と小文字化のサンプル
同じ文字列から大文字化版と小文字化版を作る全体例です。
#include <ctype.h>
#include <stdio.h>
#include <string.h>
void str_to_upper_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '#include <ctype.h>
#include <stdio.h>
#include <string.h>
void str_to_upper_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
dst[i] = (char)toupper((unsigned char)src[i]);
}
dst[i] = '\0';
}
void str_to_lower_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
dst[i] = (char)tolower((unsigned char)src[i]);
}
dst[i] = '\0';
}
int main(void) {
const char *src = "Hello, World! こんにちは"; // 日本語は変わらない
char upper[256];
char lower[256];
str_to_upper_copy(src, upper, sizeof(upper));
str_to_lower_copy(src, lower, sizeof(lower));
printf("src : %s\n", src);
printf("UPPER: %s\n", upper);
printf("lower: %s\n", lower);
return 0;
}
' && i + 1 < dst_size; ++i) {
dst[i] = (char)toupper((unsigned char)src[i]);
}
dst[i] = '#include <ctype.h>
#include <stdio.h>
#include <string.h>
void str_to_upper_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
dst[i] = (char)toupper((unsigned char)src[i]);
}
dst[i] = '\0';
}
void str_to_lower_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
dst[i] = (char)tolower((unsigned char)src[i]);
}
dst[i] = '\0';
}
int main(void) {
const char *src = "Hello, World! こんにちは"; // 日本語は変わらない
char upper[256];
char lower[256];
str_to_upper_copy(src, upper, sizeof(upper));
str_to_lower_copy(src, lower, sizeof(lower));
printf("src : %s\n", src);
printf("UPPER: %s\n", upper);
printf("lower: %s\n", lower);
return 0;
}
';
}
void str_to_lower_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '#include <ctype.h>
#include <stdio.h>
#include <string.h>
void str_to_upper_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
dst[i] = (char)toupper((unsigned char)src[i]);
}
dst[i] = '\0';
}
void str_to_lower_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
dst[i] = (char)tolower((unsigned char)src[i]);
}
dst[i] = '\0';
}
int main(void) {
const char *src = "Hello, World! こんにちは"; // 日本語は変わらない
char upper[256];
char lower[256];
str_to_upper_copy(src, upper, sizeof(upper));
str_to_lower_copy(src, lower, sizeof(lower));
printf("src : %s\n", src);
printf("UPPER: %s\n", upper);
printf("lower: %s\n", lower);
return 0;
}
' && i + 1 < dst_size; ++i) {
dst[i] = (char)tolower((unsigned char)src[i]);
}
dst[i] = '#include <ctype.h>
#include <stdio.h>
#include <string.h>
void str_to_upper_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
dst[i] = (char)toupper((unsigned char)src[i]);
}
dst[i] = '\0';
}
void str_to_lower_copy(const char *src, char *dst, size_t dst_size) {
if (dst_size == 0) return;
size_t i = 0;
for (; src[i] != '\0' && i + 1 < dst_size; ++i) {
dst[i] = (char)tolower((unsigned char)src[i]);
}
dst[i] = '\0';
}
int main(void) {
const char *src = "Hello, World! こんにちは"; // 日本語は変わらない
char upper[256];
char lower[256];
str_to_upper_copy(src, upper, sizeof(upper));
str_to_lower_copy(src, lower, sizeof(lower));
printf("src : %s\n", src);
printf("UPPER: %s\n", upper);
printf("lower: %s\n", lower);
return 0;
}
';
}
int main(void) {
const char *src = "Hello, World! こんにちは"; // 日本語は変わらない
char upper[256];
char lower[256];
str_to_upper_copy(src, upper, sizeof(upper));
str_to_lower_copy(src, lower, sizeof(lower));
printf("src : %s\n", src);
printf("UPPER: %s\n", upper);
printf("lower: %s\n", lower);
return 0;
}
src : Hello, World! こんにちは
UPPER: HELLO, WORLD! こんにちは
lower: hello, world! こんにちは
C言語初心者の注意点
文字列リテラルは書き換えない
文字列リテラル(例: "abc"
)は読み取り専用領域に置かれることがあり、書き換えると未定義動作です。
書き換えたい場合はchar
配列にコピーしてから行います。
#include <ctype.h>
#include <stdio.h>
int main(void) {
// NG例(参考): 文字列リテラルの書き換えは未定義動作
// char *p = "abc";
// p[0] = 'A'; // 実行時エラーになる可能性がある
// OK例: 書き換え可能な配列として保持する
char s[] = "abc";
s[0] = (char)toupper((unsigned char)s[0]);
printf("%s\n", s); // "Abc"
return 0;
}
Abc
constを活用: 書き換えない文字列はconst char *
で受けると意図が明確になります。
日本語(UTF-8)は変換しない
toupper
/tolower
はバイト単位の処理であり、UTF-8の日本語やアクセント付き文字などの多バイト文字は変換されません。
ASCII英字のみが対象と考えると安全です。
多言語対応の大小変換はワイド文字系(wchar_t
とwctype.h
のtowupper
/towlower
)や別ライブラリの利用を検討します。
#include <ctype.h>
#include <stdio.h>
#include <string.h>
void str_to_upper_inplace(char *s) {
for (size_t i = 0; s[i] != '#include <ctype.h>
#include <stdio.h>
#include <string.h>
void str_to_upper_inplace(char *s) {
for (size_t i = 0; s[i] != '\0'; ++i) {
s[i] = (char)toupper((unsigned char)s[i]);
}
}
int main(void) {
char s[] = "abc日本語XYZ";
str_to_upper_inplace(s);
printf("%s\n", s); // ASCIIだけが変わる
return 0;
}
'; ++i) {
s[i] = (char)toupper((unsigned char)s[i]);
}
}
int main(void) {
char s[] = "abc日本語XYZ";
str_to_upper_inplace(s);
printf("%s\n", s); // ASCIIだけが変わる
return 0;
}
ABC日本語XYZ
大文字・小文字の判定は不要
事前にisupper
やislower
で判定する必要はありません。
toupper
/tolower
は、対象外の文字にはそのままの文字を返すからです。
これによりコードが簡潔で高速になります。
#include <ctype.h>
#include <stdio.h>
// 判定せずに毎回toupperを呼ぶ簡潔な方法
void to_upper_simple(char *s) {
for (size_t i = 0; s[i] != '#include <ctype.h>
#include <stdio.h>
// 判定せずに毎回toupperを呼ぶ簡潔な方法
void to_upper_simple(char *s) {
for (size_t i = 0; s[i] != '\0'; ++i) {
s[i] = (char)toupper((unsigned char)s[i]);
}
}
int main(void) {
char s[] = "MiXeD Case 123";
to_upper_simple(s);
printf("%s\n", s);
return 0;
}
'; ++i) {
s[i] = (char)toupper((unsigned char)s[i]);
}
}
int main(void) {
char s[] = "MiXeD Case 123";
to_upper_simple(s);
printf("%s\n", s);
return 0;
}
MIXED CASE 123
もし判定を書く場合: 書くとしてもisupper
/islower
の引数にも(unsigned char)
キャストを忘れないようにします。
まとめ
toupper
とtolower
は、英字の大文字・小文字変換を安全かつ簡単に実現する基本関数です。
1文字の変換から、'\0'
までのループで文字列全体を変換する方法まで理解できれば、ほとんどの用途をカバーできます。
最重要ポイントは次の3つです。
まず引数は常に(unsigned char)
にキャストすること。
次にchar
配列以外の文字列リテラルは書き換えないこと。
そしてUTF-8の日本語など多バイト文字は変換しないことです。
これらを守れば、初心者の方でも安全に文字の大小変換を実装できます。