0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C言語 enumで値指定は注意が必要

Posted at

概要

ふとしたことでC言語のenumの挙動を調べていて気付いたことです。
enumの値設定の使用には注意が必要だということに気付きました。

環境

$ cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo

$ gcc --version
gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ clang --version
Ubuntu clang version 18.1.3 (1ubuntu1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

内容

enum_test.c
#include <stdio.h>

typedef enum enum_test
{
	Enum1_a,
	Enum1_b,
	Enum2_a = 5,
	Enum2_b,
} EnumTest;
char *PrintEnumName(EnumTest Val)
{
	if (Val == Enum1_a)
	{
		return "Enum1_a";
	}
	if (Val == Enum1_b)
	{
		return "Enum1_b";
	}
	if (Val == Enum2_a)
	{
		return "Enum2_a";
	}
	if (Val == Enum2_b)
	{
		return "Enum2_b";
	}
	return ("Not Assigned Enum");
}
void PrintEnumTest(EnumTest Val)
{
	printf("Enum: %s : %d\n", PrintEnumName(Val), Val);
}

int main(int argc, char **argv)
{
	PrintEnumTest(Enum1_a);
	PrintEnumTest(Enum1_b);
	PrintEnumTest(Enum2_a);
	PrintEnumTest(Enum2_b);
	return 0;
}

結果は

$ gcc -Wall enum_test.c -o enum_test
$ ./enum_test
Enum : Enum1_a : 0
Enum : Enum1_b : 1
Enum : Enum2_a : 5
Enum : Enum2_b : 6

想定通り
ちょっと長いですが

enum_test2.c
#include <stdio.h>

typedef enum enum_test
{
	Enum1_a,
	Enum1_b,
	Enum1_c,
	Enum1_d,
	Enum1_e,
	Enum1_f,
	Enum2_a = 5,
	Enum2_b,
} EnumTest;
char *PrintEnumName(EnumTest Val)
{
	if (Val == Enum1_a)
	{
		return "Enum1_a";
	}
	if (Val == Enum1_b)
	{
		return "Enum1_b";
	}
	if (Val == Enum1_c)
	{
		return "Enum1_c";
	}
	if (Val == Enum1_d)
	{
		return "Enum1_d";
	}
	if (Val == Enum1_e)
	{
		return "Enum1_e";
	}
	if (Val == Enum1_f)
	{
		return "Enum1_f";
	}
	if (Val == Enum2_a)
	{
		return "Enum2_a";
	}
	if (Val == Enum2_b)
	{
		return "Enum2_b";
	}
	return ("Not Assigned Enum");
}
void PrintEnumTest(EnumTest Val)
{
	printf("Enum: %s : %d\n", PrintEnumName(Val), Val);
}

int main(int argc, char **argv)
{
	PrintEnumTest(Enum1_a);
	PrintEnumTest(Enum1_b);
	PrintEnumName(Enum1_c);
	PrintEnumTest(Enum1_d);
	PrintEnumTest(Enum1_e);
	PrintEnumTest(Enum1_f);
	PrintEnumTest(Enum2_a);
	PrintEnumTest(Enum2_b);
	return 0;
}

実行結果

$ gcc -Wall enum_test2.c -o enum_test2
$ ./enum_test2
Enum: Enum1_a : 0
Enum: Enum1_b : 1
Enum: Enum1_d : 3
Enum: Enum1_e : 4
Enum: Enum1_f : 5
Enum: Enum1_f : 5
Enum: Enum2_b : 6

特にワーニングなどなく実行できてしまう。

PrintEnumTest(Enum2_a);

の処理が

PrintEnumTest(Enum1_f);

として実行されています。

if (Val == Enum2_a)
	{
		return "Enum2_a";
	}

は実行されていないことになります。

enum_test3.c
#include <stdio.h>

typedef enum enum_test
{
	Enum1_a,
	Enum1_b,
	Enum1_c,
	Enum1_d,
	Enum1_e,
	Enum1_f,
	Enum2_a = 5,
	Enum2_b,
} EnumTest;
char *PrintEnumName(EnumTest Val)
{
	switch (Val) {
		case Enum1_a: return "Enum1_a";
        case Enum1_b: return "Enum1_b";
        case Enum1_c: return "Enum1_c";
        case Enum1_d: return "Enum1_d";
        case Enum1_e: return "Enum1_e";
        case Enum1_f: return "Enum1_f";
        case Enum2_a: return "Enum2_a";
        case Enum2_b: return "Enum2_b";
		default: return ("Not Assigned Enum");
	}
}
void PrintEnumTest(EnumTest Val)
{
	printf("Enum: %s : %d\n", PrintEnumName(Val), Val);
}

int main(int argc, char **argv)
{
	PrintEnumTest(Enum1_a);
	PrintEnumTest(Enum1_b);
	PrintEnumName(Enum1_c);
	PrintEnumTest(Enum1_d);
	PrintEnumTest(Enum1_e);
	PrintEnumTest(Enum1_f);
	PrintEnumTest(Enum2_a);
	PrintEnumTest(Enum2_b);
	return 0;
}

switch文を使ったら

$ gcc -Wall enum_test3.c -o enum_test3
enum_test3.c: In function ‘PrintEnumName’:
enum_test3.c:23:9: error: duplicate case value
   23 |         case Enum2_a: return "Enum2_a";
      |         ^~~~
enum_test3.c:22:9: note: previously used here
   22 |         case Enum1_f: return "Enum1_f";
      |         ^~~~

ちゃんと怒られたけどenumの値を問題にしているわけではなくてswitch文の条件に被りがあるとエラーを出している。
ちなみにdefault:をなくしてswitchの中にすべてのenumをすべて書かなかったら

warning: enumeration value ‘xxxxx’ not handled in switch [-Wswitch]

と指摘してくれます。

誰も気にしていないのか?

一応気にしていいる人はいるらしい。
clangを使えばワーニングを出してくれる。

$ clang enum_test2.c -Wduplicate-enum -o tmp2
enum_test2.c:10:2: warning: element 'Enum1_f' has been implicitly assigned 5 which another element has been assigned [-Wduplicate-enum]
   10 |         Enum1_f,
      |         ^~~~~~~
enum_test2.c:11:2: note: element 'Enum2_a' also has value 5
   11 |         Enum2_a = 5,
      |         ^~~~~~~~~~~
1 warning generated.

まとめ

gccではenum listが少なければ可能な限りswitchで処理するのが正しいと思われます。
listが膨大になったらひたすら気を付けるしかないということです。
enumの値がdefineで宣言された値とかbitでorとか取られていると確認がとても大変そうですが…。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?