C语言—指针

1 指针

1.1 指针的定义

指针是内存单元的地址,它可能是变量的地址、数组的地址,或者是函数的入口地址。存储地址的变量称为指针变量,简称为指针。定义指针的格式如下

int* p;
char* p;

在使用指针时,需要明确两个概念,指针对象和指针指向的对象。指针对象指的是明确命名的指针变量,也就是上面的p。指针指向的对象是另一个变量,用*p来表示。

上面定义指针时,没有对指针进行初始化。未初始化的指针变量的值是随机的。在使用指针时,如果没有对指针进行初始化,可能会导致程序运行时出现非法指针访问错误,从而使程序异常终止。因此,在定义指针时需要对指针初始化

如果在定义指针时,并不能确定指针变量的初始值。可以将指针定义成空指针。

int* p = NULL;

或者

int* p = 0;

有一种特殊的指针类型void*,void*类型可以与任意的数据类型匹配。void 指针在被使用之前,必须转换为明确的类型。

1.2 “&”和“*”

“&”是地址运算符,它的作用是获取变量的地址。“*”是间接运算符,它的作用是获取指针所指向的变量。可以通过一个简单的程序,直观地了解一下这两个运算符的作用

#include<stdio.h>
int main()
{
    int i = 0;
    int* p = &i;   // 获取i的地址
    
    *p = 10;   // 给i赋值
    
	printf("**************************\n");
	printf("i的值为:%d\n",i);
	printf("**************************\n");
	return 0;
}

输出内容为

**************************
i的值为:10
**************************

有一种特殊类型的指针void*,void*类型可以与任意的数据类型匹配。void 指针在被使用之前,必须转换为明确的类型

#include<stdio.h>

int main()
{
    int i = 0;
    void* p = &i;   // 获取i的地址
    
    // void 指针在被使用之前,必须转换为明确的类型
    *(int*)p = 10;   // 给i赋值
    
	printf("**************************\n");
	printf("i的值为:%d\n",i);
	printf("**************************\n");
	return 0;
}

1.3 指针与堆内存

堆内存能够被动态地分配和释放,在 C 程序中通过malloc(或 calloc、realloc) 和 fee 函数实现对内存的分配和释放。

malloc是动态内存分配函数,用于申请一块连续的指定大小的内存块区域,以void*类型返回分配的内存区域的首地址。如果内存分配失败,会返回NULL。malloc函数的输入值是要开辟的内存所占的字节数。在使用malloc函数时,需要声明malloc.h头文件。可以通过一个简单的程序来了解一下malloc函数的使用方法

#include <stdio.h>
#include <malloc.h>

int main()
{
    // 分配一个4字节内存,p指向首地址
    int* p = (int*)malloc(sizeof(int));
    
    // 分配一个8字节内存,c指向首地址
    char* c = (char*)malloc(8 * sizeof(char));
    
    *p = 10;   // 将10存储到第一个内存块
    strcpy(c,"20230712");   // 将一串字符串存储到第二块内存块
    
	printf("**************************\n");
	printf("第一块内存内容为:%d\n",*p);
	printf("第二块内存内容为:%s\n",c);
	printf("**************************\n");
	return 0;
}

输出结果为

**************************
第一块内存内容为:10
第二块内存内容为:20230712
**************************

因为内存资源是有限的,所以若申请的内存块不再需要就及时释放。如果程序中存在未被释放(由于丢失其地址在程序中也不能再访问) 的内存块,则称为内存泄漏。持续的内存泄漏会导致程序性能降低,其至崩溃。释放内存块使用的是free函数。使用后该指针变量一定要重新指向NULL,防止悬空指针(失效指针)出现,有效规避错误操作

	free (p);   // 释放第一块内存
	*p = 0;   // 将p重新设置为空指针
	
	free (c);   // 释放第二块内存
	*c = 0;   // 将c重新设置为空指针

1.4 指针运算

指针变量可以加上或者减去整数值,叫做指针的运算。指针运算会在利用指针访问数组元素时用到,下面会介绍。

#include <stdio.h>

int main()
{
    int* p = 0x20020;
    
    p = p + 1;
    
    printf ("p的地址为:%p",p);
	
	return 0;
}

输出结果为

p的地址为:0x20024

加1的意思是指向下一个地址。注意这里的加1实际是往后推了4字节的内容。具体往后推几个字节,取决于指针变量的类型

1.5 常量指针与指针常量

1.5.1 常量指针

指针指向的对象时常量,这个指针叫做常量指针

const int* p = 0;   // 定义一个常量指针

在定义时,const修饰p,而p是指针指向的对象。它可以等效定义为

int const* p = 0;   // 定义一个常量指针

常量指针由于指针指向的对象是常量,所以指针指向对象的值是不可以被改变的。但是指针变量本身的值可以改变。

1.5.2 指针常量

指针常量是指指针本身是一个常量,不能再指向其他对象。

int* const p = 0;   // 定义一个指针常量

由于指针常量中,指针本身是一个常量,所以指针的值不能被改变。但是指针指向对象的值可以被改变。

1.6 函数指针

在C语言中,可以将函数地址保存在函数指针变量中,然后用该指针间接调用函数。函数指针的定义方法是

函数的返回值类型(*指针名)(函数的参数列表类型)

看一个简单的例子

#include <stdio.h>

int add (int a,int b)
{
    return a + b;
}

int main()
{
    int i = 0;
    int (*p)(int,int) = &add;   // 定义函数指针
    
    i = p(2,3);
    
    printf ("i=%d",i);
	
	return 0;
}

输出结果为

i=5

在定义函数指针时。&函数名,是函数的起始地址

2 指针与数组

可以利用指针去访问数组。看一个简单的例子

#include <stdio.h>

int main()
{
    int temp = 0;   // 临时循环变量
    char a[10];
    char* p = &a[0];   // 定义一个指针,指向数组起始地址
    
    // 利用指针给数组成员赋值
    for (temp = 0;temp < 10;temp ++)
    {
        *(p + temp) = temp;
    }
    
    // 利用指针读取数组成员的值
    for (temp = 0;temp < 10;temp ++)
    {
        printf ("a[%d]=%d\n",temp,*(p + temp));
    }
	
	return 0;
}

输出结果为

a[0]=0
a[1]=1
a[2]=2
a[3]=3
a[4]=4
a[5]=5
a[6]=6
a[7]=7
a[8]=8
a[9]=9

3 指针与函数

指针可以作为函数的输入参数或者返回值。

这里看一个常见的函数,利用指针交换两个参数的值

#include <stdio.h>

void swap (int* x,int* y);

int main()
{
    int i = 10;
    int j = 100;
    
    swap (&i,&j);
    
    printf ("i=%d,j=%d",i,j);
	
	return 0;
}

void swap (int* x,int* y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}

输出结果为

i=100,j=10

其中指针x和y是函数swap的输入参数。

指针也可以作为函数返回值,但是把指针作为返回值不是很推荐

4 指针与链表

4.1 链表

什么是链表?链表是一种动态地进行存储分配的数据结构。链表中的每一个元素称为一个结点。每个结点都应该包含两个部分,分别是用户实际需要的数据和下一个结点的地址。链表有一个头指针变量,这个变量存放一个地址,这个地址指向一个元素。链表的最后一个元素称为表尾,它的地址部分存放NULL。

链表中各个元素的地址可以是不连续的。要找某一个元素,必须先找到该元素的上一个元素,根据上一个元素提供的地址来找到下一个元素。如果不知道头指针,那么整个链表都无法访问。链表的基本结构如下

链表的基本结构

4.2 链表的建立与输出

这里通过一个例子来展示一下链表的建立与输出。

#include<stdio.h>

struct student
{
    char* name;
    int age;
    char* number;
    struct student* next;   // 下一个结点的地址
}student;

int main()
{
    struct student a,b,c;
    struct student* head;   // 头指针
    struct student* p;   // 用来访问链表的指针
    
    // 结构体成员赋值
    a.name = "ertu";
    a.age = 23;
    a.number = "001";
    
    b.name = "yingting";
    b.age = 18;
    b.number = "002";
    
    c.name = "yitong";
    c.age = 14;
    c.number = "003";
    
    head = &a;   // 头指针
    a.next = &b;
    b.next = &c;
    c.next = NULL;   // 表尾
    
    p = head;
    
    // 输出链表内容
    while (p != NULL)
    {
    	printf("**************************\n");
    	printf("Name:%s,Age:%d,Number:%s\n",p->name,p->age,p->number);
    	printf("**************************\n");
    	
    	p = p->next;
    }

	return 0;
}

输出结果为

**************************
Name:ertu,Age:23,Number:001
**************************
**************************
Name:yingting,Age:18,Number:002
**************************
**************************
Name:yitong,Age:14,Number:003
**************************
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

二土电子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值