藤田様の別スレッドに便乗なのですが、CS+のRL78のコード生成ツールが生成するuart通信機能のポインタ変数でvolatile修飾子の記述位置がおかしいものがあるように思います。具体的な変数名で言うと、gp_uart0_tx_addressとgp_uart0_rx_addressです。(CS+ for CC V5.00.00のコード生成機能で確認しました。)コード生成ツールが生成した変数宣言は以下の通りです。volatile uint8_t * gp_uart0_tx_address; /* uart0 send buffer address */volatile uint8_t * gp_uart0_rx_address; /* uart0 receive buffer address */ところが、以下の画面コピーの赤枠の箇所の通り、割り込みルーチン内で変更されるのはgp_uart0_tx_addressやgp_uart0_rx_addressの変数自身ですので以下の変数宣言が正しい気がします。uint8_t * volatile gp_uart0_tx_address; /* uart0 send buffer address */uint8_t * volatile gp_uart0_rx_address; /* uart0 receive buffer address */試しに型修飾子の作用の仕方がvolatileと同様なconst(型修飾子の作用自体(機能)としては対義語的になる)で試してみると、変数自身に作用したのは以下の画面コピーのように橙枠の方でしたので、volatileも変数自身に作用させる場合には同様である筈です。
リカルドさん、こんにちは。藤田様、フォロー有難う御座います。
リカルドさまwrote: said:>1 void hoge(void) 2 { 3 extern volatile int* hogehoge; 4 while (*hogehoge) ; 7 while (hogehoge) ; 10 } hoge: movq .refptr.hogehoge(%rip), %rax movq (%rax), %rdx .L2: while (*hogehoge)に相当 movl (%rdx), %eax testl %eax, %eax jne .L2 .L3: while (hogehoge)に相当 jmp .L3 hogehoge がゼロでない事を何処で判断したの? .text
リカルドさまwrote: said:>NULL は何のオブジェクトも指しませんが、逆に、何かのオブジェクトを指すポインタは NULL でないことは確定的です。前段の hogehoge の指す先をアクセスした以降では hogehoge の値は NULL ではないということで、無限ループとなります。 NULL だったら関数を抜けると言う判断をどこかでしているのなら分かります。 しかし、そんな事はしていないよね。 NULL かどうか判断して、「NULLでした」と表示するプログラムは作れないと言うこと? ...略... コンパイルの動作がおかしいんじゃないかと言う話です。
> NULL だったら関数を抜けると言う判断をどこかでしているのなら分かります。
> しかし、そんな事はしていないよね。
明示的に NULL チェックを行い、ポインタの値が NULL である可能性があることをコンパイラに伝えれば NULL だった場合は安全に関数を抜けるコードを吐きますよ。
$ cat -n hoge.c 1 void hoge(void) 2 { 3 extern volatile int* hogehoge; 4 if (hogehoge) { 5 while (*hogehoge) { 6 ; 7 } 8 } 9 while (hogehoge) { 10 ; 11 } 12 } $ gcc -Wall -W -S -O2 hoge.c -o - .text hoge: movq .refptr.hogehoge(%rip), %rax movq (%rax), %rdx testq %rdx, %rdx je .L1 .L5: movl (%rdx), %eax testl %eax, %eax jne .L5 .L4: jmp .L4 .L1: rep ret $
> NULLをどう使うかはプログラマーしだいだと思います。printfだと0x00の前まで送ると言う仕様だから、それに従うしか無い。
> しかし自分で作った関数で、文字列の先頭アドレスと転送バイト数を指定すれば0x00の所で止める必要も無いわけです。
NULL と '\0' を混同されているのでは?
> 言いたいことは、「NULLだったらエラーにしなくてはならず、NULLを使ってはいけません」と言う規則がある訳でも無いだろうと言う事です。
NULL が何のオブジェクトも指さないことは『プログラミング言語 C』の初版から書かれていることで、世に数多ある C コンパイラも ISO/IEC 9899:1999 等の標準規格を前提として作成されているので、それ以外の使い方をされたいのであれば自己責任ということになります。
例えば NULL ポインタアクセスの検出をしたいとして
void hoge(void) { *(uint32_t*)NULL = 0xDEADBEEF; }
のようなコードを書いたとして、プログラマが期待した動作をするかは不明です。Cygwin64 上の gcc で試したところ
$ cat -n hoge.c 1 #include <stdint.h> 2 #include <stddef.h> 3 4 void hoge(void) 5 { 6 *(uint32_t*)NULL = 0xdeadbeef; 7 } $ gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-cygwin/5.4.0/lto-wrapper.exe Target: x86_64-pc-cygwin Configured with: /cygdrive/i/szsz/tmpp/gcc/gcc-5.4.0-1.x86_64/src/gcc-5.4.0/configure --srcdir=/cygdrive/i/szsz/tmpp/gcc/gcc-5.4.0-1.x86_64/src/gcc-5.4.0 --prefix=/usr --exec-prefix=/usr --localstatedir=/var --sysconfdir=/etc --docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin --without-libiconv-prefix --without-libintl-prefix --libexecdir=/usr/lib --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --enable-bootstrap --enable-__cxa_atexit --with-dwarf2 --with-tune=generic --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-graphite --enable-threads=posix --enable-libatomic --enable-libcilkrts --enable-libgomp --enable-libitm --enable-libquadmath --enable-libquadmath-support --enable-libssp --enable-libada --enable-libgcj-sublibs --disable-java-awt --disable-symvers --with-ecj-jar=/usr/share/java/ecj.jar --with-gnu-ld --with-gnu-as --with-cloog-include=/usr/include/cloog-isl --without-libiconv-prefix --without-libintl-prefix --with-system-zlib --enable-linker-build-id --with-default-libstdcxx-abi=gcc4-compatible Thread model: posix gcc version 5.4.0 (GCC) $ gcc -Wall -W -S -O2 hoge.c -o - .text hoge: movl $0, 0 ud2 $
不正命令例外をおこす命令を出力しました。
> これってNULLポインタの所に書き込みだからまずいんじゃないかな。読み出しならば問題無いと思います。
試してましたが
$ cat -n hoge.c 1 #include <stddef.h> 2 3 char readNullPtr(void) 4 { 5 return *(const char*)NULL; 6 } $ gcc -Wall -W -S -O2 hoge.c -o - .text readNullPtr: movzbl 0, %eax ud2 $
読み出しでも不正命令例外をおこす命令を出力しますね。
> fujita nozomu さんのコンパイラはパソコンのプログラム開発のCコンパイラでしょう。
> そのためユーザはNULLポインタの所をアクセスしないと言う前提の元にコンパイルし、前述のコンパイル結果になったのかなと思っています。
パソコンのプログラム開発用と限ったものではなく、出力するアーキテクチャは x86 と x64 に対応しており、例えば MMU を持たない80376をターゲットとして Windows や Linux の様な OS を使わない組み込み用途にも使えるものです。
> 「NULL が何のオブジェクトも指さない」と言われても、ゼロ番地からメモリーダンプするのに困るじゃないか。> NULLポインタを使ってアクセスしたらエラーだよなんて、コンパイラが勝手に判断したら困るだろうと思いました。
C の言語仕様として NULL は何のオブジェクトも指さないことは決められているので、安全確実にそれを行える方法はありません。
> NoMaY さん紹介の qiita.com/AoiMoe/items/2554f78dc9c197d22109 も参考にしました。
記事にある `-fno-delete-null-pointer-checks' のコンパイル指示を gcc に与えると
$ cat -n hoge.c 1 #include <stddef.h> 2 #include <stdint.h> 3 4 void writeNullPtr(void) 5 { 6 *(uint32_t*)NULL = 0xDEADBEEF; 7 } 8 9 char readNullPtr(void) 10 { 11 return *(const char*)NULL; 12 } 13 14 void hoge(void) 15 { 16 extern volatile int* hogehoge; 17 while (*hogehoge) { 18 ; 19 } 20 while (hogehoge) { 21 ; 22 } 23 } $ gcc -Wall -W -S -O2 hoge.c -o - .text writeNullPtr: movl $0, 0 ud2 .text readNullPtr: movzbl 0, %eax ud2 .text hoge: movq .refptr.hogehoge(%rip), %rax movq (%rax), %rdx .L4: movl (%rdx), %eax testl %eax, %eax jne .L4 .L5: jmp .L5 $ gcc -Wall -W -S -O2 -fno-delete-null-pointer-checks hoge.c -o - .text writeNullPtr: movl $-559038737, 0 ret .text readNullPtr: movzbl 0, %eax ret .text hoge: movq .refptr.hogehoge(%rip), %rax movq (%rax), %rdx .L4: movl (%rdx), %eax testl %eax, %eax jne .L4 testq %rdx, %rdx je .L3 .L8: jmp .L8 .L3: rep ret $
NULL ポインタ先への読み書きはそれを行うコードが出力され、一度アクセスしたポインタの NULL チェックを省く最適化も行われなくなるようです。
この辺りの話は使用するコンパイラに依存するものであり、C の言語仕様に反する NULL ポインタアクセスを安全に行う方法がどの処理系にも等しく提供されている、というものではありません。
> 「NULL が何のオブジェクトも指さない」と言われても、ゼロ番地からメモリーダンプするのに困るじゃないか。
ゼロ番地からメモリーダンプできて当たり前、と考えるのは間違いです。
> 5 return *(const char*)((void *)0); > > ならどうでしょう
$ cat -n hoge.c 1 char readNullPtr(void) 2 { 3 return *(const char*)((void *)0); 4 } $ gcc -O2 -S hoge.c -o - .text readNullPtr: movzbl 0, %eax ud2 $
変わらんですね。