首页 新闻 会员 周边 捐助

捡起c的指针

0
[已解决问题] 解决于 2025-08-30 18:02

int x = 5; // ① 申请一个盒子,贴标签 x,里面写数字 5
int *p = &x; // ② 申请第二个盒子,贴标签 p
// 把“盒子 x 的门牌号”抄到 p 里

 

Python 和 Java 都把“实参本身的重新指向”给禁掉了

只能:
  • 基本/不可变 → 完全改不了;
  • 可变对象 → 改内容可以,改指向不行

而C/C++ 可以把实参本身改掉,于是诞生了指针

在函数里交换两个整数
#include <stdio.h>

void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main(void) {
    int x = 1, y = 2;
    swap(&x, &y);        // 传入地址
    printf("x=%d y=%d\n", x, y);  // x=2 y=1
    return 0;
}
main栈帧        swap栈帧
┌-----┐        ┌-----┐
│ x 1 │  ◄-----┤ a ◄─┼---- 指向 x
│ y 2 │  ◄-----┤ b ◄─┼---- 指向 y
└-----┘        └-----┘
  • C 里 &x 得到的地址就是真实地址本身,传进去后 swap 里的 ab 不是副本哦,而是那个地址
  • Java 的“地址”是“引用值的副本”——传进去的是一个 8 byte 的副本哦,里面装的可是对象地址哦
复制代码
在 C 里发生了什么(图 + 真值)
main 栈帧         swap 栈帧
┌------┐          ┌------┐
│ x 1  │ ◄─ a ----┤ int*a│  (a 里存 0x7ff...100)
│ y 2  │ ◄─ b ----┤ int*b│  (b 里存 0x7ff...104)
└------┘          └------&x 产生 0x7ff...100(x 的真实地址)
这个 8 字节地址值 被压栈 传给形参 a,但地址本身不是副本;
你可以把 a 当成“指向 x 的指针变量”,通过 *a 直接读写 x。

java Java 里发生了什么(传引用副本)
void foo(Node n) { // n 是“引用副本” n.value = 99; // ✅ 改对象内部 n = new Node(); // ❌ 只改副本指向,外部无感 } Node p = new Node(5); foo(p); 内存示意: main 栈帧 foo 栈帧 ┌------┐ ┌------┐ │ p ----┼─┐ │ n ----┼─┐ (副本,值同 p) └------┘ │ └------┘ │ ▼ ▼ [Node] (指向同一对象) 传进去的仍是 8 byte 的“地址值”,但它是 拷贝; 因此 n = new Node(); 只是改副本,外层 p 丝毫不动。
复制代码
_java_python的主页 _java_python | 小虾三级 | 园豆:984
提问于:2025-08-30 17:11
< >
分享
最佳答案
0

定义时的 *:只是指针类型的标记

int *p = &x;

等价于:
int *p;
1. 先声明:p 是一个指针 p = &x; 
2. 再赋值:把 x 的地址存进去
此时的 * 不是运算符,只是类型说明符,“p 指向 x” 或 “p 存了 x 的地址”。
 
使用时的 *:才是“解引用”运算符,真正去内存取值/存值
printf("%d\n", *p);
// 这里的 * 才是运算符,叫做“解引用” *p = 10;
// 也是运算符,把 10 写到 p 所指的地址
这里的 * 是解引用运算符,“取 p 指向的内容” 或 “把 p 指向的那块内存设为 10”

*a = *b 就是把 b 指向的值写到 a 指向的那块内存

& 取地址,* 按地址拿值;

 

 

Python 跟 Java 在这一点上是同一个套路:

 

  1. Python 的“变量绑定”也是值传递,传进去的是“对象引用的副本”。
  2. 函数里 重新绑定(param = new_obj)只是改了这个副本,外层实参仍然指向原来的对象。
  3. 如果对象是可变对象,你可以在函数里 改它的内部内容,这跟 Java 完全一样。
  4. # 1. 不可变对象(类似 Java 的基本类型)
    def foo(x):
        x = 99          # 重新绑定:只改副本
    
    a = 5
    foo(a)
    print(a)            # 5,外层不变
    
    # 2. 可变对象(类似 Java 的引用类型)
    def bar(lst):
        lst.append(99)  # 改对象内部
        lst = [7, 8]    # 重新绑定:只改副本
    
    b = [1, 2]
    bar(b)
    print(b)            # [1, 2, 99],原对象被改;但 lst = [7,8] 对外层无影响

    Java 的规则

    • 原始类型(int、double…):传的是数值的副本,函数里改形参,对外面零影响。
    • 引用类型(数组、对象):传的是引用值的副本——里面存的是对象地址的副本。
    •  原始类型:绝对改不了
      java
      void foo(int x) {
          x = 10;      // 只改了副本
      }
      int a = 5;
      foo(a);
      System.out.println(a);  // 还是 5
      
      引用类型:只能改对象内部,改不了实参本身指向谁
      class Node { int v; Node(int v){this.v=v;} }
      
      void foo(Node n) {
          n.v = 10;          // ✅ 能改对象内部
          n = new Node(99);  // ❌ 只改了副本指向,外部不受影响
      }
      
      Node p = new Node(5);
      foo(p);
      System.out.println(p.v); // 10,不是 99

       

 

java 和 Python 都是“值传递”

这里的“值”不是指“对象本体”,而是指:
  • Java:把**引用值(地址)**拷贝一份传进去。
  • Python:把**对象指针(PyObject*)**拷贝一份传进去。

不是语言缺陷,而是一种设计权衡(trade-off)。
把“能不能把实参本身改掉”这件事放在语言设计层面,Java 和 C 各走了不同路线,没有绝对优劣,而是一种有意为之的安全设计。

_java_python | 小虾三级 |园豆:984 | 2025-08-30 17:18
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册