一、指针到底是什么?
一般我们所说的指针其实指的是指针变量。那么既然是变量,指针其实说白了跟普通变量没有什么太大的区别。变量是存放数据的手段。
指针变量也是如此,只不过指针变量存放的是地址。
比如:
int a = 10;
int *p = &a; //p是一个int类型的指针
上边这两句代码可以说:p指向了a的空间。本质上就是p中存放的是地址指向了空间a。准确的讲应该是:指针变量p里边存放的地址指向了空间a。
二、变量空间的首字节地址,作为整个空间的地址
实际上,内存中每一个字节空间都有一个地址,如果是内核32根地址总线,地址以二进制表示,其最大可寻址范围就是:
00000000 00000000 00000000 00000000 - 11111111 11111111 11111111 11111111
地址的十六进制表示就是:
0x00000000 - 0xffffffff
0x00000000 为内存中第一个地址
0xffffffff为内存中最后一个地址。
既然每个字节存放的地址都是32位的,那么所有存放地址的指针变量大小也就是32位的,也就是4个字节。如果机器有64根地址线,道理也是如此。参考:一个指针占几个字节?原理是什么呢?
int a 的空间有4个字节,&a代表的是第一个字节的地址。所以首地址字节顺延3个字节的空间,一个4个字节作为整个变量的空间。
三、指针变量类型的作用
对于普通变量来说:int a float b;
a和b都占4个字节的空间大小,但是他们的存储结构是不同的。
对于指针变量来说:一般构成是 普通数据类型 + *(星号)构成。星号的个数,表明了指针变量的级数,指针变量用来存放地址。当不涉及强制类型转换时:有如下关系:
某类型一级指针变量 = 该类型一级地址;
某类型二级指针变量 = &(该类型一级指针变量);
n+1级指针变量 = &(n级指针变量);
所以普通变量的地址都是一级地址,所以一级指针变量的地址都是二级地址……
四、指针使用三部曲:
1.定义(声明)
int *p = NULL; //初始化一下,防止野指针
2.关联
int a = 10;
p = &a;//a空间的首地址给了p,所以p里边的地址常量指向了a空间,因此也说p->a空间。
3.引用 (读空间:读值操作,前提是里边有数据才行)
int b = *p; //等价于 b = a;
写空间:向空间写入新的值
*p = 30; //等价于 a = 30;
五、定义指针后,需要关系的一些内容
例1:
int a = 10;
int *p = &a;
(1)p:表示int*型的一级指针变量空间,里边存放的是a的地址
(2)*p:表示的是p所指向的空间中存的地址所对应的值。指的就是a的空间,只不过是通过地址找到的。
(3)&p:表示的是指针变量p的地址,它需要一个int **的二级指针变量来存放。
例2:
int a = 10;
int *p = &a;
int **p1 = &p;
(1) p1:表示的是:首先是一个二级指针变量,用于存放二级地址,恰好p的地址就是二级地址。
(2)p1:表示的是:引用取空间操作,找到p1所指向的空间,也就是p的空间。
(3)**p1:表示的是:将其中p1替换成p,**p1就变成了*p,指的就是a的空间。
(4)&p1:指的是二级指针变量的地址,是一个三级地址。
六、野指针与段错误问题
所谓的野指针就是:指向一个不确定的地址空间或者虽然指向了一个确定的地址空间,但是引用空间的结果却是不可预知的,这样的指针就是野指针。
段错误:就是地址错误。
ps:#include<forme.h>与#include"forme.h"的区别?
<>表示的是编译器从工程文件指定的路径搜索forme.h文件,而""表示的则是:编译器从当前文件路径和工程指定路径搜索forme.h
七、指针数组与数组指针的区别与分析
指针数组:是数组:数组中的内容都是指针变量;
数组指针:是指针:指向的内容是一个数组。
以下这个表达式如何解读呢?是指针数组还是数组指针呢?
int *p[5];
分析:构成:有int *p 和int p[5];
第一步:找核心:即是p,
第二步:找结合,看谁(与[])离核心最近(以优先级来判断远近);
因为[]的优先级比高,所以p是一个数组,而且是一个int类型的数组,而数组中的元素是指针变量。
假如:int (*p)[5];//就是一个数组指针,int类型的指针p指向一个数组;
八、函数指针的实质(还是指针变量)
函数指针本质上还是指针,本身还是占4个字节(32位的平台上)
函数指针,数组指针,普通指针本质都是指针,只不过所指向的内容不同而已。
函数的实质是一段代码,这一段代码在内存中是连续分布的,对于函数而言,最重要的就是函数的第一句代码,也就是函数名,也就是函数的首地址。
重要的是分析问题的方法,横向联系,多思考,多实践。
函数名与数组名最大的区别就是:函数名做右值时加不加&效果和意义是一样的。但是数组名就不一样,不加&表示的这个数组的首元素首地址,而加了&则表示的是整个数组的首地址。
九、数组与指针
数组可以以指针的方式访问,指针也可以以数组的方式访问。
//将指针以数组的形式访问
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char * a = "China NB Plus";
int length = strlen(a);
int i = 0;
for(i=0; i<length; i++)
{
printf("%c",a[i]);
}
printf("\n");
printf("Hello world!\n");
return 0;
}
//将数组以指针的形式访问
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char a[] = "China NB Plus";
char *p = a;
//这里只打印了8个字符
printf("%c%c%c%c%c%c%c%c",*p,*(p+1),*(p+2),*(p+3),*(p+4),*(p+5),*(p+6),*(p+7));
//要理解*(p+1)--->即*(p+n) 就是指针指向的数组的第一个元素,--->第n个元素。
printf("\n");
printf("Hello world!\n");
return 0;
}
十、指针变量与字符串名的区别
既然指针可以用数组的方式访问,数组也可以用指针的方式访问,都同样指向地址。那么它们由什么区别呢?
看一下下边的程序有什么问题?是否可以获取字符串的个数呢?
答案是不可以的:编译直接报错了: lvalue required as increment operand 报错的意思就是递增运算符的使用需要一个左值。那么说明字符串名不是一个左值,而是一个地址常量。指针变量是可以作为一个左值的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
unsigned char a[] = "China NB Plus!";
unsigned char *p = a;
unsigned char count = 0;
while (*a++ != '\0') //所以while (*a++ != '\0') 改动成while (*p++ != '\0')才可以正常运行实现功能
{
count++;
printf("%c",*p);
}
printf("字符串a的长度是%d",count);
return 0;
}
十一、数组指针与指针数组
int *p[5] 和 int (p)[5] 如何区分呢?
优先级的问题。
[]优先级高于 号,那么int * p[5] 就是一个指针数组,数组中的元素是指针,类型是int ,int ( * p)[5] ,这个 首先p结合,是一个指针,是一个什么样的指针呢?是一个指向数组的指针。
指针数组,是一个数组,
数组指针,是一个指针。
指针数组的初始化
#include <stdio.h>
#include <stdlib.h>
int main()
{
//定义一个指针数组,它本身是一个数组,里边存放的是指针,类型是int*
unsigned char *p[5] =
{
"让编程改编世界",
"just do it",
"一切皆有可能",
"让子弹飞",
"永不止步"
};
int i;
for(i=0; i<5; i++)
{
printf("%s\n",p[i]);//打印字符串,那么我们传字符串的首地址就好。
}
printf("Hello world!\n");
return 0;
}
————————————————————————————————————
数组指针的初始化
#include <stdio.h>
#include <stdlib.h>
int main()
{
//定义一个数组指针----它是指针,指向含有5个int类型元素的数组
unsigned char temp[5] = {1,2,3,4,5};
unsigned char (*p)[5] = &temp; //注意这里
int i;
for(i=0; i<5; i++)
{
printf("%d\n",*(*p+i)); //注意这里:
}
printf("Hello world!\n");
return 0;
}