指针是什么?
指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑中的另一个地方的值,由于通过地址能找到所需的变量单元。可以说地址指向该变量单元。因此,将地址形象化的称为“指针”。
为什么存在指针?
举个例子:我们的土地管理方式是,国家分为n个省,每个省又划分为m个市,每个市又划分为k个县。。。这样做可以很好的管理,通过一个地址就找到对应的位置。
同样,计算机的内存可以分成许多小的单元,每个单元就对应一个独一无二的地址,一个地址标识一块空间。
指针的大小?
对于32位的机器,在这里就有2的32次方个地址。
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111
一个地址是由32个0或者1组成的二进制序列,8个位需要一个字节,所以一个地址需要4个字节来存储,即一个指针变量的大小应该是4个字节。
同理,在64位机器上,一个指针变量的大小应该是8个字节。
指针的类型?
变量有不同的类型,指针也同样。
char *pc = NULL;//char *类型的指针是为了存放char类型变量的地址。
int *pi = NULL;//int *类型的指针是为了存放int类型变量的地址。
short*ps = NULL;//short*类型的指针是为了存放short类型变量的地址。
指针的类型决定了对指针解引用的时候有多大的权限。
指针的算数运算
char a[20];
int *ptr=(int *)a; //强制类型转换并不会改变a 的类型
ptr++;
指针ptr 的类型是int*,它指向的类型是int,它被初始化为指向整型变量a。最后一句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4,因为在32 位程序中,int 占4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中从第4 号单元开始的四个字节。
&和*?
&是取地址运算符,&a的运算结果是一个指针,指针的类型是a的类型加上加上*,指针所指向的类型是a 的类型,指针所指向的地址嘛,那就是a 的地址。
*是间接运算符,*p的结果是p所指向的东西,它的类型是p指向的类型,它所占用的地址是p所指向的地址。
int a=12; int b; int *p; int **ptr;
p=&a; //&a 的结果是一个指针,类型是int*,指向的类型是
//int,指向的地址是a 的地址。
*p=24; //*p 的结果,在这里它的类型是int,它所占用的地址是
//p 所指向的地址,显然,*p 就是变量a。
ptr=&p; //&p 的结果是个指针,该指针的类型是p 的类型加个*,
//在这里是int **。该指针所指向的类型是p 的类型,这
//里是int*。该指针所指向的地址就是指针p 自己的地址。
*ptr=&b; //*ptr 是个指针,&b 的结果也是个指针,且这两个指针
//的类型和所指向的类型是一样的,所以用&b 来给*ptr 赋
//值就是毫无问题的了。
**ptr=34; //*ptr 的结果是ptr 所指向的东西,在这里是一个指针,
//对这个指针再做一次*运算,结果是一个int 类型的变量。
NULL指针
它作为一个特殊的指针变量,表示不指向任何东西,要使一个指针变量为NULL,你可以给它赋一个零值。NULL指针概念非常有用,它提供了一种方法表示某个特定的指针目前并未指向任何东西。对指针进行解引用可以获得它指向的值,NULL指针并未指向任何东西,因此对一个NULL指针进行解引用操作是非法的。
指针和数组
联系:(1)当一个指针变量被初始化成数组名时,就说该指针变量指向了数组(数组名就是该数组的首元素地址)
(2)数组的下标访问操作可以通过指针实现:a[i]转换为*(a+i)
int arr[10]={0,1,2,3,4,5,6,7,8,9},data;
data=arr[0]; //也可写成:data=*array;
data=arr[3]; //也可写成:data=*(array+3);
data=arr[4]; //也可写成:data=*(array+4);
区别:(1)数组名在传参时会退化为指针,指针不会。
(2)内存中数组是一块连续开辟的空间,指针只占有一个指针类型的大小空间。
(3)数组可以通过下标直接访问,指针需要进行计算间接访问。
(4)数组名具有常性,不能进行++操作,指针可以。
复习指针和数组相关运算
一维数组
int a[] = {1, 2, 3, 4};
printf("%d\n", sizeof(a)); //16 表示整个数组大小
printf("%d\n", sizeof(a+0)); //4 数组首元素
printf("%d\n", sizeof(*a)); //4 数组首元素
printf("%d\n", sizeof(a+1)); //4 数组第二个元素的地址
printf("%d\n", sizeof(a[1])); //4 第二个元素
printf("%d\n", sizeof(&a)); //4 数组的地址
printf("%d\n", sizeof(&a+1)); //4 数组后的地址
printf("%d\n", sizeof(&a[0])); //4 第一个元素的地址
printf("%d\n", sizeof(&a[0]+1)); //4 第二个元素的地址
字符数组
char arr[] ={'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr)); //6 首元素地址
printf("%d\n", sizeof(*arr)); //1 首元素
printf("%d\n", sizeof(arr[1])); //1 第二个元素
printf("%d\n", sizeof(&arr)); //4 数组地址
printf("%d\n", sizeof(&arr+1)); //4 数组后的地址
printf("%d\n", sizeof(&arr[0]+1)); //4 第二个元素地址
printf("%d\n", strlen(arr)); //随机值 遇到\0才停下来
printf("%d\n", strlen(arr+0)); //随机值
printf("%d\n", strlen(*arr)); //err a的ASCII码97 不允许访问
printf("%d\n", strlen(arr[1])); //err b
printf("%d\n", strlen(&arr)); //随机值
printf("%d\n", strlen(&arr+1)); //随机值
printf("%d\n", strlen(&arr[0]+1)); //随机值
*char *p = "abcdef";
printf("%d\n", sizeof(p)); //4 字符a的地址
printf("%d\n", sizeof(p+1)); //4 字符b的地址
printf("%d\n", sizeof(*p)); //1 p是字符指针,对p解引用可以访问一个字节
printf("%d\n", sizeof(p[0])); //1 p[0]==*(p+0) 第一个元素
printf("%d\n", sizeof(&p)); //4
printf("%d\n", sizeof(&p+1)); //4
printf("%d\n", sizeof(&p[0]+1)); //4 b的地址
printf("%d\n", strlen(p)); //6
printf("%d\n", strlen(p+1)); //5 p+1为b的地址
printf("%d\n", strlen(*p)); //err
printf("%d\n", strlen(p[0])); //err
printf("%d\n", strlen(&p)); //随机值
printf("%d\n", strlen(&p+1)); //随机值
二维数组
int a[3][4] = {0};
printf("%d\n", sizeof(a)); //48 整个数组大小
printf("%d\n", sizeof(a[0][0])); //4 第一个元素大小
printf("%d\n", sizeof(a[0])); //16 第一行的大小
printf("%d\n", sizeof(a[0]+1)); //4 第一行第二个元素
printf("%d\n", sizeof(a+1)); //4 第二行地址
printf("%d\n", sizeof(&a[0]+1)); //4 第二行地址 a[0]为第一行数组名
printf("%d\n", sizeof(*a)); //16 第一行的地址解引用 假想为一唯数组 第一行的大小
printf("%d\n", sizeof(a[3])); //16 sizeof内部的表达式不参与运算 第四行数组名虽不存在但不影响计算 因为不会放入
一级指针
#include <stdio.h>
int main()
{
int a = 10;
int *p = &a;
printf("%p\n",p);
return 0;
}
此处p为一个一级指针,p中存储的是变量a的地址
由此可见,指针就是变量, 用来存放地址的变量。
二级指针
int main()
{
int a=10;
int *pa=&a;
int **ppa=&pa;
printf("%p\n",a);
printf("%p\n",pa);
return 0;
}
指针类型
通过指针引用一个变量,必须知道该数据的类型,才能按存储单元的长度以及数据的存储形式正确的取出该数据。即在定义指针变量时必须指定基类型。
指针表达式解析
一般左值代表空间,右值代表内容,左值不一定能够放在等号左边
&ch; //不能放在等号左边,能放在等号右边
cp; //能放在等号左边,能放在等号右边
&cp; //不能放在等号左边,能放在等号右边(表示cp的地址)
*cp+1; //不能放在等号左边(常量不能被赋值),放在等号右边相当于字符b
*(cp+1) ;//表示a后面的空间,能放在等号左边
++cp; //在c语言中,一个变量进行前置加加或者后置加加都不能放在等号左边
cp++;
*++cp; //和表达式*(cp+1)效果一样
*cp++; //能放到等号左边,能放到等号右边
++*cp; //能放到等号右边,相当于字符b
(*cp) ++;//能放到等号右边,相当于字符a,但此时ch指向b
++*++cp; //表示ch下一块地址的东西自增
指针数组
指针数组是数组, 是一个存放指针的数组。
eg:
int *arr1[10];
char *arr2[4];
char **arr3[5];
数组指针
eg:int (*p)[10];
p先和*结合, 说明p是一个指针变量, 然后指着指向的是一个大小为10个整型的数组。 所以p是一个指针, 指向一个数组, 叫数组指针。
函数指针
void(*P)(int char)=test1;
void(*P)(int char)=test1;//函数指针
void(*parr[3])(int char)=(test1,test2,test3);//函数指针的数组
void(*(*ptr[3])(int char)=&arr;//指向函数指针的指针
#include<stdio.h>
int ADD(int a,int b)
{
return(a+b);
}
int main()
{
int (*p)(int,int)=ADD;//定义一个返回值类型为整形,有两个参数,且参数为整形的函数指针*p
printf("%d\n",p(1,3));//通过函数指针来访问其所指向的函数Add
return 0;
}