C语言—指针

简介: 本文介绍了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。

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

92bed97f687c350b45f5a674f6ebcac1_29a8cb3cfa2b41beacac48a1ff7e55d8.png

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
**************************
相关文章
|
2月前
|
C语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
54 0
|
18天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
71 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
18天前
|
存储 编译器 C语言
【C语言】指针大小知多少 ?一场探寻C语言深处的冒险 !
在C语言中,指针的大小(即指针变量占用的内存大小)是由计算机的体系结构(例如32位还是64位)和编译器决定的。
44 9
|
18天前
|
安全 程序员 C语言
【C语言】指针的爱恨纠葛:常量指针vs指向常量的指针
在C语言中,“常量指针”和“指向常量的指针”是两个重要的指针概念。它们在控制指针的行为和数据的可修改性方面发挥着关键作用。理解这两个概念有助于编写更安全、有效的代码。本文将深入探讨这两个概念,包括定义、语法、实际应用、复杂示例、最佳实践以及常见问题。
40 7
|
28天前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
100 13
|
21天前
|
存储 程序员 编译器
C 语言数组与指针的深度剖析与应用
在C语言中,数组与指针是核心概念,二者既独立又紧密相连。数组是在连续内存中存储相同类型数据的结构,而指针则存储内存地址,二者结合可在数据处理、函数传参等方面发挥巨大作用。掌握它们的特性和关系,对于优化程序性能、灵活处理数据结构至关重要。
|
22天前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
67 3
|
22天前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
28天前
|
存储 C语言 开发者
C 语言指针与内存管理
C语言中的指针与内存管理是编程的核心概念。指针用于存储变量的内存地址,实现数据的间接访问和操作;内存管理涉及动态分配(如malloc、free函数)和释放内存,确保程序高效运行并避免内存泄漏。掌握这两者对于编写高质量的C语言程序至关重要。
52 11
|
22天前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
34 1