C语言学习第四阶段-指针

记住一句话指针就是地址

一、指针的基本类型

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这种方式的原因是,这个地址是随机的,可以运行的,如果你的程序没有报错,但是执行的结果是错误的,在代码很长的时候,你没办法去找,所以为了避免这个错误。所以不能使用这种野指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

永不秃头的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值