百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

C语言基础知识:最核心的—指针!知识总结(第二部分)

yund56 2025-05-08 16:41 11 浏览

指针是C语言最重要也是最难理解的部分,它在我们平时的工作中无处不在。

今天我们继续来看看指针的剩下的知识总结吧!上一批的话可以在主页看到哦~

5 指针与结构体

一个指针,它指向的可以是一个结构体类型,这称为结构体指针。而一个结构体,它的成员中也可以有指针成员。

struct{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组
    float score;  //成绩
} stu1 = { "Tom", 12, 18, 'A', 136.5 }, *pstu = &stu1;

上面的代码中,定义了一个结构体变量stu1。这个变量中有一个指针变量name。还定义了一个结构体指针pstu。

我们想要通过结构体指针访问结构体成员一般有以下两种方式:

(*pstu).name;
pstu->name;


6 指针与const:常量指针与指针常量

初学者常常对这两个概念搞错。首先,我认为需要理解这里说的常量是什么意思。常量就是只可读不可修改的。那常量指针和指针常量到底哪个是只可读不可修改的呢?是指针还是指针指向的内容?这里有一个方法,能让你迅速明白哪个是不可修改的。就是在声明时,以星号(*)为界,分成两部分,星号左边的和星号右边的。const在哪边,那个就是只可读不可修改的。以下面这个代码为例,我们来分析一下:以星号(*)为界,星号左边是char,没有const关键词,所以它指向的内容不是常量。然后,我们看星号的右边是const ptr,所以我们可以说ptr是一个常量。所以,这行代码声明了一个是常量的指针但是指向的内容不是常量。即这个是一个指针常量。

char* const ptr = "just a string";

类似的,我们也可以分析下面的代码:

// Neither the data nor the pointer are const
//
char* ptr = "just a string";

// Constant data, non-constant pointer
//
const char* ptr = "just a string";

// Constant pointer, non-constant data
//
char* const ptr = "just a string";

// Constant pointer, constant data
//
const char* const ptr = "just a string";

6.1 指针常量(Constant Pointers)

指针常量(Constant Pointers): 它的本质是一个常量,只不过这个常量是指针。由于指针是只可读不可修改的,所以这个指针不能指向别的地址了,但是该地址里的内容还是可以改变的。指针常量的声明格式如下:

<type of pointer> * const <name of pointer>
例如: int * const ptr;

我们来看下序:

#include<stdio.h>
int main(void)
{
    int var1 = 0, var2 = 0;
    int *const ptr = &var1;
    ptr = &var2;
    printf("%d\n", *ptr);

    return 0;
}

上面这段程序中:

我们首先定义了两个变量var1,var2;

然后,定义了一个指针常量ptr,并且指向了var1

接着,试图让ptr指向var2

最后,打印出指针ptr指向的地址的内容

让我们来运行一下这个程序:

main.c: In function 'main':
main.c:6:9: error: assignment of read-only variable 'ptr'
     ptr = &var2;
         ^

我们看到这个程序编译报错了:试图对只读(read-only)变量ptr进行赋值。所以,一旦我们定义了指针常量,那这个指针就不能指向其他变量了。

但是我们还是可以修改指向的地址里的内容的:

#include<stdio.h>
int main(void)
{
    int var1 = 0;
    int *const ptr = &var1;
    *ptr = 10; // OK
    printf("%d\n", *ptr); // 10
    return 0;
}

6.2 常量指针(Pointer to Constants)

常量指针(Pointer to Constants):它的本质是一个指针,只不过它指向的值是常量(只可读,不可修改)。由于指向的是一个只可读不修改的值,所以指针不能通过它存储的地址间接修改这个地址的值,但是这个指针可以指向别的变量。

常量指针的声明格式如下:

const <type of pointer>* <name of pointer>
例如: const int* ptr;

还是有一段程序:

#include<stdio.h>

int main(void)
{
    int var1 = 0;
    const int* ptr = &var1;
    *ptr = 1;
    printf("%d\n", *ptr);

    return 0;
}

我们还是来分析一下这个程序:

我们定义了一个变量 var1,并且初始化为0

然后我们定义了一个指针常量ptr,并且将它指向了var1

接着,试图通过指针ptr来改变var1的值

最后,打印出ptr指向的地址的内容。

我们进行编译:

main.c: In function 'main':
main.c:7:10: error: assignment of read-only location '*ptr'
     *ptr = 1;
          ^

编译报错也很明显: *ptr是一个只读的。所以不能通过ptr来修改var1的值。

但是,我们可以将ptr指向其他的变量:

#include<stdio.h>

int main(void)
{
    int var1 = 0;
    const int* ptr = &var1;
    printf("%d\n", *ptr); // 0
    int var2 = 20;
    ptr = &var2; // OK
    printf("%d\n", *ptr); // 20
    return 0;
}

6.3 指向常量的常量指针

理解了上面两种类型的话,理解这个就很容易了。指向常量的常量指针是指这个指针既不能指向其他的地址也不能通过地址修改内容。

它的声明格式如下:

const <type of pointer>* const <name of pointer>
例如: const int* const ptr;

同样,下面一段程序,我想你一定知道哪里编译错误了。

#include<stdio.h>

int main(void)
{
    int var1 = 0,var2 = 0;
    const int* const ptr = &var1;
    *ptr = 1;
    ptr = &var2;
    printf("%d\n", *ptr);

    return 0;
}

编译结果:

main.c: In function 'main':
main.c:7:10: error: assignment of read-only location '*ptr'
     *ptr = 1;
          ^
main.c:8:9: error: assignment of read-only variable 'ptr'
     ptr = &var2;
         ^


7 指针与函数

7.1 函数指针

指针与函数相结合有两种情况:指针函数、函数指针。

指针函数,它的本质是一个函数,它的返回值是一个指针。

int * func(int x, int y);

函数名本身就是一个指针(地址),这个地址就是函数的入口地址。

#include <stdio.h>
int sum(int a, int b)
{
	return a + b;
}
 
int main()
{
	printf("%p\n", sum);
	return 0;
}

输出:

0000000000401550

而函数指针,它的本质是一个指针。只不过它存的地址恰好是一个函数的地址罢了。

函数指针变量定义的格式一般是:

返回值 (*变量名)(参数列表)

比如:

#include <stdio.h>
int sum(int a, int b)
{
    return a + b;
}
 
int main()
{
    printf("%p\n", sum);
    int (*psum)(int, int);  // 函数指针变量,参数名可以省略
    psum = sum;
    printf("%p\n", psum);
    return 0;
}

输出:

0000000000401550
0000000000401550

可以发现,两者地址相等。

函数指针类型的定义:

typedef 返回值 (* 类型名)(参数列表);复制代码

比如:

typedef int(*PSUM)(int, int);
PSUM pSum2 = sum;
PSUM pSum3 = sum;

这样的好处就是,首先通过typedef定义一个函数指针类型PSUM,定义完后,PSUM就相当于一种新的类型,可以用此类型去定义其他函数指针变量,就不用每次都使用int(*pSum)(int, int);来定义一个函数指针变量。

#include <stdio.h>
int sum(int a, int b)
{
    return a + b;
}
int func2(int a, int b)
{
    return a - b;
}
typedef int (*PFUNC) (int, int);
int main()
{
    int (*psum)(int, int);
    psum = sum;
    printf("psum(4, 5):%d\n", psum(4, 5));
    PFUNC p2 = func2;
    printf("p2(5, 2):%d\n", p2(5, 2));
    p2 = sum;
    printf("p2(5, 2):%d\n", p2(5, 2));
    return 0;
}

输出:

psum(4, 5):9
p2(5, 2):3
p2(5, 2):7


7.2 回调函数

说到函数指针,那还有一个概念不得不提——回调函数。因为在实际的项目代码中实在是太常见了。

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

那为什么要使用回调函数呢?或者说使用回调函数有什么好处呢?回调函数允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似的事情时,可以灵活的使用不同的方法。

怎么使用回调函数:

#include <stdio.h>
int Callback_1(int a)   ///< 回调函数1
{
    printf("Hello, this is Callback_1: a = %d \n", a);
    return 0;
}

int Callback_2(int b)  ///< 回调函数2
{
    printf("Hello, this is Callback_2: b = %d \n", b);
    return 0;
}

int Callback_3(int c)   ///< 回调函数3
{
    printf("Hello, this is Callback_3: c = %d \n", c);
    return 0;
}

int Handle(int x, int (*Callback)(int)) // 注意这里用到的函数指针定义
{
    Callback(x);
}

int main()
{
    Handle(4, Callback_1);
    Handle(5, Callback_2);
    Handle(6, Callback_3);
    return 0;
}

如上述代码:可以看到,Handle()函数里面的参数是一个指针,在main()函数里调用Handle()函数的时候,给它传入了函数Callback_1()/Callback_2()/Callback_3()的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。


8 二维指针

二维指针,或者二级指针。就是指向指针的指针。比如:

#include<stdio.h>
int main()
{
    int a = 10;
    int *pa = &a;
    int **ppa = &pa;
    printf("%p, %p, %p, %p, %p", a, pa, *pa, ppa, *ppa);
    return 0;
}

输出如下:

000000000000000A, 000000000061FE14, 000000000000000A, 000000000061FE08, 000000000061FE14

从输出结果也可以看到,pa存的内容*pa= 000000000000000A,刚好与a的地址相同。而ppa存的内容*ppa= 000000000061FE14也刚好等于pa的地址。它们之间的内存关系可以用如下的图表示:


8.1 命令行参数

处理命令行参数是指向指针的指针的一个用武之地。

一般main函数具有两个形参。第一个通常称为argc,它表示命令行参数的数目。第2个通常称为argv,它指向一组参数值。由于参数的数目并没有内在的限制,所以argv指向这组参数值(从本质上来说是一个数组)的第一个元素。这些元素的每个都是指向一个参数文本的指针。如果程序需要访问命令行参数,main函数在声明时就要加上这些参数。

int main(int argc, char **argv)

举例:

#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("argc: %d\n", argc);

    // 打印参数,直到遇到NULL指针。程序名被跳过
    while (*++argv != NULL) {
        printf("%s\n", *argv);
    }
    return 0;
}

在windows上执行: \test2.exe hello world

输出:

argc: 3
hello
world

注意,如果命令行中传递的一个参数包括空格,就需要用 ""将参数括起来,比如:

.\test2.exe "hello word"

则上面的代码将输出:

argc: 2
hello word


9 结束语

本文关于指针的讲解就结束了。我相信你一定对指针有更深入的了解。

对啦对啦!另外的话为了帮助大家,轻松,高效学习C语言/C++,我给大家分享我收集的资源,从最零基础开始的教程到C语言项目案例,帮助大家在学习C语言的道路上披荆斩棘!可以来我粉丝群领取哦~

编程学习书籍分享:

编程学习视频分享:

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!

相关推荐

Excel COUNTA 函数

COUNTA函数COUNTA函数是Excel中的一个预制函数,它计算范围内具有值(包括数字和字母)的所有单元格。输入=COUNTA如何使用=COUNTA函数:选择一个单元格输入=COUNT...

COUNTA函数与COUNT函数如何区分使用?

COUNTA函数是计数非空单元格的个数,COUNT函数是计数数值的个数,下面我们用实际例子来区分它们的使用情景1.一列都是数值的情况COUNTACOUNT都是数值的情况下是一样的2.存在文本数字COU...

COUNT、COUNTA和COUNTIF的区别

每天学习和分享Excel表格中应用的函数,今天用我自己的方式来聊一聊COUNT、COUNTA和COUNTIF的区别。首先COUNT是统计某一个区域中数值有多少个,比如一列中有数字、文字或者符号……...

90%的人分不清COUNT、COUNTA、COUNTBLANK!

别再被这三个统计函数搞懵了!今天用最炸裂的对比图+翻车案例,3分钟让你成为数据统计高手!一、核心区别(必存对比图)函数统计对象典型使用场景致命误区COUNT只认数字/日期(冷血无情)销售数据统计、年龄...

1个示例告诉你,SumproDuct才是求和函数No.1

原创作者:兰色幻想-赵志东转自:Excel精英培训兰色在网上看到一个同学关于求和的问题:用Sumif公式出现了错误的结果,明明是404.76,结果是2624.55。仔细一下,原来把前17位相同的单...

Sumproduct函数,轻松搞定按月&amp;按季度求和

举两个Excel函数在工作中经常用到的经典案例——按月求和与按季度求和。一、按月求和有一份详细的销售数据,需要按照月份和姓名进行汇总,“SUMPRODUCT”函数将成为你的得力助手。(1)首先设定第一...

轻松学会:Excel中行与列交叉查询的三个小妙招

先看一下如下动态图:这种通过月份跟水果名的选择,如何实现这行与列的交叉查询?我带来了三个简单又实用的小妙招,保证让你轻松上手!第一妙招:VLOOKUP与MATCH联手查询在你想显示查询结果的单元格中,...

替换函数Substitute都不会使用,那就真的Out了

在Excel中,如果要替换相应的数据,除了【查找和替换】功能外,还有一个函数可以完成此功能,此函数就是Substitute。功能:将字符串中的部分字符串以新字符串替换。语法结构:=Substit...

15个excel常用函数,可直接套用,几乎每天都用得到,收藏备用吧

Hello.大家好,今天跟大家分享15个Excel函数公式,都是我们工作中经常用到的公式,工作中遇到类似的问题,可直接套用,快速提高工作效率,话不多说,下面就让我们来一起学习下吧1.身份证号码提取出...

如何把单元格的数值每位数字进行相加?又学会一个Excel技巧

问题:单元格中有一个值,如何将这个值中的所有数字进行相加?例如:下图中,B3单元格的值为:1845,每个数字进行相加,即:1+8+4+5=18,也就是D3单元格中显示的结果。如何用函数公式来解决?具体...

Excel双向多条件求和,很多人都不会,一个SUMPRODUCT函数就搞定

我是【桃大喵学习记】,点击右上方“关注”,每天为你分享职场办公软件使用技巧干货!前几天有个粉丝提问了一个问题,就是如何对Excel表格数据进行双向多条件求和?所谓双向多条件求和就是对两个方向的条件进行...

vlookup快走开,它才是求和函数No.1

今天无意在网上看到一个用Vlookup函数求和的教程,还起了一个很吸引人的标题:你能想象vlookup函数还可以进行求和吗?如下图所示,要求在B11设置公式,根据A11姓名在上表中查找并计算它的1-6...

COUNTIF与SUMPRODUCT函数过招

原创:卢子1987转自:Excel不加班关于使用分隔符号-会出错的问题,这个是去年无意间发现的,这点希望所有人记住。详见文章:这是我此生见过COUNTIF函数,最奇葩的错误!COUNTIF和COU...

万能函数SUMPRODUCT,数组乘积之和

公式解析:【SUMPRODUCT】在给定的几组数组中,将数组间对应的元素相乘,并返回乘积之和当我们输入=SUMPRODUCT,会弹出如下的提示框用法解析(以下均为虚拟数据):1.直接求和:计算总销售额...

EXCEL中如何实现横竖双条件计数

在Excel中,要实现横竖双条件计数,可以使用COUNTIFS函数,它支持多个条件进行计数。以下是具体步骤和示例:方法:使用COUNTIFS函数COUNTIFS函数的语法为:=COUNTI...