ポインタ配列の const が理解できなかったのでメモ

検証コード

const char* const foo[] = {"foo","bar","baz"};

extern void __print(const char* buf);
void main(void) {
	char* str;
	__print(foo[0]);
}

これの foo のついている2つのconstを消したりつけたりする。__print は最適化で消されないように extern してるだけで特に意味はない。

arm-none-eabi-gcc -c c.c -o a.o && arm-none-eabi-objdump -t a.o

このようにして symbol table を見て、どのセクションに配置されるかを確認する。

char* foo[]

...
00000000 g     O .data  0000000c foo
00000000 g     F .text  0000002c main
...

当然 .data に配置される。

const char* const foo[]

...
0000000c g     O .rodata        0000000c foo
00000000 g     F .text  00000028 main
...

当然 .rodata に配置される。

const char* foo[]

...
00000000 g     O .data  0000000c foo
00000000 g     F .text  0000002c main
...

.data に配置される。

これがいまいちよくわからない。ここにconstをつけても以下はコンパイルエラーにならない。何が const になっているのだろう?「ポインタの配列 foo」そのもの?

foo[0] = "piyo";

しかし以下のように「ポインタの配列 foo」そのものを更新しようとしてもエラーになるので、そもそも「ポインタの配列 foo」そのものを更新しようがない気がする。

	char* bar[] = {"xxx"};
	foo = bar; //=>  error: assignment to expression with array type

char* const foo[]

0000000c g     O .rodata        0000000c foo
00000000 g     F .text  00000028 main

.rodata に配置される。

この場合、foo[0] への代入はコンパイルエラーになる。「ポインタの配列」の「ポインタ」が const になっている?

「ポインタ配列」の場合、上記のように「ポインタの配列 foo」そのものを更新しようがないので、.rodata で良いのだろうか?

foo[0] = "piyo"; //=> error: assignment of read-only location 'foo[0]'

char* const foo と const char* const foo は全く同じバイナリが吐かれる。ポインタ配列の最初の const は無意味なのだろうか?

  1. トップ
  2. tech
  3. C言語のポインタ配列の const の効果

前につくったアッテネータを測ってみる 高周波用アッテネータを作ってみる | tech - 氾濫原

NanoVNA だと 300MHz 付近にデコボコがあるようにみえますが、これはおそらく 0.5〜900MHz でキャリブレーションした状態で、0.5〜500MHz の範囲を見ている (校正の補完を使っている) せいと思われるので、再度この範囲でキャリブレーションしなおしてみます。(300MHz を超えると高調波モードを使うので段差ができやすい)

消えました。

リターンロス (S11)

前回の結果の再掲 (TG つきスペアナ + リターンロスブリッジの結果)

挿入損失 (S21)

前回の結果 TGつきスペアナでの結果

  1. トップ
  2. tech
  3. NanoVNA の測定メモ
  1. トップ
  2. nanovna
  3. NanoVNA の測定メモ

#!/usr/bin/env ruby

require 'pp'

D = Struct.new(:sec, :size, :name)

target = ARGV.shift

sram = `arm-none-eabi-objdump -t '#{target}'`.chomp.split(/\n/).
	select {|l| /\.bss|\.data/ =~ l }.
	map {|l|
		sec, size0 = *l.split(/\t/)
		size, name = *size0.split(/\s+/) if size0
		D.new(sec, size.to_i(16), name)
	}

total = sram.map {|i| i.size }.reduce {|r,i| r + i}

sram.sort_by {|i|
	i.size
}.each { |i|
	puts "% 3d%% % 10d %s" % [i.size.to_f / total * 100, i.size, i.name]
}

puts "total: %d bytes" % total

こういうのを書いて

$ foo  build/ch.elf

と実行すると

...
  0%        124 SD1
  0%        132 USBD1
  1%        192 dump_buffer
  1%        208 ch_idle_thread_wa
  2%        384 SDU1
  2%        384 rx_buffer
  4%        640 waThread2
  6%        960 waThread1
  7%       1064 impure_data
 10%       1616 measured
 10%       1616 trace_index
 13%       2048 spi_buffer
 30%       4552 current_props
total: 15081 bytes

こういう感じである程度わかる。

  1. トップ
  2. tech
  3. SRAM 使用量のカウント

avstack.pl である程度できる。

avstack.pl の $objdump を適当にアーキテクチャをあわせて変えておく。

my $objdump = "arm-none-eabi-objdump";

このうえで、GCC のコンパイルフラグに -fstack-usage をつける。そうすると .o の同じ名前で .su というファイルができる。

asm から変換されたファイルとかでは .su は作られないので、.su がない .o を除外して、avstack.pl の引数にすべて与える。

$ ./avstack.pl build/obj/Font5x7.o build/obj/adc.o ...
  Func                               Cost    Frame   Height
------------------------------------------------------------------------
> Thread1                             792       20       16
  sweep                               772       84       15
  ui_process                          688       12       14
  ui_process_touch                    676       12       13
  touch_pickup_marker                 664       52       12
  drag_marker                         612       44       11
  ui_process_lever                    608       12       12
  ui_process_normal                   596       28       11
> menu_marker_sel_cb                  580       12       11
...

すると、使用率順に表示してくれる。> マークはどこからもそれが呼ばれていない関数、つまり呼び出し元と思われるもの。

Cost は最大スタック利用量。Frame は該当関数のスタック利用量 (コールコストを含む)。Height は最大コールスタック数。

Height * コールコストが意外とでかい。

  1. トップ
  2. tech
  3. スタック使用量の静的解析