news 2026/9/28 11:44:09

C 语言指针从入门到避坑:把指针踩在脚底(4)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C 语言指针从入门到避坑:把指针踩在脚底(4)

目录

  • 字符指针变量
    • char*
  • 数组指针变量
    • 数组指针变量是什么
    • 数组变量的写法与初始化
  • 二维数组传参的本质
  • 函数指针变量
    • 函数指针变量是什么及创建
    • 函数指针变量怎么用
  • 两个有趣的代码
  • typedef 关键字
  • 函数指针数组
    • 函数指针数组的运用——转换表

字符指针变量

char*

我们一般使用char*时,是如下使用的

intmain(){charc='w';char*ch=&c;return0;}

这里有char的另一种用法,即可以用char来存放字符串。

存放字符串的两种方法

  • 运用数组————char arr[ ] =…
  • 运用字符指针变量,如下
intmain(){char*ch="abcdefg";//“abcdefg"是常量字符串,指针变量ch存放的是a的地址printf("%c",*ch);//这里打印的是aprintf("%s",*ch);//这里打印的是abcdefgreturn0;}

不过即然说了abcdefg是常量字符串,所以它是不能更改的。为了严谨性和避免程序崩溃,我们一般在char* ch前添加const以表明不能修改。


下面再分享一道题加深理解

#include<stdio.h>intmain(){charstr1[]="hello bit.";charstr2[]="hello bit.";constchar*str3="hello bit.";constchar*str4="hello bit.";if(str1==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return0;}

结果

str1与str2因为str1与str2是数组,它们先向内存申请空间地址,再存放hello bit. 它们只是存放的的内容相同,但是str1与str2比较的本质是数组的起始地址,所以结果是are not same
str3与str4这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同⼀个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。


同理,字符指针数组

intmian(){char*pp[3]={"abcd","hijk","wml"};//pp[1]存的是字符a的地址//pp[2]存的是字符h的地址//pp[3]存的是字符w的地址return0;}

数组指针变量

数组指针变量是什么

先申明数组指针变量是指针,具体是什么见如下类比

整型指针int*p 指向整型变量的指针,存放的是整型变量的地址 字符指针char*p 指向字符变量的指针,存放的是字符变量的地址 数组指针int(*p)[5]=&arr 指向数组的指针,里面存放的是数组的地址

注意区分前面所学的指针数组:它表示存放指针变量的数组。


数组变量的写法与初始化

intmain(){intarr[3]={1,2,3};int(*p)[5]=&arr;//&数组名 是整个数组的地址return0;}

理解

  • 为什么是(p): 因为*与p在一起才能表示p是指针变量,如果不加()就变成了intp[3],此时*与int在一起了,p成指针数组了。
  • [参数] :表明指向的数组有几个元素。
intmain(){chararr[3]={'a','b','c'};char(*p)[3]=&arr;return0;}

以上也是一个数组变量

二维数组传参的本质

voidtest(arr[3][5],inta,intb){inti=0;intj=0;for(i=0;i<a;i++){for(j=0;j<b;j++0){printf("%d ",arr[i][j]);}printf("\n");}intmain(){intarr[3][5]={{1,2,3,4,5},{2,3,4,5,6},{8,9,5,4,5}};test(arr,3,5);return0;}

前文所说二维数组的元素是一维数组(每一行),所以当二维数组传参时,传的也是首元素的地址,即第一行(一维数组)。所以函数对二维数组传参的接收有另一种写法

voidtest(int(*p)[5],inta,intb)//接收的是第一行代表的一维数组的地址{inti=0;intj=0;for(i=0;i<a;i++){for(j=0;j<b;j++0){printf("%d ",*((*arr+i)+j))://数组地址+-与数组中每一个元素+-不同}printf("\n");}

函数指针变量

函数指针变量是什么及创建

类比前文,函数指针————指向函数的指针变量,用来存放函数的地址。

voidtest();intmain(){printf("%p",test);//打印的是函数的地址printf("%p",&test);//打印的是函数的地址return0;}

不同于数组

  • 数组名是数组首元素的地址,函数名是函数的地址
  • &数组名是数组的地址,&函数名是函数的地址

下面是函数指针的写法

voidtest(intx,inty);intmain(){void(*p)(intx,inty)=test//P1 P2 P3return0;
  • P1:与指向函数的返回类型有关
  • P2:指针变量的名字
  • P3:与指向函数的参数有关

该指针变量的类型为 ==void(*)(int x,int y)==即去掉指针变量的名字

函数指针变量怎么用

函数指针变量是通过解引用来使用的,一般有两种解引用

  • (*p)(传入的参数): (*p)就等效于函数本身,注意括号不能少。
  • p(传入的参数):p就等效于函数本身,这里可以不用解引用操作符*仅限函数指针
#include<stdio.h>intAdd(intx,inty){returnx+y);}intmain(){inta=3;intb=5;int(*p)(intx,inty)=Add;//也可以是&Addintr=(*p)(a,b);//int r = p(a,b);printf("%d",r);//打印的结果是8。

如上展示了函数指针变量解引用的用法。

两个有趣的代码

(*((void)(*)())0)();

上述代码实质是调用0地址代表的函数。

  • ((void)(*)())是强制类型转换,将0转换为一个函数指针,它此时代表着0地址的函数。
  • 而( *((void)(*)())0) (),是在调用这个函数指针,这个函数没有返回值,也没有参数。

void(*signal(int,void(*)(int)))(int);

上述代码是一个函数声明。

  • 一个signal函数,传参类型是int和void(*)(int)类型的
  • 它的返回类型是外围的void(*)(int)类型的

typedef 关键字

typedef 能够重新将复杂的类型重命名

下面将展示常见的typedef用法


简化一些复杂的类型

typedefunsignedintunit;unit a=7;//或者简化数组指针typedefint(*ptr_t)[5];//c语言语法,不能写成typedef int(*)[5] ptr_tintarr[5]={0};ptr_tp=&arr;//或者简化函数指针typedefvoid(*ptr_t)(int);//不能写成typedef void(*)(int)ptr_tptr_tsignal(int,ptr_t);

函数指针数组

当我们想要用到一个可以用来存放函数指针的数组时,就需要函数指针数组

intAdd(intx,inty){}intSub(intx,inty){}intMul(intx,inty){}intDiv(intx,inty){}intmain(){int(*per[4])(int,int)={Add,Sub,Div,Mul};

和数组的写法 类型+数组名+[ 参数 ]类似
int(*)(int,int)是数组的类型,per是数组名。
该数组中存放的函数指针都是**int(*)(int,int)**类型的。

函数指针数组的运用——转换表

#include<stdio.h>intadd(inta,intb){returna+b;}intsub(inta,intb){returna-b;}intmul(inta,intb){returna*b;}intdiv(inta,intb){returna/b;}intmain(){intx,y;intinput=1;intret=0;int(*p[5])(intx,inty)={0,add,sub,mul,div};//转移表do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n");printf("*************************\n");printf("请选择:");scanf("%d",&input);if((input<=4&&input>=1)){printf("输⼊操作数:");scanf("%d %d",&x,&y);ret=(*p[input])(x,y);printf("ret = %d\n",ret);}elseif(input==0){printf("退出计算器\n");}else{printf("输⼊有误\n");}}while(input);return0;}

以上通过输入一个数,找到对应函数指针数组里的函数并且调用该函数,这种类似跳转的感觉就是转换表
局限性只有返回类型和参数类型相同的函数,才能存入数组

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 11:43:38

生成模型杂谈——Consistency Model

Consistency Model 的训练核心是构造这样一对样本: ztz_tzt​:生成轨迹上较“噪声”的状态; zsz_szs​:同一条轨迹上另一个时间点的状态; s<ts<ts<t,并且二者应该最终生成同一个干净样本 xxx。 然后让网络在这两个状态上的输出尽可能一致: fθ(zt,t)≈fθ(zs,…

作者头像 李华
网站建设 2026/9/28 11:43:15

SST固态变压器技术漫谈【9】从样机到量产的核心工程 Knowhow

模块九 从样机到量产的核心工程 Knowhow 本章内容在公开文献中基本查不到,或者只给结论不给路径。它们来自样机炸机、现场整改、量产返修的代价。 摘要:本章聚焦固态变压器(SST)从样机到量产的核心工程 Knowhow,涵盖高压器件串联均压、高频 LLC 谐振点漂移抑制、多模组并…

作者头像 李华
网站建设 2026/9/28 11:40:09

微信小程序form组件详解:表单提交与重置

一、form组件简介 在小程序开发中&#xff0c;表单是最常见的交互形式之一。微信小程序提供了form组件&#xff0c;用于将内部的表单控件&#xff08;如input、checkbox、radio等&#xff09;的值进行收集和提交。 form组件的核心作用是&#xff1a;当用户点击提交按钮时&#…

作者头像 李华
网站建设 2026/9/28 11:40:04

293基于SpringBoot4+Vue3的全民反诈宣传平台、反诈宣传网站、反诈宣传系统、电信网络诈骗宣传平台、全民反诈网站;毕业设计、课程设计

✅博主简介&#xff1a;Java全栈开发工程师&#xff08;bishecoder&#xff09;&#xff0c;精通Java开发、系统设计、项目实战。 ✅技术栈&#xff1a;SpringBoot、Vue、React、Node.js、Nest.js、uni-app等 ✅技术擅长&#xff1a;定制项目、修改代码、编写文档、技术指导等。…

作者头像 李华
网站建设 2026/9/28 11:38:02

Cursor 最新规则下,用 TaoToken 统一 Key 打通 AI 工具链的配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华