C语言 指针

目录

1.指针是什么?

2. 指针和指针类型

2.1指针的解引用

2.2指针加减整数

3.野指针

3.1 指针未初始化

3.2指针越界访问

4.指针运算

4.1指针加减整数

4.2指针减指针

4.3指针的关系运算

5.指针和数组

例1

 例2

6.二级指针

7.指针数组

8.结语


1.指针是什么?

把内存单一的编号就称为地址(地址也就叫指针)
指针其实就是地址,地址就是编号
指针就是内存单元的编号

指针变量:可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量

int main()
{
    int a = 10;//a是整型变量,占用4个字节的内存空间
    int* pa = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。

    //a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量 中,p就是一个之指针变量。
    //pa是一个指针变量,用来存放的地址的
    // 指针变量里边存放的是地址,而通过这个地址,就可以找到一个内存单元
    //指针是内存中一个最小单元的编号
    //本质上指针就是地址,口语中说的指针,其实是指针变量,指针变量就是一个变量,指针变量是用来存放地址的一个变量

    return 0;
}

总结:指针是用来存放地址的,地址是唯一标示一块地址空间的。

指针的大小在32位平台是4个字节,在64位平台是8个字节。

2. 指针和指针类型

指针的定义方式是: type + *

int main()
{
    char* pc = NULL;//char* 类型的指针是为了存放char类型变量的地址
    short* ps = NULL;//short* 类型的指针是为了存放short类型变量的地址
    int* pi = NULL;
    double* pd = NULL;
    printf("%zu\n", sizeof(pc));//sizeof 返回的值的类型是无符号整型 unsigned int
    printf("%zu\n", sizeof(ps));
    printf("%zu\n", sizeof(pi));
    printf("%zu\n", sizeof(pd));
    return 0;
}

指针类型的意义

2.1指针的解引用

int main()
{
    int a = 0x11223344;
    //int* pa = &a;
    //*pa = 0;
    char* pc = (char*)&a;//int*
    *pc = 0;
    //结论1:
    //指针类型决定了指针在被解引用的时候访问了几个字节
    //如果是int*的指针,解引用访问4个字节
    //如果是char*的指针,解引用访问1个字节
    //推广到其他类型

    return 0;
}

2.2指针加减整数

int main()
{
    int a = 0;
    int* pa = &a;
    char* pc =(char*) &a;
    printf("pa = %p\n", pa);
    printf("pa = %p\n", pa+1);
    printf("pc = %p\n", pc);
    printf("pc = %p\n", pc + 1);
    //结论2:
    //指针的类型决定了指针+-1操作的时候,跳过几个字节
    //决定了指针的步长

    return 0;
}

3.野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

3.1 指针未初始化

int main()
{
    int* p;
    //p没有初始化,就意味着没有明确的指向
    //一个局部变量不初始化,放的是随机值:0xcccccccc

    *p = 10;//非法访问内存了,这里的p就是野指针
    return 0;
}

3.2指针越界访问

int main()
{
    int arr[10] = { 0 };
    int* p = arr;//&arr[0]
    int i = 0;
    for (i = 0; i <= 10; i++)
    {
        *p = i;//当指针指向的范围超出数组arr的范围时,p就是野指针
        p++;
    }
    return 0;
}

如何规避野指针

1. 指针初始化

2. 小心指针越界

3. 指针指向空间释放即使置NULL

4. 避免返回局部变量的地址

5. 指针使用之前检查有效性

int main()
{
    int b = 0;
    int a = 10;
    int* p = &a;
    int* p2 = NULL;//不知道赋什么值时,就赋个NULL,表示NULL -> 0,也就和整型变量初始化赋零是一样的
    *p2 = 100;//error,0地址是无法访问的
    //如果真的想用int* p2=NULL,可以按以下方法做
    int* p3 = NULL;
    if (p3 != NULL)
    {
        *p3 = 100;
    }
    return 0;
}

4.指针运算

4.1指针加减整数

int main()
{
    int arr[10] = { 0 };
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    //数组下标的写法
    for (i = 0; i < sz; i++)
    {
        arr[i] = 1;
    }

    int* p = arr;
    for (i = 0; i < sz; i++)
    {
        *p = 1;
        p++;
    }

    int* p = arr;
    for (i = 0; i < sz; i++)
    {
        *(p + i) = 1;
    }
    return 0;
}

4.2指针减指针

int main()
{
    int arr[10] = { 0 };
    printf("%d\n", &arr[9] - &arr[0]);
    //指针减去指针的绝对值得到的是指针和指针之间元素的个数
    //不是所有的指针都能相减
    //指向同一块空间的2个指针才能相减!

    return 0;
}

int my_strlen(char* str)
{
    char* start = str;
    while (*str != '\0')
    {
        str++;
    }
    return str - start;
}

int main()
{
    int len = my_strlen("abcdef");
    printf("%d\n", len);
    return 0;
}

4.3指针的关系运算

for(vp = &values[N_VALUES]; vp > &values[0];)

       *--vp = 0;

}

代码简化, 这将代码修改如下:

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)

{

      *vp = 0;

}

实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证 它可行。

标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较。

5.指针和数组

数组:一组相同类型元素的集合
指针变量:是一个变量,存放的是地址

例1

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	return 0;
}

结果如下:

可见数组名和数组首元素的地址是相同的

结论:数组名表示的是数组首元素的地址。不过有两种情况除外,可以在以下文章中4.一维函数的数组名可见

C语言 数组-CSDN博客文章浏览阅读518次,点赞20次,收藏8次。本文详细介绍了一维数组、二维数组、冒泡排序。https://blog.csdn.net/qq_58094522/article/details/136687874可以这么写代码

int arr[10] = {1,2,3,4,5,6,7,8,9,0};
 int *p = arr;//p存放的是数组首元素的地址

 例2

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	//arr 是首元素的地址
	//&arr[0]
	int* p = arr;
	//通过指针来访问数组
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%p ------- %p\n", &arr[i], p + i);
	}
	
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

结果如下:

 所以p+i其实计算的是数组 arr 下标为i的地址

以下两种方式都是正确的

#include <stdio.h>
void test(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
}

void test(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);//arr[i] --> *(arr+i)
	}
}

int main()
{
	int arr[10] = { 0 };
	test(arr, 10);
	return 0;
}

6.二级指针

二级指针变量是用来存放一级指针变量的地址的

int main()
{
    int a = 10;
    int* pa = &a;//pa是一个指针变量,一级指针变量
    //  *说明pa是指针,而int是在说pa指向的对象是int类型,这里指的是pa指向的a的类型是int
    int** ppa = &pa;//ppa是一个二级指针变量
    //int*在说明ppa指向的对象pa是int*类型
    //第二颗*说明ppa是指针

    **ppa = 20;

    //*pa = 20;
    printf("%d\n", *pa);
    return 0;
}

二级指针的运算

*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa , *ppa 其实访问的就是 pa .

int b = 20;

*ppa = &b;//等价于 pa = &b;

**ppa 先通过 *ppa 找到 pa ,然后对pa进行解引用操作:*pa ,那找到的是a.

**ppa = 30;

//等价于*pa = 30;

//等价于a = 30;

7.指针数组

存放指针的数组就是指针数组

int main()
{
    int a = 10;
    int b = 20;
    int c = 30;

    int arr[10];
    
    int* pa = &a;
    int* pb = &b;
    int* pc = &c;

    int* parr[10] = { &a,&b,&c };
    //parr就是存放指针的数组
    //指针数组

    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d ", *(parr[i]));
    }
    return 0;
}

用指针表示二维数组

#include <stdio.h>
int main()
{
	int arr1[4] = { 1,2,3,4 };
	int arr2[4] = { 2,3,4,5 };
	int arr3[4] = { 3,4,5,6 };
	int* parr[3] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 4; j++)
		{
			printf("%d ", parr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

8.结语

今天的经验分享就到这里,有喜欢的朋友可以点赞➕评论➕收藏➕关注,如果有不懂的地方可以咨询博主,谢谢大家支持博主!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Mozi-goethe

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

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

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

打赏作者

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

抵扣说明:

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

余额充值