C语言中的符号重载

《Expert C Programming》(C专家编程)读书笔记 C专家编程笔记 第一章 C: 穿越时空的迷雾 C 与 UNIX 的历史 1969: Multics 工程失败(通用电气、麻省理工、贝尔实验室) 1969: 一个简易的新型操作系统诞生 (用PDP-7汇编编写) 1970: 更名为 UNIX ,并采用 PDP-11 汇编重写 (所以UNIX系统时间从1970-01-01:00:00:00 算起) 1970: Dennis Ritchie 利用PDP-11的强大性能,创立了 “New B” 语言,即 C 的前身 1972: 可能是3月,更名为 阅读详情

C语言非常的简洁, 以至于不愿意用太多的符号, 这样有很多符号在不同的地方有不同的含义

这样会让用户很困惑, 这是c的语言特性, 也是设计上的一些失误

static    
在函数内部,表示该变量的值在各个调用间一直保持延续性;
对于函数,表示该函数只在本文件中可见

extern
 

用于变量,表示该变量在其它地方定义;
用于函数定义, 表示全局可见(属于冗余的)

void     

用于参数列表中,表示该函数参数为空,如int main(void);
用于返回值,表示该函数返回void,即不返回任何值,相当与pascal中的过程;
在指针声明中,表示通用指针

* 
乘法运算符;
 
用于指针前,表示对指针所指内容的间接引用;
用于声明中表示指针,如int *pi,表示指向整形的指针,char *strcpy(...)表示函数的返回值为指针

&    
  
位与运算符;
取地址操作符

() 

函数定义中,包围函数形式参数表,如int main(int argc, char **argv);
调用一个函数,如srand();
改变表达式的运算顺序,如 a * ( b + c );
类型转换, 如 (int*)x;
定义带参数的宏,如 #define MAX(a, b) ((a) > (b)) ? (a) : (b)
包围sizeof操作符的操作数(如果是类型名),如 sizeof(int)

 

数组和指针的本质区别

C编程新手最常听到的一句话是'数组和指针是相同的', 不幸的是, 这是一种危险的说法, 他并不正确.

最典型的一个错误是

file 1:
int mango[100];
file 2:
extern int *mango;


这样的声明是错的, 必须声明为extern int  mango[];

关于声明的意义在上篇讲解extern时已经详细说明过

 

我们首先要区分"地址y"和"地址y的内容"之间的区别, 这是一个相对微妙之处, 因为大多数编程语言中我们用同一个符号来表示两样东西, 并由编译器根据上下文环境来判断它的具体含义.

int X, Y;

X = Y;

这边我们就来深入讨论一下这个赋值语句, 看上去X和Y是同一种东西, 都是int型变量

但对于编译器而言, 一个赋值语句两边的东西是不一样的 (以汇编的角度思考)

左值  =  右值;

左值是地址, 右值是数据, 是有本质区别的

所以上面的X是X代表的地址, Y是Y代表的地址里面的内容

所以对于编译器而言,

X=2, 这个很直接, 把2放到X代表的地址上

X=Y, 需要先从Y代表的地址上读出数据, 再放到X代表的地址上

需要注意, 左值是在编译时就可以明确知道的, 因为编译器会为每个变量分配一个地址

而右值往往只有在运行时才可知的

 

那么下面详细看看为什么大家会误认为两者相同

int a[10], *p, *q;
p = a ;
q = p+1 ;

q = a+1;

int i;

i = a[0];

i = (*a);

i = (*p);

如上的代码, 你发现可以象操作指针一样来操作数组变量名a, 貌似a和指针p时等价的

其实非也, a其实代表数组的首地址, 就是个常量, 进行q = a+1, 类似于X = 2+1

而p是指针变量, p=a后, p代表的地址里存放了a,即数组首地址

那么q = p+1, 需要从p代表的地址中读出a, 并放到q代表的地址中去, 类似于X = Y+1

同样对于*操作, 是取地址上的内容, *a直接取a地址上的内容, 而*p是要先从p代表的地址取出地址数据, 再取该地址数据上的内容

而且两者还有一个区别是时间上的差别

对于q = a+1, 因为a代表常量, 在编译时就可以算出q的值

而对于q = p+1, p是变量, 必须到运行时才能知道q的值

 

两者是有本质区别的, a代表常量, 有人把它称为'常量指针', 是不能赋值或改变的指针, 这绝对的是误导

a就不是指针, 根本就不能作为左值, 你有见把2作为左值的吗

此处可以认为a等同于&p

之所以大家会混淆, 完全是因为c语言的设计不规范导致的, 相同的语句却有着完全不同的操作

 

此处个人觉得是数组的设计欠缺妥当

int i, * p;

对于一般的变量, 无论是i还是p, 作为左值表示变量名所代表的地址, 作为右值表示变量名所代表的地址上存放的数据. 其实i和p从这个层面上来讲, 完全没有什么分别, 只不过p中存放的数据是地址而不是其他.

 

但是对于int a[];

就 不一样了, 因为a只是作为一个数据集合的开始位置, a无法作为左值(设计者认为这样的不恰当). 同时a作为右值时, 也无法取a地址上的数据,   因为它只是个开始位置, 你不知道取多少. 所以设计者打破了普通变量的规则, 把a当作一个地址常量来看待, 即当a作为右值时, 直接取a代表的地址作为右值, 而不会象通常变量一样去取地址上的数据作为右值.

问题是, 根据c的语法, 其在使用时和指针又十分相似, 这样的设计是不严谨的. 其实如果这样设计会稳妥些,

将a[]作为一个整体, 不能单独对a做任何操作, 如需取地址则用&(a[])

不过这样应该不符合c语言的简单美学...c语言是灵活, 简单的语言, 但是很多设计太随意, 不够严谨, 所以导致很多地方不符合逻辑, 很难理解

 

再论数组

本书和很多讲解数组和指针的文章过于复杂, 我个人觉得那是把简单的问题复杂化了.

我在上面应该已经把数组和指针的关系讲清楚了, 下面补充几点

1. 书中指出数组和指针在作为函数参数时是一样的(equivalent), 这个说法来自"C Programming Language, 2nd Ed, Kernighan & Ritchie"

如下,

char my_array[10];
char * my_ptr;
...
i = strlen( my_array );
j = strlen( my_ptr );

在c语言中, 作为函数的参数都是传值的, 但是数组却是个例外, 确实对于大数组, 传值明显是相当低效的

所以当你自己把数组作为函数参数的时候, 编译器其实是生成一个指向数组首地址的指针, 并把这个指针作为参数传入函数.

其实如果想把整个数组传值传入函数, 象前面说的, 只要把数组封装在结构中, 即可, 不过不推荐这样, 甚至在把结构体对象当作参数的时候, 要check结构体内是否有数组, 如果有数组, 最好把地址作为参数.

所以在作为函数参数时, 数组首地址会被转化为指针作为参数, 这个只是编译器出于方便统一这样处理.

在这点上说两者equivalent, 我个人觉得是不妥当的

 

2. 书中说"表达式中的数组名就是指针", 这个说法是相当危险的, 会把刚有些明白的程序员再次绕晕

int a[100];

int *p;

p = a+1;

书中的意思是, 这个操作中, 编译器出于处理的方便, 会先把a封装成一个指针, 然后赋值给p, 所以可以把a看作是指针, 这个说法很不负责

编译器可以出于处理方便的需要把a封装成指针, 但是数组名a绝对不是指针, 如果a是指针, 就应该可以作为左值进行赋值, a = p

实际上是不行的, a就是地址常量

 

3. 数组中的[]符号就代表地址偏移

在编译的时候, a[i] 会被改写成 *(a+i)

对于+是没有前后顺序的

所以你这样写, i[a], 也是可以的, 因为在编译时, 也是被改写成 *(i+a)

所以虽然看着很bt, 但是其实效果是一样的, 这就是[]的真正意义.

C语言实现“+”的重载 实现“+”的重载。(20分) 题目内容: 加法的重载,在C++中,“+”是可以被重载的,现在我们来实现这个功能。例如,如果两个数字相加,那么就给出两个数字的和,如果是字符串+字符串,那么应该输出两个字符串相连的结果。字符串和数字的区别是:数字由一连串的数字字符组成,可以转化为一定 的数值。字符串是包含了字母、数字或其他字符(不含“+”字符)组成的字母数字串。 需要指出的是:字符串和数字是不能相加的,“+”符号左右两边,只要有一个被认定为字符串,那么另外一个也是。 输入格式: 25+435 输出格式: 460(回车) 输入样例: 73b+a67 输出样例: 73ba67(回车) 立即下载

相关推荐

C语言运算符重载

C语言运算符重载

C专家编程 笔记

C语言中的符号重载C语言非常的简洁, 以至于不愿意用太多的符号, 这样有很多符号在不同的地方有不同的含义这样会让用户很困惑, 这是c的语言特性, 也是设计上的一些失误static    在函数内部,表示该变量的值在各个调用间一直保持延续性; 对于函数,表示该函数只在本文件中可见 extern 用于变量,表示该变量在其它地方定义; 用于函数定义, 表示全局可见(属于冗余的)void     用于参数列表中,表示该函数参数为空,如int main(void); 用于返回值,表示该函数返回

日知录 2848

Halcon图片拼接避坑指南:特征点匹配常见问题与解决方案

本文深入解析Halcon图片拼接中特征点匹配的常见问题与解决方案,涵盖特征点提取、匹配优化及变换模型选择等关键环节。针对工业视觉检测中的拼接错位、重影等问题,提供参数调优、图像预处理和算法选择等实战技巧,帮助开发者高效避坑并提升拼接精度。

weixin_28694169的博客 198

C 运算符重载

C++运算符重载 一、运算符重载: 1.定义:重载操作符是具有特殊函数名的函数,关键字operator后面接需要定义的操作符符号。操作符重载也是一个函数,具有返回值和形参表。它的形参数目与操作符的操作数目相同,使用运算符重载能够提高代码可读性。 运算符函数定义一般格式: <返回类型说明符> operator <运算符符号>(<参数表>) { ...

King逍灬遥 5300

【Unity】ABB CRB 15000 外部引导运动实战:从配置到实时控制

本文详细介绍了ABB CRB 15000协作机器人的外部引导运动(EGM)功能在Unity环境中的实战应用。从RobotStudio配置到Unity实时通信架构,再到EGM运动控制技巧和问题排查,全面解析如何实现机器人的高精度实时控制。通过实际案例展示EGM技术在精密装配等场景中的显著效果,帮助开发者快速掌握这一先进技术。

weixin_29057695的博客 445

SAP数据导入工具(LSMW) 超级详细教程(批量导入内部订单)

用户提了个需求,让我批量导入内部订单几百个内部订单。我本打算用写段ABAP 代码来调用 BAPI (BAPI_INTERNALORDER_CREATE)就可以了。但想到我很久没有用过LSMW 了,都快要忘记了。于是我采用LSMW的方式,完成了任务。并把过程记录下来,形成教程方便我以后回头查看,分享出来也希望对大家有帮助。

IT小职员 2万+

C语言进阶:C专家编程(读书笔记一)

C语言进阶:C语言三剑客读书笔记,希望我做一些工作能够让你花更少的时间去了解更多的知识

嵌入式小师兄的博客 807

Gazebo仿真平台模型搭建与修改

ROS里踩过的坑(一)Gazebo仿真平台模型搭建与修改文件讲解Models 文件World文件Launch文件模型编辑可视化编辑配置文件编辑模型展示如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出...

Redglass的博客 1万+

用c语言实现函数重载

一.什么是函数重载? 函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表(参数个数、类型、顺序)的函数,这组函数被称为重载函数。重载函数通常用来声明一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。 二、为什么要用函数重载 在我们之前学习的C中,我们对一个功能函数要实现不同类型的调用时,就必须得取不同的名称...

ypshowm的博客 2万+

【Matlab】matlab代码实现迭代学习控制

该文介绍了在Matlab中实现迭代学习控制(ILC)的方法。ILC通过利用历史轨迹数据来优化重复任务的控制性能。文章提供了一个步进电机控制示例,包括:1)定义正弦轨迹;2)设置100次迭代和0.1学习率;3)建立二阶系统模型;4)执行ILC算法计算控制输入;5)绘制期望轨迹与实际输出的对比图。该示例展示了ILC的基本实现过程,并指出实际应用可能需要更复杂的设计。代码演示了误差计算、控制输入更新和结果可视化等关键步骤。

unber的博客 200

C# 上位机内存泄漏排查指南:3 个工具 + 4 个技巧,从 GC 频繁到稳定运行 720 小时

C# 上位机内存泄漏排查的核心是「先定位泄漏存在,再追溯引用根源,最后修复验证通过「Visual Studio 诊断工具(快速定位)+ dotMemory(深度分析)+ PerfView(生产监控)」三个工具的配合,结合「复现场景、排查静态成员+事件、检查非托管资源、GC 日志验证」四个技巧,可高效解决绝大多数泄漏问题。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 735

【c】C语言中符号的重载

C语言中符号的重载 符号 意义 static 在函数内部,表示该变量的值在各个调用间一直保持延续性 在函数这一级,表示该函数只对本文件可见 extern 用于函数定义,表示全局可见 用于变量,表示它在其他地方定义 void 作为函数的返回值类型,表示不返回任何值 在指针声明中,表示通用的指针类型 ...

mysterious superstar 572

【反封禁策略】集成 Undetected-Chromedriver:让 Browser-Use 绕过 Cloudflare 五秒盾

摘要:AI Agent如何突破Cloudflare检测的实战指南 本文针对AI Agent在Cloudflare检测下频繁受阻的问题,深入分析了undetected-chromedriver的工作原理与局限性。文章指出,Cloudflare 2026年的检测体系已升级为"浏览器指纹+IP信誉+协议指纹+行为模式"的多维交叉验证,单一伪装方案难以奏效。 核心内容包括: undetected-chromedriver的三层补丁体系解析 v2与v3版本的适用场景对比 两种Browser-Use集成方案:Selen

ZDQ58818的博客 284

C语言拾遗(二):符号重载

C语言一个很重要的特点是简洁。但有时候它太简洁了,仅增加,修改或删除一个字符,原先的程序就可以截然不同。 简洁的C语言会让一个符号,在不同的上下文环境里表示不同的意思。这称之为符号“重载”。 比如void符号,会有以下常见的不同含义: 1>作为函数的返回类型,表示不返回任何值 2>在指针声明中,表示通用指针 3>位于函数的参数列表中,表示没有参数 再来举个乘号*的例子...

weixin_30832983的博客 164

上位机图像处理和嵌入式模块部署(qmacvisual入门)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 虽然我们前面学习了很多的知识点,比如说在windows这边,用qt写界面,用opencv写图像处理代码;在linux这边先用树莓派测试,最后再部署到自己的平台上面。但是,对于一个商业软件是怎么运行的,很多时候我们心理还是没有底,或者说,自己写的软件距离一个商业软件还有多远,大部分学习的同学是不知道的。 就算有的同学说,这个时候可以借鉴一下vision pro,或者是

嵌入式-老费,一个分享专业嵌入式知识的blog 1228

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

C语言符号重载与运算符优先级存在的问题

C语言中的符号重载 C语言运算符优先级存在的问题

海盗船长y的博客 390

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

c/c++排坑(2) -- c语言中的符号重载

所谓的符号重载就是在不同的上下文环境里有不同的意义。甚至有些关键字也被重载而具有好几种意义,这也是C语言的作用域规则对程序员不那么清晰的主要原因。 本章内容摘自《c专家编程》P37。 大家可以去我的csdn博客查看原文哦。 我的GitHub 转载于:https://www.cnblogs.com/dnhua/p/10091310.html...

weixin_30478757的博客 85

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

为何不精通C? 02 C语言中的符号重载,优先级

本文主要将两个部分,符号重载和运算优先级问题,这两个问题很常见,很繁琐。我猜绝大部分的人在学习时都选择性忽略该章节的内容。但是在面试时,却常常考这方面的东西,因为,“精通”之名。 C中,具备着符号重载,符号重载是体现在程序上下文语境中,不同符号表现出不同的含义。比如“*”,具备三个含义:声明指针,指针解引,乘号。 《C专家编程》总结了下所有的符号重载,用以理清C语言作用域规则: ...

weixin_34389926的博客 104

C语言---C语言中的符号重载

《C专家编程》符号重载 符号 意义 static 在函数内部,表示 该变量的值在各个调用间一直保持延续性 在函数这一级,表示该函数只对本文件可见 extern 用于函数定义,表示全局可见(属于冗余的) 用于变量,表示它在其他地方定义 void 作为函数的返回类型,表示不返回任何值 在指针申明中,表...

tyustli 422

C语言的符号重载

一、主要重载操作符 1、static 在函数内部,表示该变量的值在各个调用间一直保持延续性。 在函数这一级,表示该函数只对本文件可见。 2、extern 用于函数定义,表示全局可见(属于冗余)。 用于变量,表示它在其他地方定义。 3、void 作为函数的返回类型,表示不返回任何值。 在指针声明中,表示通用的类型。 位于参数列表中,表示没有参数。 4、* 乘法运算符。 用于指针,表示间接引用。 在声明中表示指针。 5、& 位的AND操作符。 取地址运算符。 6、= 赋值符 7、== 比较运算符。 8、

jackJruit的博客 435

C 语言的 符号重载

即同一个符号,在不同的上下文环境里有不同的意义。 static 在函数内部,表示变量在各个调用间保持延续性 在函数这一级,表示该函数只对本文件可见 extern 用于函数定义,表示函数全局可见(默认情况下是这种情况) 用于变量,表示变量在其他地方定义 void 用于函数返回类型,表示不返回任何值 用于指针声明,表示通用指针类型 用于参数列表,表示没有参数 ...

weixin_44135325的博客 278
上一篇: Java 正则表达式
下一篇: C语言易混淆关键词详解-const, static, extern, typedef, 声明
romaticjun2011
博客等级 码龄15年 6粉丝 24原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值