8. C语言指针初阶

本文详细介绍了指针的概念,包括指针作为内存地址以及指针变量的用法。讨论了指针类型的意义,强调了不同类型指针解引用的权限和步长差异。还深入探讨了野指针的成因及避免方法,如初始化、越界访问和内存释放后的处理。此外,文章还涵盖了指针运算,包括指针加减整数、指针间比较以及在数组和字符串中的应用。最后,提到了二级指针和指针数组的概念,展示了它们在编程中的作用。

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

一、指针是什么

在计算机科学中,指针编程语言中的一个对象,利用地址,它的值直接指向存在电脑存储器另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化地称为“指针”。
通过它可以找到以它为地址的内存单元

指针理解的两个要点:

  1. 指针是内存中一个最小单元的编号,也就是地址。
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

总结:指针就是地址,口语说的指针通常是存放地址的指针变量。

可以这样理解:

内存

在这里插入图片描述

int main()
{
	int a = 10; 	// a 是一个整型变量,占四个字节空间
	int* pa = &a;	// 对变量 a 取地址用'&'操作符。这里将第一个字节的地址存放在变量pa 中,pa 是一个指针变量。

	*pa = 20;		// '*'是解引用操作符,通过对指针变量 pa 解引用找到整型变量 a ,可以直接改变 a 的值。	
	return 0;
}

总结:

  • 在32位的机器上,地址是32个0或1组成的二进制序列,那么地址用4个字节来存储,所以一个指针变量的大小应该是4个字节。
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
  1. 指针是用来存放地址的,地址对一块空间来说是唯一的。
  2. 指针的大小在32位平台是4个字节,在64位平台是8个字节。

二、指针和指针类型

2.1 指针类型

int num = 10;
int* p = #

p 是一个指针变量,相应的类型有:

char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;

在这里插入图片描述

由此说明指针变量占字节数与指针变量类型无关

2.2 指针类型的意义

  1. 指针类型决定了,指针解引用的权限有多大。
  2. 指针类型决定了,指针走一步的步长为多少。
int a = 0x11223344;
printf("%d\n", a);
int* pa = &a;
*pa = 0;
//char* pc = &a;
//*pc = 0;
return 0;

在这里插入图片描述

在这里插入图片描述

int 类型指针解引用时改变了4个字节
char 类型指针解引用时改变1个字节

int n = 10;
char *pc = &n;
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);

在这里插入图片描述

int 类型指针步长为4
char类型指针步长为1

实例:

int arr[10] = { 0 };
int* p = arr;                 //数组名arr是数组首元素的地址
for (int i = 0; i < 10; i++)
{
   printf("%p\n", p + i);    //遍历输出数组中每个元素的地址
}

三、野指针

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

3.1 野指针成因

  1. 指针未初始化
int* p;	// 局部变量指针未初始化,默认为随机值
*p = 20;// 非法访问内存

p 为野指针

  1. 指针越界访问
int arr[10] = { 0 };
    int* p = arr;
    int i = 0;
    for (i = 0; i <= 10; i++)
    {
        *p = i;
        p++;
    }

当 i = 10时,执行 i++ ,后 i = 11,*p 越界访问 arr[11]

  1. 指针指向的内存释放

内存中申请一块空间,并由一个指针指向它,但之后这块内存被释放,这个指针仍指向原先位置(此时没有申请内存)。指针成为野指针。

#include<stdio.h>
int* test()
{
    int a = 10;
    return &a;
}
int main()
{
    int* p = test();   	// test函数返回a的地址,但是出函数后a被销毁 
    *p = 20;			// *p指向的内存被释放,*p 成为野指针。
    return 0;
}

3.2 如何规避野指针

  1. 指针初始化
int* p = NULL;

int a = 10;
int* pa = &a;
  1. 小心指针越界
  2. 指针指向空间释放时及时置NULL
  3. 指针使用前检查有效性

四、指针运算

指针运算

  • 指针 + - 整数
  • 指针 - 指针
  • 指针的关系运算

4.1 指针 + - 整数

#include<stdio.h>
#define sus 10			// 定义 sus 为10
int main()
{
    float v[sus] = { 0 };		// 定义并初始化数组 v 
    float* pv = NULL;			// 定义并初始化指针 pv 
    for (pv = &v[0]; pv < &v[sus];)		// 指针 pv 指向数组的第一个元素,且小于 v[10](下标最大为 9 )
    {
        *pv = 2;
        pv += 1;	// 指向数组的下一个元素
    }
    //	for 循环体也可写为 *pv++ = 2	/ *(pv++) = 2 / *(pv)++ = 2
    return 0;
}

4.2 指针 - 指针

#include<stdio.h>
int main()
{
    int arr[10] = {0,1,2,3,4,5,6,7,8,9};

    printf("%d",&arr[9] - &arr[0]);		// 两个指针(地址)相减
    
    return 0;
}

结果为两个指针之间的元素个数

指针相减的前提是两个指针指向同一块空间

实例:统计字符串长度

  1. 递归法
#include<stdio.h>
int my_strlen(char* str)
{
    while(*str!='\0')					// 字符串结束的标志是 '\0'
        return 1 + my_strlen(str+1);	// 函数递归
    return 0;
}
int main()
{
    int arr[100];
    scanf("%s",&arr);			// 数组赋值
    int len = my_strlen(arr);
    printf("%d",len);
    return 0;
}
  1. 指针法
#include<stdio.h>
int my_strlen(char* str)
{
    char* p = str;		// 记录第一个元素的指针
    while(*str!='\0')
        str++;			// 遍历数组直到最后一个元素
    return str - p;		// 返回两个指针相减的结果
}
int main()
{
    int arr[100];
    scanf("%s",&arr);
    int len = my_strlen(arr);
    printf("%d",len);
    return 0;
}

4.3 指针关系运算

for( vp = &v[10] ; vp>v[0] ; )
{
	*--vp = 0;
}

c语言标准规定:

允许指向数组元素的指针和指向数组最后一个元素之后内存位置的指针比较,但是不允许和指向数组第一个元素之前内存位置的指针比较

五、指针和数组

数组名是数组首元素的地址

int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i< 10 ; i++ )
{
	printf("%p <==> %p",&arr[i],p+i);
	// &arr[i] 和 p+i 一样都是数组第 i 个元素的地址
}
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = arr;	// 数组名
// [] 是一个操作符,2 和 arr 是两个操作数 
printf("%d\n", arr[2] );
printf("%d\n", 2[arr] );

// 编译时先将 arr[2] --> *(arr+2)
// arr[2] --> *(arr + 2) --> *(2 + arr) --> 2[arr]

// arr[2] <==> *(arr+2) <==> *(p+2) <==> *(2+p) <==> *(2+arr) == 2[arr]
// 2[arr] <==> *(2+arr)
return 0;

六、二级指针

int main()
{
	int a = 10;
	int* pa = &a;	 // pa 是指针变量,一级指针
	
	// ppa 是一个二级指针变量
	int* *ppa = &pa;	// pa 也是个变量,&pa 取出pa在内存中的起始地址
	// 第一个 '*' 表示定义指针类型,第二个 '*' 表示 *ppa 也是一个指针。
	int* **pppa = &ppa;
	return 0;
}

*ppa == pa
*pa == a
**ppa == a

七、指针数组

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

int arr[10];	// 整形数组
int* parr[10];	// 整形指针的数组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值