C语言——指针 ①

本文围绕C语言指针展开,介绍了指针的必要性,如共享内存数据、构建复杂数据结构等。阐述了指针的定义、运算,包括赋值和算术运算,还讲解了空指针和野指针的概念、野指针的成因及规避方法,帮助读者入门C语言指针。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一. 指针 - 入门

1.1 为什么需要指针?

1.2 什么是指针?

1.3 指针变量的定义

1.3.1 指针的大小

1.4. 指针的运算

1.4.1 赋值运算

1.4.2 算术运算

1.5 空指针

1.6 野指针

1.6.1 野指针的成因

1.6.2 规避野指针


一. 指针 - 入门

1.1 为什么需要指针?

①   指针的使用使得不同代码区域可以轻松地共享内存数据。尽管你也可以通过复制数据来达到相同的效果,但这往往效率不高。对于占用大量字节的结构体等大型数据,复制操作会消耗很多性能。然而,使用指针就可以有效避免这个问题,因为任何类型的指针所占用的字节数都是一样的(根据平台可能是4字节、8字节或其他)。

②   指针使得构建一些复杂的连续性数据结构成为可能,例如链表、链式二叉树等等。

③   某些操作必须使用指针,比如操作申请的堆内存。此外,在C语言中,所有函数调用中的值传递都是"按值传递"的,如果我们要在函数中修改被传递过来的对象,就必须使用该对象的指针来实现。

1.2 什么是指针?

      指针是一种特定的数据类型,包括int指针类型、char指针类型、double指针类型等等。

指针用于存储变量或数据在内存中的地址。它可以指向其他变量或数据的位置,通过指针可以直接访问或修改这些数据。

      指针变量是用来存储内存地址的。

总结:

指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量。

1.3 指针变量的定义

指针变量的一般形式为:数据类型 *指针变量名  

解释:

   数据类型表明指针所指向对象的类型

   * 表明声明的变量是指针

例如:

int *p;  //定义了一个指针变量p,指向整型变量。

注意:

①  无论何种类型的指针变量,它们都是用来存放地址的,因此指针变量自身所占内存的大小它所指向的变量数据类型无关尽管不同类型的变量所占内存空间不同,但是不同类型指针变量所占内存空间大小相同。

②   "*" 在指针声明时表示该变量是一个指针变量而在使用指针时用于间接引用指针所指向的数据。

③   指针的类型和它所指向变量的类型必须相同。


1.3.1 指针的大小

    指针是一个用于存储内存地址的变量,它本身也需要在内存中占用一定的空间。

    指针变量的大小通常是由编译器和所运行的计算机体系结构决定的。  指针的大小与所指向的数据类型无关。无论是指向整型、字符型、结构体还是其他类型的指针,在相同的系统和编译器下,它们的大小都是相同的,但是步长不相同。

    在大多数32位系统上,指针通常占用4个字节的内存空间。而在64位系统上,指针通常占用8个字节的内存空间。

1.4. 指针的运算

1.4.1 赋值运算

    指针的赋值运算是将一个地址赋给指针变量使其指向该地址

赋值运算使用赋值操作符"=",将一个地址或另一个指针赋给指针变量

int num = 10;       // 声明一个整型变量num,并赋值为10
int *p = #      // 声明一个指向整型数据的指针p,并将num的地址赋给p

int *q;             // 声明一个指向整型数据的指针q
q = p;              // 将指针p的值(即num的地址)赋给指针q

&运算符用于获取变量的地址,&num代表变量num的地址。

指针变量p通过赋值运算符,将&num赋给它,使得p指向了变量num所在的内存地址。

      需要注意的是,指针的类型必须与它所指向的数据类型相匹配

在上述示例中,p和q都是指向int类型数据的指针,因此可以进行赋值运算。

指针的赋值运算是将一个地址赋给指针变量,从而使指针指向特定的内存位置。

通过指针,我们可以访问或修改该内存位置上存储的数据


1.4.2 算术运算

     指针的算术运算是基于指针所指向的数据类型的大小来计算的并且要求指针变量指向的是数组或是动态分配的内存块,而非指向单个变量的指针,所以在使用指针算术运算时,需要保证指针变量指向的是合法的内存地址。

指针加法运算:

      当一个指针加上一个整数时,指针的值会增加相应的偏移量,偏移量取决于指针所指向的数据类型大小

      例如,指针变量p指向一个int类型的数组,则通过p + N得到的是数组中第N个元素的地址(即&p[N])

指针减法运算:

     当一个指针减去一个整数时,指针的值会减少相应的偏移量,偏移量取决于指针所指向的数据类型大小

     例如,如果一个指针p指向一个int类型的数组,那么通过p - N可以得到指向数组中第N个元素前一个元素的指针,即p向前移动了N个元素的大小

1.5 空指针

意义

      空指针是不指向任何有效内存地址的指针,在C语言中,可以使用宏定义NULL或整数常量 0 来表示空指针,这样就标志此指针为空指针,没有指向任何空间。

注意

      对指针解引用操作可以获得它所指向的值。但从定义上看,NULL指针并未指向任何东西,因为对一个NULL指针解引用是一个非法的操作所以在解引用之前,必须确保它不是一个NULL指针。

1.6 野指针

概念

       野指针就是指向的内存地址是未知的(随机的,不正确的,没有明确限制的)。
说明

       指针变量也是变量,是变量就可以任意赋值。但是,任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针,此指针指向的区域是未知(操作系统不允许操作此指针指向的内存区域)。


注:野指针不会直接引发错误,操作野指针指向的内存区域才会出问题。

int a = 10;
int *p;

p = a;       //把a的值赋值给指针变量p,p为野指针,不会有问题,但没有意义
 
p = 0x555;   //给指针变量p赋值,p为野指针,不会有问题,但没有意义

*p = 10;     //对野指针进行赋值操作就出错了


1.6.1 野指针的成因

1. 指针未初始化:指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,所以它所指的空间也是随机的。

int main()
{
	int * p;

	*p = 100;

	return 0;
}

2. 指针越界访问指针指向的范围超出了合理范围,或者调用函数时返回指向栈内存的指针或引用,因为栈内存是在函数结束时会被释放。

int main()
{
	int arr[5] = {0};

	int *p = arr;

	for(int i = 0; i <= 6; i++)
	{
		*(P++) = i;    //当指针指向的范围超出数组arr的范围,p变成野指针。
	}

	return 0;
}

3 .指针释放后未置空有时指针在free或delete后未赋值 NULL,并没有把指针本身忘记。此时指针指向的就是无效内存。

int main()
{
	int *p = NULL;

	p = (int *)malloc(sizeof(int) * 5);

	free(p);

	return 0;
}

1.6.2 规避野指针

1. 初始化指针

int main()
{
    /*1.系统分配的内存*/

    int a;
    int *p = &a;

    /*2.用户申请内存(堆内存)*/

    char *q = (char *)malloc(128);
    
    return 0;
}

2. 避免指针越界

int main()
{
	int arr[5] = {0};

	int *p = arr;
	for(int i = 0; i < 5; i++)
	{
		*(P++) = i;//严格遵守有效范围。
	}

	return 0;
}

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

int * test()
{
	int a = 20;

	return &a;
    //局部变量存在栈区,当被调函数结束后 ,栈区上局部变量的内存空间被释放,
    //若再去访问该空间就不合理了
}

int main()
{
	int *p = NULL;

	p = test();

	printf("%d\n", *p);

	return 0;
}

4. 开辟的指针释放后置为NULL

int main()
{
	int *p = NULL;

	p = (int *)malloc(sizeof(int) * 128);

	//成功开辟内存,可以操作内存。
	free(p);

	p = NULL;//避免野指针

	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值