字符串跟指针的一些疑问

字符串和指针的一些疑问?

/*  char *p="i love";成立,那么是不是可以使用字符串初始化char * 类型指针?
如果是 char * 类型数组该如何初始化?这样可不可以?
char *p[]={"i love","c language"};  */

/*既然上述问题可以,那么我可不可以把{"i love","c language"}看成二维数组?那么
char ar[][20]={"i love","c language"};
char (*ar_p)[20]=ar;
是不是可以成立?*/

/*ar_p是指向20个char元素的指针变量,p是元素为char指针变量的数组*/
#include<stdio.h>
int main(void)
{
        char *p[]={"i love","c language"};
        puts(p[0]);
        puts(p[1]);
        printf("%s\n",p[1]);
        char ar[2][20]={"i love","c language"};
        char (*ar_p)[20]=ar;
        puts(ar_p[0]);
        puts(ar_p[1]);
        printf("%s\n",ar_p[1]);
//      char *p1="i love";
//      char (*p2)[]="c language";
        return 0;
}


这两种方法都可以,p与ar_p是不是等价的?如果不等价,我也觉得不能等价,不然我三观都毁了,为什么两种打印结果完全相同?
p的类型是不是 char **?
ar_p的类型为char (*)[20],如果等价,那么char (*)[]是不是与char **等价?

还有,指针好难,请推荐一本准确详细的介绍指针的书。我看的是《c primer plus(第五版)》,这本书就不要推荐了字符串跟指针的一些疑问指针讲的太差了
------解决方案--------------------
p的类型是不是 char **?
------------------------------------
不是。这个是指针数组。
char **是指针的指针。(两个,可以通用是因为,数组名,可以表示,数组的起始地址)
数组的地址是固定的。“容量”也是固定的。
指针指向的地址是可变的,“容量”也是可变的。
char *p[]={"i love","c language"};这个语句,说明,这个指针数组,初始化成含有两个指针的指针数组。

ar_p的类型为char (*)[20],如果等价,那么char (*)[]是不是与char **等价?
-----------------------------------------
上面已经说明。

做一个类比吧。可能不太恰当。
把内存当成一本书。
数组,相当于,书本的目录。表示某个内容在书本某一页-某一页,书本出版出来,这个是不能变了,虽然我们可以在指向的那一页做做批注笔记什么的(修改)。但我们不能改变目录的内容了。
指针,相当于书签,我们读到了哪里,就把书签插到那里(指针指向的地址改变),便于下次接着读。
书签非常多,我们想规范一下书签,于是又有了不同的整理方法,书签的书(指针数组),和书签的书签(指针的指针)。

------解决方案--------------------
1-p与ar_p是不是等价的?
答:不等价;p的类型char *数组;ar_p数据类型是char(*)[]数组指针!一个数组(多个元素),一个指针变量(单变量),当然不等

2-为什么两种打印结果完全相同?
p两个成员,每个成员指向一个字面字符串;二维ar,两行,每行20个字符,每行存储一个字符串。赋值给ar_p后,即*ap_p = ar; ar_p[0],ar_p[1]存储的两个行指针!(注意,我省略了第二维)输出的时候输出一行!因此,和输出p一致

3-那么char (*)[]是不是与char **等价?
性质上不等价,操作上可等价。参考《c和指针》的数组与指针的相关内容。

4-请推荐一本准确详细的介绍指针的书:《c & pointer》
------解决方案--------------------
char (*)[20]与char **是不等价的,前者是指针数组,后者是二级指针。指针数组的意思就是指针指向了整个数组,这点体现在自加自减运算上最为明显,二级指针的意思是这个指针指向了另一个一级指针的地址。
你可以用代码试一下。
char a[2][20];
char (*p)[20];
p = a;
char **q ;
char *q1 = "a";
q = &q1;
printf("%p\t%p\n",p,q);
p++;
q++;
printf("%p\t%p\n",p,q);
看看结果就知道指针指向整个数组的意思了。
至于书的话就是c和指针,c陷阱与缺陷。