在其他语言中,有的语言(例如 java、C#)直接取消了指针的相关语法,但由此就必须引入 “值类型” 和 “引用类型” 的概念。例如在 java 中,存在 “实” 和 “名” 的概念:
public static void Demo() {
int[] arr = new int[10];
int[] arr2 = arr; // “名”的复制,浅复制
int[] arr3 = Arrays.copyOf(arr, arr.length); // 用库方法进行深复制
}
本质上来说,这个 “名” 就是栈空间上的一个指针,而 “实” 则是堆空间中的实际数据。如果取消指针概念的话,就要强行区分哪些类型是 “值类型”,会完全复制,哪些是 “引用类型”,只会浅复制。
C# 中的结构体和类的概念恰好如此,结构体是值类型,整体复制,而类是引用类型,要用库函数来复制。
而还有一些语言保留了指针的概念(例如 Go、Swift),但仅仅用于明确指向和引用的含义,并不提供指针偏移运算,来防止出现越界问题。例如 go 中:
func Demo() {
var a int
var p *int
p = &a // OK
r1 := *p // 直接解指针是OK的
r2 := *(p + 1) // ERR,指针不可以偏移
}
swift 中虽然仍然支持指针,但非常弱化了它的概念,从语法本身就能看出,不到迫不得已并不推荐使用:
func f1(_ ptr: UnsafeMutablePointer<Int>) {
ptr.pointee += 1 // 给指针所指向的值加1
}
func demo() {
var a: Int = 5
f1(&a)
}
OC 中的指针更加特殊和 “奇葩”,首先,OC 完全保留了 C 中的指针用法,而额外扩展的 “类” 类型则不允许出现在栈中,也就是说,所有对象都强制放在堆中,栈上只保留指针对其引用。虽然 OC 中的指针仍然是 C 指针,但由于操作对象的 “奇葩” 语法,倒是并不需要太担心指针偏移的问题
void demo() {
NSObject *obj = [[NSObject alloc] init];
// 例如调用obj的description方法
NSString *desc = [obj description];
// 指针仍可偏移,但几乎不会有人这样来写:
[(obj+1) description]; // 也会越界
}
(六)隐式类型转换
隐式类型转换在一些场景下会让程序更加简洁,降低代码编写难度。比如说下面这些场景:
double a = 5; // int->double
int b = a * a; // double->int
int c = '5' - '0'; // char->int
但是有的时候隐式类型转化却会引发很奇怪的问题,比如说:
#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
void f1() {
int arr[5];
size_t size = ARR_SIZE(arr); // OK
}
void f2(int arr[]) {
size_t size = ARR_SIZE(arr); // WRONG