Skip to content

为什么要聊这个?

的确,这是非常简单也没有太大意义的问题,但是因为各种各样的原因,教学、传播、造谣、历史,等等。很多人已经无法明确这个问题了,所以我们单独讲一下。

注意,聊 C++。

数组与指针的关系是?

数组不是指针,指针不是地址,(引用不是指针)。

多数人提起数组与指针的时候,默认的是 数组与指针对象,而不是数组与指针类型。 这造成了很多的问题,同时衍生出很多的莫名其妙的说法,比如:

  • 数组名

并且甚至有人将,“数组名”,和“数组”区分,用来指代两种东西,我不清楚这是否是目前大学的考点,但我的猜测是,他们谈论起“数组名”的时候,应该要表达的是 数组对象,但是,萌新能区分对象和类型? 这太神奇了,不管。

这种说法就如同 “int 名” 一样愚蠢。

cpp
int a = 0;     // 这是一个 int 类型的对象,它的名字是 a
int arr[10]{}; // 这是一个数组类型(int[10])的对象,它的名字是arr

如果你认可“数组名”这种愚蠢的说法,应该也认可“int 名”、“double 名”、“string 名”。

数组和指针的区别

有很多人都有着错误的想法,诸如:

我知道数组不是指针,但是它用起来和指针一样啊,除了 sizeof 的时候,那何必纠结呢?

说出这种话的原因非常简单,学少了

我们下面详细聊聊能有多少区别,由常见的,到稍微不那么常见的。

函数传参数组

cpp
void test(int*){}
void test2(int**){}

int arr[10]{};
int arr2[100]{};

test(arr);        //所以 int[10] == int*?int[10][10] == int**?
test2(arr2);      //Error

有些人可能会想了,改成

cpp
void test2(int[10][10]){}

int arr2[100]{};

test2(arr2);

你真的觉得函数 test2 的形参类型是 int[10][10] 吗?不是的,只是表象,这里实际会退化为数组指针 void test2(int(*)[10]),这是语言的基本规则。

形参列表中的每个函数形参的类型根据下列规则确定:

  1. 首先,以如同在任何声明中的方式,组合声明说明符序列 和声明符以确定它的类型。
  2. 如果类型是“T 的数组”或“T 的未知边界数组”,那么它会被替换成类型“指向 T 的指针”
  3. 如果类型是函数类型 F,那么它被替换成类型“指向 F 的指针”
  4. 从形参类型中丢弃顶层 cv 限定符(此调整只影响函数类型,但不改动形参的性质:int f(const int p, decltype(p)); 和 int f(int, const int); 声明同一函数)

参见文档。这里不想计较这些愚蠢的设计和模糊,只是为了简单告诉各位而已,就这么一个简简单单的小问题,也能注意到,数组和指针的不同。

  • 存在从数组类型的左值和右值到指针类型的右值的隐式转换:它构造一个指向数组首元素的指针。凡在数组出现于不期待数组而期待指针的语境中时,均使用这个转换。

数组引用

cpp
template<typename T,std::size_t N>
void print(T(&arr)[N]) {
    for (const auto i : arr) {
        std::cout << i << ' ';
    }
    std::cout << '\n';
}

int arr[]{ 1,2,3,4 };
int* p = arr;

print(arr);    //OK

print(p);      //Error

我希望各位不要忘记:

  • 数组的长度,是数组类型的一部分,数组的类型信息包括长度。

模板中数组类型

其实上一个例子也是用了模板,不过无所谓,我们再聊点:数组类型在 C++类型系统本身的意义

数组它是一类类型,它类型本身就有意义,天生的第一无二的意义,它是数组,数组就是数组。

我们可以写模板的时候对数组类型进行偏特化。没必要自己写,看标准库。

cpp
template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

template <
    class T,
    class Deleter
> class unique_ptr<T[], Deleter>;

指针是地址?

指针是指针,指针指代一类类型,比如:int*double*,说类型是什么,本身很莫名其妙,如果要聊指针对象,也一样莫名奇妙,指针对象是指针对象,它还能是啥?

指针对象可以存储别的对象的地址,就这么简单。

std::vector 能存的东西多了去了,那它是什么?

总结

聊的不算非常完全,但,也暂时够了,可以提 pr 修改。