记住一句话指针就是地址
一、指针的基本类型
int p; // 这 是 一 个 普 通 的 整 型 。
int *p; /*首 先从 P 处 开 始, , 先 与* * 结 合, 所 以 说明 P 是 一
个 指 针,然 后 再 与 int 结 合, 说 明 指 针 所 指 向 的 内 容 的 类 型 为 int 型 所以 以 P 是 一 个 返 回 整 型 数 据 的 指针。*/
int p[3]; /* 首 先从 P 处 开 始, , 先 与 [] 结 合, 说明 P 是 一 个 数 组, 然 后与int 结 合,说 明 数 组 里 的 元 素 是 整 型 的, 所 P 是 一 个 由 整 型 数 据 组 成 的 数*/
int *p[3]; // 首 先从P处 开 始,先 与 [] 结 合, , 因 为 其 优 先 级比* 高, , 所 以 P 是 一 个 数 组, 然 后 再 与* 结 合, 说 明 数 组 里 的 元 素 是 指 针 类 型, 然 后 再 int 结 合, 说 明 指 针 所 指 向 的 内 容 的 类 型 是 整 型 的, 所 以P 是 一 个 由 返 回 整 型 数 据 的 指 针 所 组 成 的 数*/
int (*p)[3]; /* 首 先 从 P 处 开 始, 先 与* 结 合, 说 明 P 是 一 个 指 针 然 后 再与 [] 结 合( ( 与 "()" 这 步 可 以 忽 略, 只 是 为 了 改 变 优 先 级 ), 说 明 指 针 所 指 向 的 内 容 是 一 个 数 组, , 然 后 再与 与 t int 结 合, , 说 明 数 组 里 的 元 素 是 整 型 的. 所以 P 是 一 个 指 向 由 整 型 数 据 组 成 的 数 组 的 指 针*/
int **p; /*首 先 从 P 开 始, , 先 与* * 结 合 , 说 是 P 是 一 个 指 针, 后 再 与* * 结 合, 说 明 指 针 所 指 向 的 元 素 是 指 针, 然 后 再 与 int 结 合, 说 明 该 指 针 所 指 向 的 元 素 是 整 型 数 据. 由 于 二 级 指 针 以 及 更 高 级 的 指 针 极 少 用 在 复 杂 的 类 型 中, , 所 以 后 面 更 复 杂 的 类 型 我 们 就 不 考 虑 多 级 指 针 了, 最 多 只 考 虑 一 级 指 针. */
int p(int); /*从 P 处 起, , 先 与 () 结 合, 说 明 P 是 一 个 函 数, , 然 后 进 入 () 里 分 析, 说 明 该 函 数 有 一 个 整 型 变 量 的 参 数 然 后 再 与 外 面 的 t int 结 合, 说 明 函 数 的 返 回 值 是 一 个 整 型 数。*/
int (*p)(int); /*从 P 处 开 始, 先 与 指 针 结 合, 说 明 P 是 一 个 指 针, , 然 后 与() 结 合, , 说 明 指 针 指 向 的 是 一 个 函 数, 然 后 再 与 () 里 的 int 结 合, 说 明 函 数 有 一个 int 型 的 参 数, 再 与 最 外 层 的 int 结 合, 说 明 函 数 的 返 回 类 型 是 整 型, 所 以 P 是 一 个 指向 有 一 个 整 型 参 数 且 返 回 类 型 为 整 型 的 函 数 的 指 针*/
二、 指 针 的 类 型
从 语 法 的 角 度 看 , 你 只 要 把 指 针 声 明 语 句 里 的 指 针 名 字 去 掉 , 剩 下 的 部 分 就 是 这 个 指 针 的 类 型 。 这 是 指 针 本 身 所 具 有 的 类 型 。 让 我 们 看 看 例 一 中 各 个 指 针 的 类 型 :
(1)int*ptr;// 指 针 的 类 型是 是 int*
(2)char*ptr;// 指 针 的 类 型是 是 char*
(3)int**ptr;// 指 针 的 类 型是 是 int**
(4)int(*ptr)[3];// 指 针 的 类 型是 是 int(*)[3]
(5)int*(*ptr)[4];// 指 针 的 类 型是 是 int*(*)[4]
三、指 针 所 指 向 的 类 型
从 语 法 上 看 , 你 只 须 把 指 针 声 明 语 句 中 的 指 针 名 字 和 名 字 左 边 的 指 针 声
明 符* * 去 掉 , 剩 下 的 就 是 指 针 所 指 向 的 类 型 。 例 如:
(1)int*ptr; // 指 针 所 指 向 的 类 型 是 int
(2)char*ptr; // 指 针 所 指 向 的 的 类 型 是 char
(3)int**ptr; // 指 针 所 指 向 的 的 类 型 是 int*
; (4)int(*ptr)[3]; // 指 针 所 指 向 的 的 类 型 是 int()[3]
(5)int*(*ptr)[4]; // 指 针 所 指 向 的 的 类 型 是 int*()[4]
四、指 针 的 值 ---- 或 者 叫 指 针 所 指 向 的 内 存 区 或 地 址
指 针 的 值 是 指 针 本 身 存 储 的 数 值 , 这 个 值 将 被 编 译 器 当 作 一 个 地 址 , 而 不 是 一 个 一 般 的 数 值 。 在 32 位 程 序 里 , 所 有 类 型 的 指 针 的 值 都 是 一个 32 位 整 数 , 因 为 32 位 程 序 里 内 存 地 址 全 都 是 32 位 长 。
指 针 所 指 向 的 内 存 区 就 是 从 指 针 的 值 所 代 表 的 那 个 内 存 地 址 开 始 , 长 度 为 sizeof( 指 针 所 指 向 的 类 型) ) 的 一 片 内 存 区。
我 们 说 一 个 指 针 指 向 了 某 块 内 存 区 域 ,就 相 当 于 说 该 指 针 的 值 是 这 块 内 存 区 域 的 首 地 址。
指 针 所 指 向 的 内 存 区 和 指 针 所 指 向 的 类 型 是 两 个 完 全 不 同 的 概 念 。 指 针 所 指 向 的 类 型 已 经 有 了 , 但 由 于 指 针 还 未 初 始 化 , 所 以 它 所 指 向 的 内 存 区 是 不 存 在 的 , 或 者 说 是 无 意 义 的,所 以 就 叫 野 指 针 。
五、 指 针 本 身 所 占 据 的 内 存
其实,这个问题很简单,稍微上网一搜,你就知道:
一个指针在32位的计算机上,占4个字节;
一个指针在64位的计算机上,占8个字节。
首先,我们要明白,指针就是地址,地址就是指针。 而地址是内存单元的编号。所以,一个指针占几个字节,等于是一个地址的内存单元编号有多长。
#include <stdio.h>
int main()
{
char ch = 'A';
int i = 99;
double x = 66.6;
char *p = &ch;
int *q = &i;
double *r = &x;
printf("%ld %ld %ld\n", sizeof(p), sizeof(q), sizeof(r));
return 0;
}
~
我自己用的64位的操作系统,所以显示的是8,但是可以看出,无论的字符型,还是整形,还是双精度型指针买他们本身占用的内存是一定的。
6、数组和指针的关系
1、数组名就是数组中第一个元素的地址:
#include <stdio.h>
int main()
{
char array[128] = {0};
printf("请输入你的名字:\n");\
scanf("%s",array);//字符串读取不需要加取地址符号
printf("你的名字的地址是:%p\n",array);
printf("你的名字的第一个元素的地址是:%p\n",&array[0]);
return 0;
}
我们在liunx下运行看结果
事实证明是这样的。
2、通过指针(就是地址)来对数组进行访问
#include <stdio.h>
int main()
{
int a[5] ={1,2,3,4,5}
printf("*a = %d\n*(a+1) = %d\n*(a+2) = %d\n",*a,*(a+1),*(a+2));
return 0;
}
我们通过指针的方式来访问数组,同样也可以达到目的,这里的加一是地址往后移动一个整形数组的长度,而不是简单的数组中的值加一。
3、通过数组的下标法来访问指针
#include <stdio.h>
#include <string.h>
int main()
{
char *array = {"xiaoheihei"}; //定义一个字符串指针
int a,b;
b = strlen(array);//b就是这串字符串的长度
for(a = 0;a<b;a++)
{
printf("%c",array[a]);//通过数组的下标法来访问定义的指针
}
printf("\n");
return 0;
}
同样的,数组通过依次读取打印指针的字符,同样可以输出。
4、取地址符的差距
#include <stdio.h>
int main()
{
int a;
int *p = &a;
printf("请输入一个整数:\n");
scanf("%d",&a);//a是一个整形变量,scanf是用来保存输入变量的地址,所以需>要取地址符
printf("a = %d\n",a);
printf("请再输入一个整数:\n");
scanf("%d",p);//p是一个指针,存放的就是地址,所以p不用取地址符
printf("a = %d",a);
return 0;
}
~
要明白指针的取地址符和(&)和取值运算符的差距(*)。
七、避免产生野指针
NULL 指向一个不被使用的地址,而这个地址一’\0’结束,表示字符串的结尾。
产生野指针的危害:
#include <stdio.h>
int main()
{
int *p1;
int *p2 = NULL;
printf("p1 address is %d\n",*p1);
printf("p2 address is %d\n",*p2);
return 0;
}
这段代码是在哔哩哔哩“小甲鱼学C”上看到的,但是我的linux不能编译提示段错误,他的编译及结果是
p1 address is 32655995; //随机指向内存中的一个地址
p2 address is Unusable address;//会提示你这是一个不能使用的地址。
为什么不能使用p1这种方式的原因是,这个地址是随机的,可以运行的,如果你的程序没有报错,但是执行的结果是错误的,在代码很长的时候,你没办法去找,所以为了避免这个错误。所以不能使用这种野指针。