Re: [问题] 双重指标

楼主: loveme00835 (发箍)   2020-06-15 23:52:48
※ 引述《spong (请输入ID)》之铭言:
: #include<iostream>
: using namespace std;
: int main(void)
: {
: int x = 5;
: int *ptr=&x;
: int **temp =&ptr;
: cout << "&ptr="<<ptr<< endl;
: cout << "*ptr=" << *ptr<< endl;
: cout << "&temp=" <<temp <<endl;
: cout << "**temp="<<**temp <<endl;
: return 0;
: }
: 既然双重指标,是指标的指标,为什么不能宣告int *temp 去指向 int *ptr呢?
: 一定要用**temp?
千万不要把指标的值用来理解指标怎么运作, 因为在没有搭配型别
的情况是没有意义的, 用不合法的操作取得的指标也是不能用的.
阵列和类别一样对于 sub-object (子物件) 的位址关系是有明确定
义的, 对于大小为 N 的阵列 A 来说, 假如索引 0 <= i, j < N,
而且 i < j, 那么 &A[i] < &A[j]. ([expr.add]/4.2). 阵列名称
单独做为叙述使用的话具备型别和它宣告的时候一样, 但当你对它
用 subscript 运算子的时候, 你会先得到转型之后的指标, 才会得
到指标运算的结果, 此指标指向阵列的第一个元素 ([conv.array]/1)
. A[i] 等同于 *(A + i), 都是指标运算的结果 ([dcl.array]/9).
只是你要确定这个 i 必须是合法的阵列索引 (0 <= i < N). 另外
单独的物件本身被视为是大小为 1 的阵列 ([expr.add]/4.2).
有了以上游戏规则, 就可以说明下面操作全是不合法的:
int a[10];
*(a + 10) = 0; // error
int j, i;
if ((&i + 1) == &j) {
*(&i + 1) = 0; // error
}
即使指标値相同, 但是因为来源不同, 算出来的位址也会有合法与
否的问题. 所以我们必须从型别出发, 才能理解每个操作真正的涵
义, 而不是转成 void* 印出来看就行.
假设我们有一个阵列, 阵列元素型别为 T, 阵列大小为 N, 那我们
一般宣告阵列的时候会是用下面的方式:
T array[N];
我们可以用下面的工具搭配 decltype 运算子来检视每一个叙述的
型别为何:
int array[10];
template <typename>
struct print_type;
print_type< decltype(array) >(); // compilation error
上面这行会编译错误, 不过是预期的结果, 现阶段在 C++ 我们要看
到完整的型别还只能透过这样的方式, 编译器会把型别放在错误讯
息里:
error: invalid use of incomplete type 'struct print_type<int [10]>
从上面我们就可以看到 array 的型别是 int[10], 获取阵列元素型
别的方法会比较丑一点, 记得引入 <type_traits> 标头档:
#include <type_traits>
print_type< std::remove_extent<decltype(array)>::type >();
std::remove_extent 可以用来取得元素的型别, 另外还有一个工具
是 std::extent 可以让我们知道阵列有几个元素, 两者搭配就可以
更深入了解一个阵列型别:
template <std::size_t>
struct print_size;
int array2[3][4];
print_size<
std::extent<decltype(array2)>::value >(); // 3
print_type<
std::remove_extent<decltype(array2)>::type >(); // int[4]
从上面的结果我们可以看到 array2 是一个大小为 3 的 int[4] 阵
列. 没错, 在 C++ 里阵列是允许巢状的, 严格说起来目前只有阵列
的阵列, 还没有多维阵列的概念.
接着我们就可以用以上的背景知识来检验你上一篇的程式码:
int ar[3][4] = {{2,4,6,8},{1,3,5,7},{10,11,12,13}};
ar // type = int[3][4], size = 3
ar+3 // type = int(*)[4], 3 exceeds the size of ar
*(ar+3) // type = int(&)[4], same as 'ar[3]' but we cannot
// de-ref an element which is not in array.
*(ar+3)+4 // type = int*, same as 'ar[3] + 4', meaningless
*(*(ar+3)+4) // type = int&, same as 'ar[3][4]', meaningless
有的指标値已经不合法了, 所以即使它能印出来观察也是没有意义
的. (对阵列取値会得到元素的左値参考, 所以会多出 & 符号, 和
std::remove_extent 的结果只有差在这里) 同理本篇的程式码也有
对应的工具可以用: std::remove_pointer. 假如我们宣告一个指标
如下:
T* ptr;
ptr 只能用来指向型别为 T 的物件 (位址)
T obj;
ptr = &obj;
给定一个指标型别, 我们就可以知道它应该指向何种物件:
int* pi;
print_type<
std::remove_pointer<decltype(pi)>::type >(); // int
int** ppi;
print_type<
std::remove_pointer<decltype(ppi)>::type >(); // int*
Reference
N4850 http://eel.is/c++draft/
作者: Lipraxde (Lipraxde)   2020-06-16 20:57:00
自从不能用 ycm 后我还在想怎么看一堆 auto 的程式,没想到 decltype 可以拿来用,C++ 总是令我惊奇
作者: shadow0326 (非议)   2020-06-20 23:03:00
推 学到一招了
作者: lc85301 (pomelocandy)   2020-06-24 23:39:00
这招感觉跟 rust 的 let () = var 有点像

Links booklink

Contact Us: admin [ a t ] ucptt.com