函数与指针两个方向:指针进函数(改外部变量、省拷贝)、从函数返回指针(别返回局部变量)。
C 里所有参数都是按值传递:函数拿到的是实参的一份拷贝。想让函数改外部的变量,只能传它的地址:
void bad_swap(int a, int b) { int t = a; a = b; b = t; } // 改了副本,无效
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; } // 改的是指向的内存
int x = 1, y = 2;
swap(&x, &y); // 传地址,x、y 真的交换了
心法(同第 7 章):要改谁,就传谁的地址。 改
int传int*,改指针传T**。
swap、scanf("%d", &x)、出参模式)。void fill(int *a, int n, int v) { // 数组/大缓冲区都走指针
for (int i = 0; i < n; i++) a[i] = v;
}
局部变量随函数返回栈帧销毁,返回它的地址即悬空指针:
// 返回局部变量地址:函数返回后 s 已不存在
char *bad(void) {
char s[64] = "hello";
return s;
}
// 更隐蔽:返回局部数组元素地址
int *bad2(void) {
int a[10] = {0};
return a; // a 在栈上,返回即失效
}
能安全返回的三种指针:
static / 字符串字面量 / 全局):char *ok1(void) { static char s[64] = "hello"; return s; }
char *ok2(void) { return "hello"; } // 字面量在只读段,永存
char *ok3(void) {
char *s = malloc(64);
strcpy(s, "hello");
return s; // 堆内存由调用者释放
}
char *ok4(char *buf) { strcpy(buf, "hello"); return buf; }
字符串字面量不可写、char[] 可写,但进了函数都变成 char* 无法区分。解决办法:用 const 声明只读意图,把「会不会改」写进签名:
void show(const char *s); // 只读,可传字面量
void upper(char *s); // 会写,调用者必须传可写数组
C 函数只能返回一个值,多返回值靠指针出参:
// 求商和余数,两个结果通过指针带出
int divmod(int a, int b, int *rem) {
*rem = a % b;
return a / b;
}
int rem;
int q = divmod(17, 5, &rem); // q=3, rem=2
swap 的两个 int* 对应两次 mov [rsi] 类间接寻址,零拷贝、极快。void f(int& x) 是「传指针」的语法糖,写法更清爽,代价是调用处看不出是否会被改(swap(x,y) 不显式取地址)。return a, b 直接打包成元组,天然多返回值。① C 只有值传递,改外部变量靠传地址;② 传指针省拷贝、支持出参;③ 返回指针只返回静态/堆/入参地址,绝不返回局部变量地址。