TL;DR —
rng.Offset(rowDelta, colDelta)は、rngから指定した行数・列数だけ ずらした新しい範囲を返します。これは場所ではなく一歩です。同じサイズを 保ち、rng自体は変えず、何も選択しません。2 つの数値は相対的です —Offset(1, 0)は 1 行下、Offset(0, 0)はそのセル自身、負の値は上・左へ 進みます。Offset は移動し、Resizeはサイズを変えます。この 2 つを組み合わせる と、VBA で最も便利な範囲イディオムが手に入ります。data.Offset(1).Resize(data.Rows.Count - 1)— 「見出しを除くすべて」です。
Offset は、マクロが「今見つけたセルの隣のセル」と言うための方法です。一致した
値・合計・見出しを見つけたところで、その 1 列隣、あるいは 1 行下のセルを読み書き
したくなります。その相対的な移動こそが Offset の仕事のすべてで、引数を座標として
ではなく歩数として読むようになれば、自然と身につきます。
この記事で学べること
- 考え方の軸:Offset は絶対アドレスではなく、相対的な一歩
- 1 つずれる罠 — なぜ
Offset(1, 1)は A1 ではなく B2 に着くのか - Offset はサイズを保ち、
Resizeはサイズを変える — そしてなぜ普通は両方必要なのか - 見出しを飛ばす定番イディオム
Offset(1).Resize(n - 1)の解説 Range.Offset(VBA)がOFFSET()ワークシート関数とどう違うのか
考え方の軸:Offset は場所ではなく一歩
あるセルに立ってみましょう。Offset(rowsDown, colsRight) はその歩数だけ進み、
着いたセル(またはブロック)を渡します — まったく新しい範囲です。元の範囲は
そのまま。移動は相対的なので、同じ Offset(1, 0) は、A1 からでも Z500 からでも
「1 行下」を意味します。
Sub OffsetSteps()
Debug.Print Range("B2").Offset(1, 0).Address ' -> $B$3 (1 行下へ)
Debug.Print Range("B2").Offset(0, 1).Address ' -> $C$2 (1 列右へ)
Debug.Print Range("B2").Offset(-1, 0).Address ' -> $B$1 (1 行上へ)
Debug.Print Range("B2").Offset(0, 0).Address ' -> $B$2 (移動なし)
End Sub
Offset(0, 0) がそのセル自身になることに注目してください。これが、引数が位置では
なく差分(デルタ)である証拠です。絶対的なセルが欲しいなら、A1 からのオフセットを
計算するのではなく Cells(row, col) を使います。Offset は「すでに持っている何かからの
相対」のためのものです。
初心者がはまるルール:1 つずれる罠
Offset の最も多いバグは、引数を座標として読んでしまうことです。Offset(1, 1) は、
セル A1 や「1 行目・1 列目」を意味しません。基準セルから1 行下・1 列右を
意味します。A1 からなら B2 に着きます。
Range("A1").Offset(1, 1).Address ' -> $B$2($A$1 ではない)
Range("A1").Offset(0, 0).Address ' -> $A$1
2 つの引数を毎回「下、右」と声に出して読めば、このバグは消えます。これは定番の
ループパターンの説明にもなります。見つけた各行の隣に結果を書くには、ループ
カウンターの分だけオフセットします。そして最初の行は Offset(1, …) ではなく
Offset(0, …) を使います。
誰もがつまずく違い:Offset は移動、Resize はサイズ変更
Offset は範囲の大きさを決して変えません — 形全体をスライドさせます。10 セルの
列を移動すれば、別の場所に 10 セルの列が得られます。
Range("A1:A10").Offset(0, 1).Address ' -> $B$1:$B$10 (10 セル、右へ移動)
範囲のサイズを変えるには、別のメソッド Resize(rows, cols) が必要です。これは
左上のセルを起点にブロックを描き直します。
Range("A1").Resize(5, 2).Address ' -> $A$1:$B$5 (5 行、2 列)
この 2 つは範囲演算の両輪です。Offset はどこを、Resize はどれくらいの大きさを 決めます。両方をセットで使う場面は絶えずあります — 最も多いのは見出し行を落とす ときです。
覚える価値のあるイディオム:Offset + Resize で見出しを飛ばす
見出しを含む表があり、データ本体だけが欲しいとします。1 行下にオフセットして 見出しから外れます — ただし Offset だけでは高さが同じままなので、範囲はデータより 1 行はみ出てしまいます。Resize でその 1 行分だけ縮めて戻します。
Sub DataBodyOnly()
Dim tbl As Range, body As Range
Set tbl = Range("A1").CurrentRegion ' 見出し + データ
Set body = tbl.Offset(1).Resize(tbl.Rows.Count - 1)
body.Select ' データ行だけ、見出しなし
End Sub
Offset(1)(列の差分は既定で 0)はブロック全体を 1 行下にスライドさせ、
Resize(tbl.Rows.Count - 1) は、はみ出した最下行を切り落とします。結果は見出しを
取り除いたデータ本体 — 並べ替え・コピー・消去にちょうど欲しい範囲です。この 1 行が、
A2:A の文字列を手で組み立てる多くの作業を置き換えます。tbl をベタ書きせずに
求める方法は、最終行の求め方 と
CurrentRegion を参照してください。
シートの端の罠
Offset は相対的なので、ワークシートの端を越えて進むと、無言で丸め込まれるのでは なくエラーになります。
Range("A1").Offset(-1, 0) ' 実行時エラー 1004 — 1 行目より上には行けない
マクロが左上の角の近くで上や左へ進む場合(見出しを探すときによくあります)は、その
移動をガードするか、上向きにオフセットする前に rng.Row > 1 を確認しましょう。
メソッドの Offset とワークシート関数の OFFSET
名前も考え方も同じですが、同じ道具ではありません。ワークシート関数の
OFFSET() はセルの中に置かれ、揮発性(volatile)で
(変更のたびに再計算され、ブックが遅くなることがあります)、数式のために値または
参照を返します。Range.Offset は VBA のメソッドで、Range オブジェクトを返し、
揮発性ではなく、コードが動くときだけ実行されます。マクロを書いているならメソッド
を使いましょう。ワークシート関数に手を伸ばすのは、数式の中で常に自動更新される
参照が必要なときだけです。
ExcelMaster の活用
Offset を多用するコードは、たいてい 1 つのものを組み立てています。見つけた何かに 対して相対的に置かれる結果 — 各一致の隣のフラグ、1 列隣の累計、ラベルの下の行から コピーした値です。ExcelMaster は、その意図を平易な日本語で受け取ります — 「延滞している請求書それぞれの隣に、延滞日数を書いて」といった具合に — そして 相対参照のロジックを生成するので、差分を手で数えたり、1 つずれるバグをデバッグ したりせずに済みます。
きっちり書いたマクロのループの中では、これからも Offset を書くでしょう。しかし
「各行の隣に結果を置く」という日々の作業なら、(1, 0) と (0, 1) を頭の中で考える
より、配置を言葉で説明する方が速く、間違えにくいのです。
よくある質問
VBA の Offset は何をするのですか?
rng.Offset(rowDelta, colDelta) は、rng から指定した行数・列数だけずらした新しい
範囲を、同じサイズのまま返します。相対的な移動です。Offset(1, 0) は 1 行下、
Offset(0, 1) は 1 列右、負の値は上または左へ動きます。元の範囲を変えることも
選択することもありません。
なぜ Offset(1, 1) はセル A1 を返さないのですか?
引数が座標ではなく差分だからです。Offset(1, 1) は基準セルから 1 行下・1 列右を
意味するので、Range("A1").Offset(1, 1) は B2 になります。相対的な一歩ではなく
絶対的なセルが欲しいときは Cells(row, col) を使いましょう。
Offset と Resize の違いは何ですか?
Offset は範囲をサイズを変えずに移動し、Resize(rows, cols) は移動せずに左上の
セルを起点にサイズを変えます。両者はよく組み合わせます —
rng.Offset(1).Resize(rng.Rows.Count - 1) は、1 行下げて 1 行縮めることで見出し行を
飛ばします。
Offset で見出し行を飛ばすには?
table.Offset(1).Resize(table.Rows.Count - 1) を使います。Offset(1) はブロックを
1 行下げて見出しの先へ動かし、Resize は、はみ出した最下行を切り落として、データ
本体だけを残します。
VBA の Range.Offset は OFFSET ワークシート関数と同じですか?
いいえ。Range.Offset は Range オブジェクトを返す VBA のメソッドで、揮発性では
ありません。OFFSET() ワークシート関数はセルの中に置かれ、揮発性で(絶えず再計算
されます)、数式のために値または参照を返します。マクロではメソッドを、常時更新
される数式の参照が必要なときだけ関数を使いましょう。
検証環境
検証環境: Excel 365(Windows 11)、VBA 7.1 — 最終確認 2026-07-30。
関連ガイド: VBA Last Row · VBA UsedRange vs CurrentRegion · VBA Range · OFFSET Worksheet Function
