什么是间址运算符-间址运算符含义
间址运算符啊,说白了就是给变量加点“路标”。
你想想,要是直接写个变量名,像开关一样,那多费事。
有时候你得先想想变量名长啥样,有时候变量名本身是个函数,这时候你就得先查表找找那个名字对应的地址,再去访问它的内存才行。
这就好比去餐厅点菜,直接说“我要那个菜单”,得知道菜单在哪;要是说“我要那个叫‘菜谱’的牌子”,还得知道牌子在哪个柜子里,再去拿。 在汇编机器里,这操作最典型的就是用 `MOV` 指令。
比如 `MOV AX, BX`,这指令不光告诉 CPU“把 BX 里的内容搬过来”,还顺便告诉寄存器 BX 里存的地址就是 AX 的存放地址。
这就叫间接寻址,它把寻址方式升级到了一个新维度:地址变成了地址。在这种模式里,内存里的地址本身就是个变量,而这个变量本身又有个地址,哪怕这变量是个函数,它被调用前的地址也是一种特殊的“变量”。 举个例子,假设你有一堆数据,一块叫 `ptr` 的数组,每个元素是 4 字节。
要是让你直接用 `mem[0]` 去取,那是直接寻址。但要是你想让 `mem` 本身是个变量,你得写 `ptr[0]` 要么 `mem[ptr]`。
这时候库里有个 `ptr` 变量存着 `0x00000123`。
要是你直接写 `mem[0]`,CPU 得先查 `mem[0]` 的地址是不是 `0x00000123`,然后再去 `0x00000123` 这个地址里找 `0`。
这就是间接寻址,出于它先把一个地址存到了另一个地址里。 还有一种叫间接寻址的变种,就是变量本身是个函数。假设有个变量 `func`,它代表一个加减法操作。你没法直接写 `func[0]` 出于函数名不能随意用下标,你得先查 `func` 的地址,找到它的符号表,看看它指的就是 `add` 函数,然后再去调用它。
这时候 `func` 这个变量存住的地址就是 `0x10000`。你写 `func[0]` 的时候,实际上是在说“把 `add` 函数在 `0x10000` 这个地址里的值取出来,存到 `0` 这个位置”。 这两类间接寻址,实际上核心差别就在于变量本身能不能被直接赋值。直接的多像一根管子,数据流得顺理成章;间接的多像个分叉路口,数据得先经过一次“转站”。 在 C 语言里,大家略微好办上手点。
比如 `int i; int j = i;`,这别看写的是直接赋值,但在汇编里实际上跟间接寻址没区别。出于 `i` 这个变量,它本身就被当作内存地址,CPU 得先去查 `i` 的地址,然后往里面存数据。查地址的过程,就是间接寻址;存数据的过程,就是直接赋值。有些编译器会特别优化,把这两步合并成一张表查,省得 CPU 多跑一次。 再比如 C 里的 `int a[10];` 这种数组。直接写 `a[0]` 就挺好办。但要是你想让 `a[0]` 跟着某个变量 `v` 动,就得写成 `v[0]`。
这时候 `v` 的值变了,数组 `a[0]` 也跟着变。并且 `v[0]` 这个表达式,在内存里实际上不是数组名 `v`,而是指向数组 `a` 开头那个元素的地址。
这就把数组的指针和数组名给搞混了。你访问的是指针指向的地址,而不是数组本身的符号。 再拿 C 语言里的函数参数来说。你定义 `void add(int x, int y);`。
要是你写 `add(10, 20)`,编译器会存 `x` 和 `y` 的地址。直接写没难题。但你要是想写 `add(x, y)` 这种形式,编译器实际上先查 `x` 的地址,是不是 `add` 的入参地址 0。
要是 `x` 是 `add` 的入参,那 `x` 的地址就是 `0x0000`。
这时候 `x` 就是个特殊的指针,代表 `add` 函数的第一个参数地址。你写 `x` 来调用 `add`,实际上是间接访问。 这种间接调用在 C 里挺常见。
比如 `add(0, 0)` 和 `add(x, y)`,编译器有时候为了优化,会查 `0` 是不是 `add` 的参数,要么查 `x` 是不是 `add` 的参数。查的过程就是间接寻址。 在汇编语言里,这种区分更明显。
比如 `MOV AX, BX` 是直接赋值,AX 和 BX 都在同一层级的符号表里。但你写 `MOV AX, [BX]`,这里 BX 就是个地址,CPU 得先去查 BX 的地址,然后往 AX 里写。
这就是间接寻址。
同理,`MOV BX, [BX+1]`,BX+1 也是个地址,BX 得先查地址,然后再写。 还有一种叫“复合间接寻址”要么双重间接,比如 `MOV AX, [BX+1]`。
起初 CPU 查 BX+1 的地址,看是不是 `1`。
要是是,就写 `1` 到 AX。
要是 `BX+1` 本身也是个地址,比如存的是 `2`,那 CPU 就得再去查 `2`,看是不是 `3`。
这一步一步查,就是层层嵌套的间接寻址。 在汇编里,这种写法有时候会显得啰嗦,就连好办出错。
比如你把一个指针 `PA` 存到 `B`,再让 `B` 的指针指向 `C`,最终用 `C` 出栈。
要是 `C` 本身是个变量,你得查 `C` 的地址,再查 `C` 的地址... 直到查到栈顶。
这种情况在深度递归要么复杂的内存布局里特别常见。 在实际开发中,这类写法别看常见,但要注意边界情况。
比如间接访问的变量,要是它还没初始化好,指针指向的位置可能是垃圾数据,程序就炸了。
要么间接寻址的变量本身就是函数,别看灵活,但要是参数表没对齐好,也可能害得地址算错。 还有种最好办的间接寻址,就是间接地址。
比如你有个常量 `0x12345678`,你想把它存到 `0x00001111` 里,直接写 `0x00001111 = 0x12345678`。但要是你想让 `0x12345678` 这个值本身是个变量,你得先查 `0x12345678` 的地址,看它是不是 `0x00000000`。
要是是,你就把这个地址的地址,存到 `0x00001111` 里。 这种写法在某些架构里是合法的,但在现代 CPU 的三级地址转换(TSW)机制下,可能会触发地址映射表,害得指令执行得慢半拍。出于指令本身不是直接给内存写数据,而是给内存写地址,内存还得给内存写地址,CPU 得跑三遍地址映射表。 举例来说,假设你有一个 `int arr[100]`,你想让它等于 `0x00000000`。你直接写 `arr[0] = 0` 没难题。但你要是想写 `arr[0] = 0x00000000`,编译器会查 `0x00000000` 的地址,看它是不是 `arr` 数组的地址。查到了,就执行一次映射。
这时候 `0x00000000` 就是个地址,CPU 得去查这个地址,看是不是 `arr` 的入口地址。查到了,再执行一次映射。最终把 `0x00000000` 的值,存到 `arr[0]` 的内存里。 这种双重映射,效率上确实比直接写慢,特别是在循环里,每次循环都要多跑一次映射表。但在调试时挺有用,出于中间那个地址 `0x00000000` 就是调试工具能看到的,你不用关心它是不是数组名,直接看它存了啥值。 在 C 语言里,这种双重间接实际上能够通过编译器优化掉。
比如 `int x; x = 0x00000000;` 这种写法,编译器会直接把它当成直接赋值,忽略中间那个地址的映射。但有些编译器为了保守性,要么为了让代码看起来像间接寻址,可能会保留那种双重映射的指令。 总而言之,间址运算符就是给代码加个“拐弯”的指令。它让数据流能够从一个点,直接通向另一个点,就连通向三个点。
这增添了灵活性,却也增添了潜在的计算开销。在写代码的时候,你既要利用它的灵活性,又要自觉检查指针和数组的地址是否合法,避免越界要么未初始化。
毕竟,没有地址的“变量”,再动听也是空。
声明:演示网站所有内容,若无特殊说明或标注,均来源于网络转载,仅供学习交流使用,禁止商用。若本站侵犯了你的权益,可联系本站删除。
