C语言数组和指针总结(上)
1、相同点:
a+n <=> (unsigned int)a + n*sizoeof(*a) 如果a是一个数组那么a+n是指向第n个元素地址
p+n <=> (unsigned int)p + n*sizoeof(*p) 如果p指向的是一个数组那么p+n是指向第n个元素地址
p[n] <=> *(p+n) <=>* (n+p) <=> n[p] ==== a[n] <=>*(a+n) <=>*(n+a) <=> n[a]
2、不同点:
数组空间大小sizeof(a) = sizeof(type)*n字节
指针空间大小sizeof(p) = 4字节
数组名可以看做常量指针,所以数组不能执行a++或者a—运算符,但是指针是一个变量,所以可以执行p++或者p—
3、在对&运算符含义或者操作时
&a = 数组地址(注意:数组地址和数组首元素地址他们值是一样的,但是含义不一样)
&p = 取指针变量p的地址
&a+n = unsigned int(&a) + n*sizeof(*&a)
unsigned int(&a) +n*sizeof(a) 所以是指向整个数组后面元素
&p+n = unsigned int(&p) + n*sizeof(&p) <=> unsigned int(&p) + n*4 就是指向下个4字节指针
4、指针与指针之间四则运算(+,-,*,/)
a)指针之间只支持减法运算
b) 参与减法运算的指针类型必须相同
当两个指针指向同一个数组时,p1– p2 = (unsigned int(p1) – unsigned int(p2))/sizeof(type) 含义就是指针所指元素的下标差
当两个指针所指不同数组进行相减运算时能够编译通过,但是结果无任何意思,只能说明内存分配谁在前,谁在后
5、指针与指针之间关系运算(>,>=,<,<=,=!,==)
a)指针关系运算的前提是同时指向同一个数组中的元素
b)任意两个指针之间的比较运算(=!,==)无限制
c)参与比较运算的指针类型必须相同
其实以上三条我都不认同,经过测试不同类型指针可以进行关系比较,因为都是指针都是保存内存地址,都是四个字节,当然可以比较,但是比较没有任何意思,只能说明在内存前后顺序,无任何意思
6、数组名作为函数参数时退化成指针目的
C语言以高效作为最初设计目标:
a) 参数传递的时候如果拷贝整个数组执行效率将大大下降
b) 参数位于栈上,太大的数组拷贝将导致栈溢出
7、const修饰指针
const int* p1
int const* p2;
int* const p3;
const int*const p4;
规律:左数右指
含义:const在*左边,指针指向内容不能变,也就是*p = 3编译错误,但是指针可以变p = &j
const在*右边,指针不变,也就是p= &j编译错误,但是指针指向内容可以变,也就是* p= 4
const int* const p4这个即在*左边也在*右边,所以都不能改变,p= &j和*p = 5;都编译错误
这里结论你们可以自己写些代码测试下,加深印象
8、下面举一些代码案例
(1)数组和指针区别:
#include <stdio.h>
int main()
{
extern int a[];
printf("&a = %p\n", &a); //0x804a014 这个是数组地址
printf("a = %p\n", a); //0x804a014 这个是数组首元素的地址
printf("*a = %d\n", *a); //1
return 0;
}
ext.c
int a[] = {1,2,3,4,5}
下面我们在改下主函数:
#include <stdio.h>
int main()
{
extern int* a;
printf("&a = %p\n", &a); //0x804a014 这个是指针地址
printf("a = %p\n", a); //1
printf("*a = %d\n", *a); //段错误,因为指针保存的1,所以按照这个地址去取值,但是这个值在内存中不允许操作,所以段错误
return 0;
}
ext.c
int a[] = {1,2,3,4,5}
上面就是指针和数组的区别了,如果没有区别,理论应该都编译通过,结果也应该是一样的,但是编译不通过,输出结果也不一样
(2)多维指针
#include <stdio.h>
int main()
{
int value = 5;
int* p1 = &value;
int** p2 = &p1; //试着把这个改成int** p2 = &value然后输出**p2会有什么后果?思考下
int*** p3 = &p2;
int**** p4 = &p3;
printf("&value = %p\n&p1 = %p\n&p2 = %p\n&p3 = %p\n&p4 = %p\n\n",&value,&p1,&p2,&p3,&p4);
printf("&value = %p\np1 = %p\n",&value,p1);
printf("&p1 = %p\np2 = %p\n",&p1,p2);
printf("&p2 = %p\np3 = %p\n",&p2,p3);
printf("&p3 = %p\np4 = %p\n\n",&p3,p4);
printf("*p4 = %p\np3 = %p\n&p2 = %p\n",*p4,p3,&p2);
printf("**p4 = %p\np2 = %p\n&p1 = %p\n",**p4,p2,&p1);
printf("***p4 = %p\np1 = %p\n&value = %p\n",***p4,p1,&value);
printf("****p4 = %d\n\n",****p4);
printf("*p3 = %p\np2 = %p\n&p1 = %p\n",*p3,p2,&p1);
printf("**p3 = %p\np1 = %p\n&value = %p\n",**p3,p1,&value);
printf("***p3 = %d\n\n",***p3);
printf("*p2 = %p\np1 = %p\n&value = %p\n",*p2,p1,&value);
printf("**p2 = %d\n\n",**p2);
printf("*p1 = %d\n",*p1);
return 0;
}
这里不讲了,自己思考下,如果实在不懂,就说明你还没有完全理解指针含义
(3)指针递增和递减
思考下下面的一些输出,哪些跟哪些相同,哪些跟哪些不同,有什么区别,如果改成数组是否有区别,如果你能全部回答正确,那说明你对指针和数组已经很了解了
*p++ 和 *(p++)
(*p)++
*++p 和 *(++p)
++*p
*p+1
*(p+1)
p+1和p++区别
p++和a++区别
p+1和&p+1有啥区别 如果是数组a+1和&a+1有区别么?
#include <stdio.h>
int main()
{
int p1[5] = {1,2,3,4,5};
int* p3 = p1;
int* p4 = p1;
int* p5 = p1;
int a,b;
printf("p3 = %p\np1 = %p\n",p3,p1); //p3 = p1 = 0xbfe4dbcc
printf("&p3 = %p\n&p4 = %p\n&p1 = %p\n",&p3,&p4,&p1);//&p3 = 0xbfe4dbfc &p4 = 0xbfe4dbf8 &p1 = 0xbfe4dbcc
printf("&p1+1 = %p\n",&p1 + 1);
//结论:
// &p3跟&p1输出是不一样的,这个就是指针和数组明显区别,&p3是取p3指针变量的地址,而&p1是数组地址(注意:p1数组名代表数组首元素地址,和&p1代表数组地址,含义不一样,虽然他们输出数值是一样的)
a = *p3++;
b = *(p4++);
printf("a = %d\nb = %d\n",a,b); //a=1 b=1
printf("p3 = %p\np4 = %p\n\n",p3,p4);//p3 = p4 = 0xbfe4dbd0
//结论:
// 因为++运算符优先级高于*,所以*p3++相当于*(p3++),跟*(p4++)是相同的,先算p3++, 等于p3,然后*p3,然后指针递增1
p3 = p1;
a = (*p3)++;
printf("a = %d\np3 = %p\n",a,p3);//a=1 p3 = 0xbfe4dbcc
printf("p1[0] = %d\n\n",p1[0]); //p1[0] = 2
//结论:
// 由于()优先级高于++,所以先算(*p3)等于p1[0],也就是a=p1[0]++;所以a=1,p[0]再自增加1,p[0]=2
p1[0] = 1; //因为上面把这个值改了,为了不影响下面测试结果,我们再给它改成初值
p3 = p1;
p4 = p1;
a = *++p3;
b = *(++p4);
printf("a = %d\nb = %d\n",a,b); //a = b = 2
printf("p3 = %p\np4 = %p\n\n",p3,p4);//p3 = p4 = 0xbfe4dbd0
//结论:
// 由于++优先级高于*,所以*++p3相当于*(++p3) = *(++p4),所以先算++p3,这样指针指向数组p1[1]地址,然后再执行取值,a = b = p1[1] = 2
p3 = p1;
a = ++*p3;
printf("a = %d\np3 = %p\n",a,p3); //a = 2 p3 = 0xbfe4dbcc
printf("p1[0] = %d\n\n",p1[0]); //p1[0] = 2
//结论:
// 因为*和++都是单目运算符,所以运算顺序是从右向左,++*p3相当于++(*p3),所以先算(*p3)= p1[0],然后自增,也就是++p[0];这样p[0] = 2
p1[0] = 1; //因为上面把这个值改了,为了不影响下面测试结果,我们再给它改成初值
p3 = p1;
a = *p3+1;
printf("a = %d\np3 = %p\n\n",a,p3); //a = 2 p3 = 0xbfe4dbcc
printf("p1[0] = %d\n\n",p1[0]); //p1[0] = 1
//结论:
// 因为*p3 = p1[0]所以a = p[0] + 1 = 2
p3 = p1;
a = *(p3+1);
printf("a = %d\np3 = %p\n\n",a,p3); //a = 1 p3 = 0xbfe4dbd0
//结论:
// 先计算()内的,就是p3,然后a = *p3,再然后p3+1
//p3 = p1++; //编译出错 为什么数组p1++编译出错,而指针p3++却可以? 因为数组名代表数组首元素地址,数组名可以看作常量指针,常量指针是不能改变的,所以编译出错,而指针是一个变量,所以可以改变
p3 = p1;
p4 = p1; //注意:这个是考察p3++和p3+1是否有区别,但是由于C语言是顺序执行的,所以就改变了下
p3++;
p5 = p4+1;
printf("p3 = %p\np4 = %p\np5 = %p\n\n",p3,p4,p5);//p3 = 0xbfe4dbd0 p4 = 0xbfe4dbcc p5 = 0xbfe4dbd0
//结论:
// 通过测试我们发现p3和p5是一样的,所以就是p3++和p3+1结果是一样的,但是p3++把自己改变了,指向了下一个元素地址,但是p3+1没有把自己改变,这个就是区别
p3 = p1; //这个是测试p3 +1 和&p3 + 1是否有区别,当时数组时p1 + 1和&p1 + 1时是否有区别
printf("p1 = %p\n&p3 = %p\n",p1,&p3); //p1 = 0xbfe4dbcc &p3 = 0xbfe4dbf8
printf("p3 + 1 = %p\n&p3 + 1 = %p\n",p3 + 1,&p3 + 1);//p3+1=0xbfe4dbd0 &p3+1=0xbfe4dbfc
printf("p1 + 1 = %p\n&p1 + 1 = %p\n",p1 + 1,&p1 + 1);//p1+1=0xbfe4dbd0 &p1+1=0xbfe4dbe0
//结论:
// p3 + 1和&p3 + 1输出数值是有区别的,p3+1就是移动4个字节,下个元素地址,但是&p3+1是先取p3指针地址,再移动4个字节,这个就是区别,但是当你把p3改成char* p3时,p3+1 = 0xbfe4dbcd,而&p3+1 = 0xbfe4dbfc,没有变化,这个也是区别了,结论就是p3+1是和p3指向的数据类型有关系,而&p3+1跟p3类型没有关系,都是移动4个字节,这是因为&p3取的是指针类型(注意不是int*这个类型),而指针保存就是4个字节,这个好像有点绕口,自己好好理解下
// p1 + 1和&p1 + 1输出有很大区别 p1 + 1就是移动4个字节,下个元素地址,而&p1 + 1移动了20字节就是数组最后一个元素地址的下一个地址,所以p1代表的是数组首元素地址,&p1代表数组地址,是完全不一样的概念
return 0;
}
p3+1和&p3+1的区别:
p3+1:取p3内容(实际是p1数组首元素地址),移动sizeof(type)个字节,下个元素位置
&p3+1:取p3指针变量地址(不是p1地址,这个要区别),移动4个字节,跟p3类型没有关系
a+1和&a+1的区别:
a+1:取数组首元素地址,移动sizeof(*a)或者sizeof(type)个字节,下个元素位置,所以p3+1=a+1
&a+1:取a数组地址(这个和数组首元素地址不一样,概念不一样,虽然值相同),移动sizeof(a)个字节,数组最后元素下个地址
其他注释已经解释的很清楚了,所以不需要讲了
主要参考狄泰软件《C语言进阶教程》