1、数组中:arr[1] 和*(arr+1)作用是一样的都是取下标1的值。
2、数组名只有在sizeof和&取数组地址的时候不是指针,否则其他情况下数组名就是指向第一个元素的首地址。
3、数组名不能++运算,因为他是一个常量,但是可以赋值各一个指针变量然后++运算。
4、创建数组声明数组后就会分配空间,但是声明普通指针变量,不初始化的话,指向的是一个随意地址,随意访问会出问题的。
5、sizeof数组名的时候返回的是数组长度,但是如何数组名赋值给一个指针变量,sizeof得到的是第一个元素的长度,同样,形参用指针接收数组,sizeof也得不到数组长度,所以c语言中一般都将数组长度计算好然后传给其他地方。这里需要注意,即使在函数形参中用的不是指针用的也是数组形式参数接收实参,这个时候也得不到数组长度,效果和指针形参是一致的。
6、一维数组总结:加入一维数组 int a[10];
假如首元素首地址是:000000
今有如下打印指针的结论:(一个元素4个字节)
a :000000
a+1 : 000004
&a : 000000
&a+1 : 000040
假如:int *p = a;
p : 000000
p+1 : 000004
假如:int *q = &a;
q : 000000
q+1 : 000004
假如:int (*m)[] = &a;
m : 000000
m+1 : 错误写法
假如:int (*m)[10] = &a;
m : 000000
m+1 : 000040
假如:int(*n)[] = a;
n : 000000
n+1 : 报错
假如:int (*n)[10] = a;
n : 000000
n+1 : 000040
6.0:如果是二维数组,那么以上的假1就是针对数组内部的一个数组而言的,比如int a[10][2]
a+1不再是加4而是加 里面一维数组元素第一个二维数组的长度 2 * 4 = 8,其他的类似的。
sizeof只能通过数组名能得到数组的长度,sizeof其他的指针都无法获取到数组的长度【这里需要注意】,因为其他的指针都是指向数组的首地址的。
6.1指针个人总结:任何地址赋值给一个指针变量,这个指针都是指向这个地址的首地址,至于指针的步长是多少,关系到指针怎么取值,步长由指针的声明决定,比如int *就是4,要想让数组的指针步长产生作用也就是指针+1能合法,需要声明数组指针的时候也要指定数组的长度,int (*p)[]这样只能决定指针首地址, int(*p)[10]才能让指针的步长起作用,也就是+1运算能起作用的。
数组名称就是指向第一个元素的首地址,取数组地址可以赋值给 指定长度的数组指针,他们的+1效果一样的也就是 int(*p)[长度]
数组和指针的上述性质决定着传数组给函数的时候必须要单独的传长度,因为形参根本无法知道数组的长度,所以也就无法用数组指针的形参来接收。
6.2数组指针除了上述int (*p)[长度]复杂的声明定义方式外,还有如下定义方式:
先用typedef定义一个数组类型,typedef int 数组名[长度],然后在程序中使用数组名来定义数组或者定义数组的指针就行了。
6.3数组指针的定义方式:
6.3.1: int (*p)[10] = &数组名
6.3.2: typedef int p[10]; p *arr = &数组名;
6.3.3: typedef int (*p)[10]; p arr =&数组名;
7、指针字符串:编译器也会给字符串常量后面的地址加上一个'\0'字符标志字符串结束。 字符串常量中有'\0'并且不会和后面的字符组成转义字符,默认就到这里结束字符串。
地址香瓜:
假如:char *str = "12345";
str指向"12345"字符串常量的首地址也就是'1'的地址
char *的步长是1,打印效果如下:
假如:char *str = "12345";假如str的首地址是000000
%s,str : "12345";
%s,str+1 : "2345";
%c,*str : '1';
%c,*(str+1) : '2'
%d,str :000000
%d,str+1 :000001
%d,*str+1 :49 【字符'1'的ASCII码数字加1】
字符加法运算,先转成对应的ASCII码,计算得到ASCII码,然后看打印什么,打印字符就是转为ASCII码对应的字符,打印数字就是对应的ASCII码数字。
【注意:】假如:char *str = "12\0nn";就是 char *str = "12"一样的效果。
8、指针数组:数组元素是指针的数组,这里以字符串数组举例,字符串本身也是一个指针,
所以就是指针的数组。
【注意】:使用字符串,直接用指针就行,不用去用*解指针的内容,但是要用到字符串中的字符就需要用到*去解指针内容了。
例如:字符串数组就用指针数组来声明,char *a[]= {"12345","67","890"};
字符串数组中的的内容全部是三个字符串的首地址,a的元素是三个指针,每个指针指向一个字符串常量,而指针变量的长度是4字节,和int的一样都是4个字节。
有如下打印效果:
比如数组的首地址是000000
%d,a : 000000
%d,a+1 : 000004
%d,a+2 : 000008
%s,*a : "12345" (取到的是元素内容,也就是第一个字符串的指针)
%s,*(a+1) : "67"
%s,*(a+2) : "890"
%d,*a : xxxxxx (取到的是数组第一个元素,也就是第一个字符串指针首地址)
以下取到的是字符串指针指向的字符内容:需要用*解指针
%c,**a :'1'
%c,*(*a+1) : '2'
%c,*(*(a+1)+1) : '7'
8.1由于数组做函数参数,要退化为指针,int a[] 在函数形参用 *来接收参数,所以指针数组一样的要用二级指针来接收参数:**
在栈区,直接用 char *a[]的方式定义指针数组,但是在堆区如何申请指针数组空间呢:如下方式:
char **a = (char **)malloc(sizeof(char *) * n); sizeof(char *) == 4的
9、二维数组初始化的时候最好两个数组都写上,至少要后一个数字要写上,而且数字要和初始化的时候的数据个数对上。
int arr[][3] = {{1,2,3},{4,5,6}}
上面和int arr[][3] = {1,2,3,4,5,6} 是等价的,因为二维数组也是线性存储的数据,这种方式,如果数据不够会以0填充最后不够的数据的 {1,2,3,4,5}其实就是{1,2,3,4,5,0}
10、二维数组的三种形参参数形式:
10.1 int arr[2][3] 定死的传统写法
10.2 int arr[][3] 省略一维的数字
10.3 int (*arr)[3] 用数组的指针的方式,使用的时候可以直接用arr[][]的方式取元素
以上三种方式,在函数中都可以用原始的[][]方式取数组的值。当然可以把数组名当做指针然后用指针的方式取值:以下取值方式是等价的
arr[0][0] ====== (*arr)[0] ===== *arr[0] ===== **arr ===*(*(arr+0)+0)
arr[0][1] ========= (*arr)[1] =====(*(arr+0))[1]======== *(arr[0]+1)
arr[1][0] ======= *arr[1] ======*(arr+1)[0]=====**(arr+1)==*(*(arr+1)+0)
arr[1][1] ========= *(arr[1]+1) ==== *(*(a+1)+1) ==== ((a+1))[1]
这里注意1:变量左边*,右边[],右边[]的优先级高于左边的*
这里注意2:数组[下标] 可以写成 : *(数组+下标的形式),当然以上方式也可以混合使用取元素。
一维数组名这个指针的步长是数组元素的长度,同样二维数组名的指针的步长是子数组每个数组元素的长度
才有上面的取值方式。谨记
因为二维数组的数组名指向第一个子数组。