MISRA Cは自動車業界などで利用しているC言語記述作法(coding standard)です。
MISRA C++もあります。
CERT C, CERT C++などの他の記述作法からも参照されています。
MISRA C:1998,MISRA C:2004, MISRA C:2012と三世代に進化しています。
Qiitaの記事から、書いた方の引用「」に加えて、
「補足: by @kaizen_nagoya 」という解説事項を加えています。
最後に、MISRAのコードの断片をコンパイル・実行する際に利用するヘッダファイルmisra_c.hを記載しました。
参考文献は
MISRA-C 2012 Referenceに掲載している文献の入手可能性を確認
https://qiita.com/kaizen_nagoya/items/96dc8b125e462d5575bb
<この項は書きかけです。順次追記します。>
C言語規格
ANSI C 1989, ISO/IEC 9899:1990, programming language, 1st
ISO/IEC/ANSI 9899:1999,programming language,2nd
ISO/IEC/ANSI 9899:2011, programming language,3rd
未定義(undefined)
CPUの機能、性能によりどのように実現すると効率的か決めるとCPU, Cコンパイラの発展に足枷となると良くないので、敢えて定義しないことを規定する。個々のCPU, Cコンパイラ処理系ごとに定義すると良い。
未規定(unspecified)
CPUの機能、性能により複数の解決方法の候補があり、どちらかを規定するとCPU, Cコンパイラの発展に足枷となると良くないので、敢えてどちらか一方にしないことを規定する。個々のCPU, Cコンパイラ処理系ごとに定義すると良い。
処理系定義(implementation definition)
処理系ごとに定義すると良い事項。未定義、未規定と異なる点は、必ず処理系で定義してプログラマに示すことが必要である事項。
ヘッダファイルなどで規定している定数、宣言などで実現している場合もある。
地域依存(locale)
処理対象、註釈(comment)として用いる、文字コード、文字フォント、自然言語の種類などにより必要な機能を定義する事項。
規則分類
必須(mandatory) 2012新設。逸脱することを想定していない。必須であっても逸脱する必要があれば、逸脱の手続きを取るとともに、MISRAに対して、逸脱から必須に変更することを要求すると良い。
必要(required)逸脱する場合には、逸脱の手続きを取る。
推奨(advisory).逸脱手順を必要としない。
「推奨」を「必要」、「必要」を「必須」として取り扱っても良い.
規則を格上げする場合には、その理由、根拠、複数のコンパイラ、複数のツールの結果を示すことが大切である。
逸脱の手続き
規則を守ることが、品質を上げることではありません。
規則が何のためにあるかを理解し、現在取り組んでいる製品と方向性が違う場合には、製品の必要な品質特性に合わせるために逸脱することが大切です。
逸脱は、ソースコードの先頭または末尾の註釈(comment)または下記のような文書を作るかは組織で決めてください。
1 Qiitaの記事
1.1 STM32マイコンでFreeRTOSを用いてLEDを制御する(Lチカ)
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
1.2 Railsアプリの例外構造パターン
module FatalError
class InvalidArgument < Base
end
end
class Device
VALID_STATUSES = {
:deleted => 0,
:activated => 1,
}
def status=(new_status)
if new_status.is_a?(String)
super VALID_STATUSES[new_status.underscore.to_sym]
elsif new_status.is_a?(Symbol)
super VALID_STATUSES[new_status]
else
# 想定外の値が指定されたので例外にする
raise FatalError::InvalidArgument
end
end
end
「elsif句を使った場合とか、case文では必ずelse句をつけなさいという先人の教え(MISRA-C 15.7とか)があります」
補足: by @kaizen_nagoya
「MISRA C:2012 15.7 すべてのif ... else if構文は、else文で終了しなければならない」
「MISRA C:2012 16.4 すべてのswitch文は、デフォルトのラベルをもたなければならない」
が先人の教えです。
1.3 OpenVX 1.1 SC版 (Safety Critical版)の変更点
1.4【社内勉強会】「リーダブルコード」の紹介
Javaコーディング規約勉強会
https://qiita.com/yuji38kwmt/items/7b1d25fc72e3fcd5d4f4
「[補足] 「関数の末尾以外の return 禁止」の発生元
MISRA-Cという「C言語のためのソフトウェア設計標準規格」が発生元らしい。」
補足: by @kaizen_nagoya
禁止ではなく、末尾以外で戻る場合には、その理由、それでいい根拠を文書化することが必須なのです。逸脱の手続きと言います。規則を守る方が信頼性が下がる場合もあります。
1.5 C Puzzle Bookの有り難み5つ、C言語規格及びCコンパイラの特性を認識
「SWESTでも、何度もMISRA Cの講演、演習を実施しています。」
1.5.1 C++ Templates Part1 BASICS Chapter 3. Class Templates 3.2 Use of Class Template Stack stack1test.cpp
1.6 [C][C++]の国際規格案の例題をコンパイルするときの課題7つ。
1.6.1
ISO/IEC 14288 C++ standard. bit-field
https://qiita.com/kaizen_nagoya/items/e731e6d02258fe559056
1.7どうやって MISRA C Example Suiteをコンパイルするか
1.7.1 MISRA C.2.1 Type widening in integer promotion,(wicm3.c )
1.7.2 MISRA C++ 5-0-16
1.8 CERT
コンパイル用shell script C版(clangとgcc)とC++版(clang++とg++)
https://qiita.com/kaizen_nagoya/items/74220c0577a512c2d7da
1.8.1 CERT C
1.8.1.1 CERT C入門(1) Rule 01. Preprocessor (PRE)
1.8.1.2 CERT C入門(2) Rule 02. Declarations and Initialization (DCL)
1.8.2 CERT C++
1.8.2.1 CERT C++入門(1) Rule 01. Declarations and Initialization (DCL)
1.9 プログラミング言語教育のXYZ
https://qiita.com/kaizen_nagoya/items/1950c5810fb5c0b07be4
「TOPPERS オープンソースのMISRA C対応。」
2. 関連資料
2.1 SWESTまとめ
https://qiita.com/kaizen_nagoya/items/62e56ae151554d6200c0
https://swest.toppers.jp/SWEST17/program.html 組込み開発者におくる MISRA C
2.2 効率的なHAZOPの進め方
「物について記述してもよい 事前,事後(初期条件,境界条件)を記述 3つ以上の設計指針を使う 共用品設計(universal design),形の設計指針,言語規約(MISRA)など」
2.3 オープンソースカンファレンスの出展、セミナ、LTのすすめ
https://qiita.com/kaizen_nagoya/items/8628baa3f6e1bb500045
「名古屋アジャイル勉強会 ういろMUG Proof Cafe TOPPERS プロジェクト MISRA C愛好会 ちょけねこ友の会 などの名古屋の勉強会の集まりで出展」
2.4 TOPPERS まとめ(5つの壁のうちの一つ)
https://qiita.com/kaizen_nagoya/items/9026c049cb0309b9d451
MISRA C MISRA C研究会は、NPO法人 組込みソフトウェア管理者・技術者育成研究会(SESSAME)の作業部会(WG)
2.5 プログラマが学会・研究会で対外発表する際の9つの関門
「二上貴夫 SWESTのMISRAの解説で、少年のような目で技術を語っていた。」
2.6 SWEST20で基調講演(再演含む)してほしい人上位10人
https://qiita.com/kaizen_nagoya/items/d4d9bf953953c720361d
https://swest.toppers.jp/SWEST19/program/s3c.html 1.3 「二上貴夫 SWEST4 自動車向け組込みC言語用ガイドラインMISRA C」
3 参考文献・URL
3.1 MISRA C bulletin board
https://www.misra.org.uk/forum/index.php
英語です。日本からも多数質問をし、原書の改訂に貢献しています。
3.2 MISRA C研究会
3.3 MISRA C愛好会
http://researchmap.jp/kaizen/MISRA-C/
OSCなどに出展する時の任意団体
3.4 misrac 断片コンパイル用ヘッダ
/* Author, (c) Dr. Ogawa Kiyoshi*/
/* ver 1.0 January 1, 1999 */
/* ver 2.0 Feburary 2, 2005 */
/* ver 3.0 March 3, 2013 */
/* @kaizen_nagoya, http://researchmap.jp/kaizen/MISRA-C/ */
/* Purpose: macro, definition and/or information to MISRA-C Examples.*/
/* https://gcc.gnu.org/onlinedocs/gcc/Standards.html */
/* https://gcc.gnu.org/c99status.html */
/* http://www.polyomino.org.uk/computer/c/ */
#ifndef __MISRA_C__
#define __MISRA_C__
#include <float.h>
#include <iso646.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h> /* define true */
#include <stddef.h> /* define NULL */
#include <stdint.h> /* C99: define int16_t */
#ifdef __STDC_VERSION__
#include <stdalign.h>
#include <stdnoreturn.h>
#endif
#include <stdio.h>
// #include <sys/types.h>
typedef bool bool_t;
typedef float float32_t; /* dir4.6 */
typedef long double float64_t; /* dir4.6 */
#ifdef nofreestanding
#include <string.h>
#include <stdlib.h>
#endif
#ifndef DIR4_6
#ifndef __STDC_VERSION__
typedef char char_t;
typedef unsigned char uint8_t; /* dir4.6 */
typedef unsigned short uint16_t; /* dir4.6 */
/*typedef unsigned long uint32_t;*/ /* dir4.6 */
typedef unsigned long long uint64_t; /* dir4.6 */
typedef unsigned long long uint128_t;
typedef signed char int8_t; /* dir4.6 */
typedef signed short int16_t; /* dir4.6 */
/* typedef signed long int32_t; *//* dir4.6 */
typedef signed long long int64_t; /* dir4.6 */
typedef signed long long int128_t;
typedef float float32_t; /* dir4.6 */
typedef long double float64_t; /* dir4.6 */
typedef long double float128_t; /*dir4.6 */
#endif
#endif
#ifndef NC30 /* without renesas NC30WA, manual C.3.3 predefined macro */
#define __ISO_IEC_9899_1999__
#define __int32bit__
#define PR1x(a,b) (void)printf(" "#a " = %" #b "=%x\n", a,a)
#define PR2x(a,b,c) (void)printf(" "#a " = %" #c "=%x \n " #b " = %" #c "=%x\n", a,a,b,b)
#define PR3x(a,b,c,d) PR1x(a,d),PR2x(b,c,d);
#define PR1(a,b) (void)printf(" "#a " = %" #b "\n", a)
#define PR2(a,b,c) (void)printf(" "#a " = %" #c "\n " #b " = %" #c "\n", a, b)
#define PR3(a,b,c,d) PR1(a,d),PR2(b,c,d);
#else /* for renesas NC30W only */
#define __ISO_IEC_9899_1990__
#define __int16bit__
#define PR1(a,b)
#define PR2(a,b,c)
#define PR3(a,b,c,d)
#endif /*__STANDARD_IO__ */
#ifdef __ISO_IEC_9899_2011__
#include <stdarg.h>
#include <stdint.h>
#define PRSV() printf("ISO/IEC 9899:2011\n")
#elseif __ISO_IEC_9899_1999__
#include <stdarg.h>
#include <stdint.h>
#define PRSV() printf("ISO/IEC 9899:1999"\n)
#elseif __ISO_IEC_9899_1990__
/* #include <stdint.h> */ /*nc30wa is not supported */
typedef _bool bool_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long int64_t;
typedef signed long long int128_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long uint64_t;
typedef unsigned long long uint128_t;
typedef float float32_t;
typedef double float64_t;
typedef long double float128_t;
/* macro using printf debug or not.*/
#define PRSV() printf("ISO/IEC 9899:1990\n")
#elseif __int16bit__
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
typedef unsigned long uint64_t;
typedef unsigned long uint128_t;
#define PRSV() printf("no ISO/IEC 9899,16bit\n")
#else
/* typedef unsigned long long uint128_t;*/
#define PRSV() printf("no ISO/IEC 9899,32bit\n")
#endif
/* for each example*/
#ifdef DIR4_8
typedef struct OpaqueType *pOpaqueType;
pOpaqueType GetObject(void);
void UseObject(pOpaqueType);
#endif
#ifdef __MISRAC_6_6__
void f (uint16_t *p){
printf("uint16_t *p = %d, p = %d\n",*p,(int)p);
}
#endif /*__MISRAC_6_6__ */
#ifdef __MISRAC_6_9__
//typedef signed char int8_t;
//typedef signed short int16_t;
//typedef signed int int32_t;
#ifndef _INT64_T
#define _INT64_T
typedef signed long int64_t;
#endif
#ifndef _bool
typedef unsigned int _bool;
#endif
#endif
#ifdef __MISRAC_DIR_4_1__
#include <string.h>
#include <stdbool.h>
typedef float float32_t;
#endif
#ifdef DIR4_13
typedef struct mutex mutex_t;
struct mutex
{
unsigned int number;
unsigned char* name;
}mutex;
mutex_t n;
mutex_t mutex_lock (void)
{
return n;
}
void mutex_unlock (mutex_t m)
{
m.number = 0;
m.name = (unsigned char *)"";
return;
}
int16_t x;
#endif
#ifdef __MISRAC_RULE_1_1__
#define __zpage // zero page, 0x0000
#define __near // short address
#define __far // full address
#define zpage __zpage
int i = 1;
#endif
#ifdef R2_1
int error_handler (void){
return true;
}
#endif
#ifdef __MISRAC_RULE_2_2__
volatile uint16_t v=1;
char a[]={"123456789ABCDEF"};
char *p=a;
#endif
#ifdef __MISRAC_RULE_3_2__
bool_t b=0;
#endif
#ifdef __MISRAC_RULE_5_1__
int32_t ABC = 1;
#endif
#ifdef __MISRAC_RULE_5_2__
int32_t engine_exhaust_gas_temperature_raw=0;
#endif
#ifdef __MISRAC_RULE_5_3__
struct astruct{
int16_t m;
};
void g (struct astruct *p){printf("struct astruct *p->m = %d\n",p->m);}
#endif
#ifdef __MISRAC_RULE_5_8__
extern void foo (void);
#endif
#ifdef __MISRAC_RULE_5_9__
extern void bar1(void) ;
extern void bar2(void);
#endif
#ifdef __MISRAC_RULE_7_1__
uint16_t code[ 10 ];
#endif
#ifdef __MISRAC_RULE_7_4__
void f1 (char * s1){
PR1(s1,s);
}
void f2(const char *s2){
PR1(s2,s);
}
#endif
#ifdef __MISRAC_RULE_8_1__
xn = 0;
int16_t xc = 1;
fn(void){
return printf("fn(void);\n");
}
int16_t fc(void){
return printf("int16_t fc(void);\n");
}
void gn(char c, const k){
PR2(c,k,d);
}
void gc(char c, const int16_t k){
PR2(c,k,d);
}
#endif
#ifdef __MISRAC_RULE_8_2__
#endif
#ifdef R8_4
extern uint16_t var1; // 宣言
extern void func1( uint16_t var1 );
extern void func2( uint16_t var1 );
#endif
#ifdef __MISRAC_RULE_8_4__
extern int16_t count;
extern void func1 (void);
extern void func2 (int16_t x, int16_t y);
extern void func3 (int16_t x, int16_t y);
#endif
#ifdef __MISRAC_RULE_8_5__
extern int16_t a;
#endif
#ifdef __MISRAC_RULE_8_6__
void f1(void);
void f2(void);
#endif
#ifdef __MISRAC_RULE_8_11__
int32_t array1[10]={0,1,2,3,4,5,6,7,8,9};
int32_t array2[]={9,8,7,6,5,4,3,2,1,0};
#endif
#ifdef __MISRAC_RULE_8_13__
char last_char (const char * const s);
// uint16_t first( const uint16_t a[5]);
#endif
#ifdef __MISRAC_8_14__
volatile bool_t b=true;
struct s{
uint16_t count;
uint16_t a[ 10 ];
};
struct s sps = {
0,
{0,1,2,3,4,5,6,7,8,9}
};
struct s * sp;
char *p="Control Statement Expressions\n";
#endif
#ifdef R11_1
void f(int16_t n){
// printf("%d\n",n);
}
#endif
#ifdef __MISRAC_RULE_11_1__
void f(int16_t n){
PR1(n,d);
}
#endif
#ifdef R11_2
void f(int16_t n){
// printf("%d\n",n);
}
#endif
#ifdef R11_3
uint32_t read_value ( void ){
return true;
}
void print ( uint32_t n ){
PR1(n,d);
}
#endif
#ifdef __MISRAC_RULE_11_3__
uint32_t read_value ( void ){
return true;
}
void print ( uint32_t n ){
PR1(n,d);
}
#endif
#ifdef R11_4
void print ( uint16_t n ){
PR1(n,d);
}
#endif
#ifdef __MISRAC_RULE_11_9__
void f ( uint8_t *p ){
if (NULL == p){
PR1(NULL,d);
} else {
PR2(*p,NULL,d);
}
}
#endif
#ifdef __MISRAC_RULE_12_1__
int8_t f(int8_t a, int8_t b){
return a+b;
}
#endif
#ifdef __MISRAC_RULE_12_3__
void f(int8_t a, int8_t b){
PR2(a,b,d);
}
#endif
#ifdef __MISRAC_RULE_13_1__
void p ( uint16_t a[ 2 ] ){
PR2(a[0],a[1],d);
}
#endif
#ifdef __MISRAC_RULE_13_2__
#endif
#ifdef R14_1
uint32_t read_u32(void){
static uint32_t i = 14;
return --i;
}
float32_t read_float32(void){
static float32_t f = 0;
return ++f;
}
#endif
#ifdef __MISRAC_RULE_14_4__
bool_t flag= true;
#endif
#ifdef __MISRAC_RULE_17_3__
double power (double d, int n){
return (double)d*n;
}
#endif
#ifdef R17_6
#define TEN 10U
uint16_t v1[TEN]={0,1,2,3,4,5,6,7,8,9};
uint16_t v2[TEN+TEN]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
uint16_t v3[TEN+TEN+TEN]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30};
uint16_t t2(uint16_t n, uint16_t a[20]) {
uint16_t i;
uint16_t sum=0U;
for(i = 0U; i < n; ++i){
sum = sum + a[i];
}
return sum;
}
uint16_t t(uint16_t n, uint16_t a[]) {
uint16_t i;
uint16_t sum=0U;
for(i = 0U; i < n; ++i){
sum = sum + a[i];
}
return sum;
}
#endif
#ifdef __MISRAC_RULE_17_6__
uint16_t v1[10]={0,1,2,3,4,5,6,7,8,9};
uint16_t v2[20]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
#endif
#ifdef __MISRAC_C_2_1_
#ifndef __STANDARD_IO__ /* without renesas NC30W */
#define PR(a,b,c,d,e) printf("%" #a " + %" #a "= %" #a ", %" #b "\n", c, d, c + d, e)
#define PRC(a,b,c,d,e,f) printf("%" #a" + %" #a " = %" #a ", %" #b "\n", c, d, (f) c + d, e)
#define PRT(a,b,c,d,e,f) PR(a,b, c, d, e); e = c + d;PR(a,b, c, d, e); e = (f) c + d;/*printf(#e"= %llu ",e)*/;PRC(a,a, c, d, e, f);PRC(a,b, c, d, e, f)
#else /* for renesas NC30W only */
#define PR(a,b,c,d,e)
#define PRC(a,b,c,d,e,f)
#define PRT(a,b,c,d,e,f)
#endif /*__STANDARD_IO__ */
#endif /* __MISRAC_C_2_1_ */
#endif /* __MISRAC__ */
4 関連文献
cpprefjp - C++日本語リファレンス
コンパイラの実装状況
typedef は C++11 ではオワコン
C99からC++14を駆け抜けるC++講座
4.1 自己参照
どうやって MISRA Example Suiteをコンパイルするか
https://qiita.com/kaizen_nagoya/items/fbdbff5ff696e2ca7f00
docker(200) Misra Example Suite at docker コンパイル完了までの道のり
https://qiita.com/kaizen_nagoya/items/71f04a0204d5a1114577
MISRA C.2.1 Type widening in integer promotion,(wicm3.c )
https://qiita.com/kaizen_nagoya/items/6a24db5d51efae358cfb
MISRA-C 2012 Referenceに掲載している文献の入手可能性を確認
https://qiita.com/kaizen_nagoya/items/96dc8b125e462d5575bb
MISRA C:2012追加文書
https://qiita.com/kaizen_nagoya/items/d08b2ae495b136f9638c
MISRA C まとめ #include
https://qiita.com/kaizen_nagoya/items/f1a79a7cbd281607c7c9
MISRA C 2012のTechnical Corrigendum 1の21.X訂正意見はほとんど日本からだった件
https://qiita.com/kaizen_nagoya/items/152c1de26b0831c02f41
Autosar Guidelines C++14 example code compile list(1-169)名古屋のIoTは名古屋のOSで https://qiita.com/kaizen_nagoya/items/8ccbf6675c3494d57a76
MISRA C++ 5-0-16
https://qiita.com/kaizen_nagoya/items/7df2d4e05db724752a74
言語規格、コーディング標準の使い方
https://qiita.com/kaizen_nagoya/items/01256365b82666e101aa
コピペコンパイルエラーあるある
C++ Error Message Collection(1)does not name a type, 11 articles
dockerにclang
docker gnu(gcc/g++) and llvm(clang/clang++)
コンパイル用shell script C版(clangとgcc)とC++版(clang++とg++)
Compare the contents of C++N4910:2022, C++N4741:2018 and C++N4606:2015
C++ sample list
clang++, g++コンパイルエラー方針の違いの例
astyle 使ってみた
C++N4606 Working Draft 2016, ISO/IEC 14882, C++ standardのコード断片をコンパイルするためにしていること
https://qiita.com/kaizen_nagoya/items/a8d7ee2f2e29e76c19c1
コンパイル用shell script C版(clangとgcc)とC++版(clang++とg++)
https://qiita.com/kaizen_nagoya/items/74220c0577a512c2d7da
Clang/Clang++(LLVM) gcc/g++(GNU) コンパイラ警告等比較
https://qiita.com/kaizen_nagoya/items/9a82b958cc3aeef0403f
C++2003とC++2017でコンパイルエラーになるならない事例集
https://qiita.com/kaizen_nagoya/items/a13ea3823441c430edff
Qiitaに投稿するCのStyle例(暫定)
https://qiita.com/kaizen_nagoya/items/946df1528a6a1ef2bc0d
cpprefjpのdecltypeをコンパイル試験
https://qiita.com/kaizen_nagoya/items/090909af702f0d5d8a67
MISRA C++ 5-0-16
https://qiita.com/kaizen_nagoya/items/7df2d4e05db724752a74
C++ Templates Part1 BASICS Chapter 3. Class Templates 3.2 Use of Class Template Stack stack1test.cpp
https://qiita.com/kaizen_nagoya/items/cd5fc49106fad5a4e9ed
ISO/IEC TS 17961:2013 C Secure Coding Rules(1) All list(to be confirmed)
https://qiita.com/kaizen_nagoya/items/54e056195c4f11b850a1
C言語(C++)に対する誤解、曲解、無理解、爽快。
https://qiita.com/kaizen_nagoya/items/3f3992c9722c1cee2e3a
C Puzzle Bookの有り難み5つ、C言語規格及びCコンパイラの特性を認識
https://qiita.com/kaizen_nagoya/items/d89a48c1536a02ecdec9
'wchar.h' file not found で困った clang++ macOS
https://qiita.com/kaizen_nagoya/items/de15cd46d657517fac11
Open POSIX Test Suiteの使い方を調べはじめました
https://qiita.com/kaizen_nagoya/items/644d5e407f5faf96e6dc
MISRA-C 2012 Referenceに掲載している文献の入手可能性を確認
https://qiita.com/kaizen_nagoya/items/96dc8b125e462d5575bb
MISRA C まとめ #include
https://qiita.com/kaizen_nagoya/items/f1a79a7cbd281607c7c9
「C++完全理解ガイド」の同意できること上位10
https://qiita.com/kaizen_nagoya/items/aa5744e0c4a8618c7671
<この記事は個人の過去の経験に基づく個人の感想です。現在所属する組織、業務とは関係がありません。>
5 文献履歴
ver. 1.00 初稿 20180316
ver. 1.01 見出し分類 20180317
ver. 1.02 10項目追記 20180322
ver. 1.03 URL追記 20201228
ver. 1.04 タグ見直し 20220618
ver. 1.05 ありがとう追記 20230315
最後までおよみいただきありがとうございました。
いいね 💚、フォローをお願いします。
Thank you very much for reading to the last sentence.
Please press the like icon 💚 and follow me for your happy life.