| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 1198 人关注过本帖, 3 人收藏
标题:关于scanf()修改常量指针指向数据值的问题
只看楼主 加入收藏
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
结帖率:100%
收藏(3)
 问题点数:0 回复次数:12 
关于scanf()修改常量指针指向数据值的问题
以下是John0826提出的一个问题:

以下是引用John0826在2014-12-30 22:50:29的发言:

楼主你好
你所说const修饰的变量运行时不能更改,我有一点不同看法。
const int num = 10;
    printf("%d\n",num);
    printf("修改num\n");
    scanf("%d",&num);
    printf("%d\n",num);
这段代码在linux(gcc编译器)和windows下c编程(cfree编译器)都能执行,并且num的值会被修改,我一直很困惑于const在c中所扮演的角色,还望解惑。


这个问题很好,值得详细解释一下。先占个位。
搜索更多相关主题的帖子: windows 编译器 linux c编程 角色 windows 编译器 linux c编程 角色 
2015-01-03 23:21
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
首先,我们解决声明为const的数据是否可修改问题。为此,写出如下代码进行测试:
图片附件: 游客没有浏览图片的权限,请 登录注册


从截图中可以看到,变量x被声明为const之后,下面试图对x赋值的行为,编译器检测出来,并予以阻止(这个时候是编译阶段,即编译就不通过了,程序更加无法运行),错误信息如图。这表明,对声明为const的数据,是在编译时期进行检查的,编译器监测和拦截所有对x试图进行修改的语句,让程序员警觉,“这意味着编程逻辑出现问题”。

授人以渔,不授人以鱼。
2015-01-03 23:32
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
然后,把程序修改,并运行,得到如下图的结果:
图片附件: 游客没有浏览图片的权限,请 登录注册


在这里,我输入了12的值给x,但打印x的结果仍然是10。这是vc2012的运行结果。显然,如果John的叙述属实,即在gcc和cfree下能得到修改的结果,那么只能说,这是编译器行为差异,或者是运行库差异。

授人以渔,不授人以鱼。
2015-01-03 23:41
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
再修改代码作如下测试:
图片附件: 游客没有浏览图片的权限,请 登录注册


这次结果有点古怪了。指针p是指向x的,由于被强制解释为指向非const型的数据,所以对指针p解引用*p出来的值就是我强制赋过去的值,但是变量x本身却仍然没有改变。这个结果很古怪,因为在理论上,*p应该就是x,但对vc2012来说,它处理的手法却是欺骗性的,你以为*p修改了数据的值,但实际上没有。这在理论上本来是对的,因为既然声明了const,就不应该千方百计去修改它,要么你修改逻辑,确认这个数据是非const的,要么你一直遵守const的约定。因此,在我看来,vc2012连*p的值也不应该改变才对,但它给出了欺骗性的假象(估计这里*p是一个临时镜像数据),不是那么好,不过与gcc真的可以修改const型数据相比,则危害性反而没有那么大。

这次我所作的修改测试,其实是C程序员绕开const声明修改const数据的传统手法,在vc6那样的编译器上也能行(估计在vc2010上也可以),不过这次测试的结果,是在vc2012上不行了,相信ms已经修补了这个缺陷。目前,我还未想到绕过的办法,但也不打算真的寻求那种方法,因为编程逻辑本来就没必要那样做。

至于John所说的方法为什么是可能的,也可以解说一番,那是基于以前能成功的经验而谈,在下面论述。

授人以渔,不授人以鱼。
2015-01-04 00:14
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
John的测试结果,正如我前面所说,一方面const限制其实只是在编译阶段,那是告诉编译器如何检查代码的,在运行阶段还是有办法的,另一方面,有传统的那种小偷小摸办法。scanf()修改,属于运行时修改,这与scanf()函数的编写有关。

scanf()函数,是通过指针传参修改数据的!比如对scanf("%d", &x)这样的函数调用,它首先分析格式字符串"%d",知道需要从输入流字符串中提取出一个可以解释为十进制整数的字符流,并把它转化为对应的数值(这个过程就是我们经常做联系的那个习题,把字符串转换为整数,或反过来),它做完这个转换之后,把结果往对应的&x地址处灌过去,从而达到赋值输入的目的。这个函数根本缺陷,是它是根据解析"%d"而认定后面对应的&x必定是指向整型数据的位置,这个时候,程序处于运行阶段,已经不是编译阶段了,而编译器编译包含scanf()语句的代码,是不作数据类型检查的(这是由于scanf()函数的编写使用了可变参数语法,凡是可变参数的部分,都取消了编译器的数据类型检查),所以就算你的x实际上的浮点数或是字符,都不相干,编译器一律放行通过,所以绝大多数scanf()造成的问题,都在运行阶段发生,初学者往往就栽在这里,满以为编译通过就没事,殊不知运行时崩溃和读到非预期数据的烦恼,更令他们摸不着头脑,以为是自己的C没学好,却不知道这是scanf()/printf()类函数把本来是强类型检查的C语言异变为弱类型检查语言的表现,一旦使用这类函数,就意味着你丧失了强类型检查的优势,冒着运行数据类型不对的风险了。

明白了上述scanf()函数的原理,自然就知道用scanf()修改const数据为什么可行了。道理很简单,对scanf()来说,别说没有const和非const的区别,实际上连数据类型的区别也没有。传统的C/C++编译器的确是这样,但我这次的测试,保守地说,至少在Windows7/8下运行vc2012编译的程序,不会再让你得逞,可能ms连DLL中的scanf()代码也修正了(估计需要与系统相关联,在内存区有标志)。

授人以渔,不授人以鱼。
2015-01-04 00:37
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
是否是因为用了带_s的安全函数导致的?
2015-01-04 08:22
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
以下是引用zklhp在2015-1-4 08:22:37的发言:

是否是因为用了带_s的安全函数导致的?

我待会试试,你也可以测试一下其他的平台。

授人以渔,不授人以鱼。
2015-01-04 13:23
zklhp
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:china
等 级:贵宾
威 望:254
帖 子:11485
专家分:33241
注 册:2007-7-10
收藏
得分:0 
以下是引用TonyDeng在2015-1-4 13:23:06的发言:


我待会试试,你也可以测试一下其他的平台。

编译器GCC 我试过带和不带_s的 在我这里都能改 但会有warnning告诉你这样会改const
2015-01-04 13:33
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
我刚试了一下,不带_s也是一样的不能改。你的环境是什么?

授人以渔,不授人以鱼。
2015-01-04 13:34
TonyDeng
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:304
帖 子:25859
专家分:48889
注 册:2011-6-22
收藏
得分:0 
是XP?

授人以渔,不授人以鱼。
2015-01-04 13:36
快速回复:关于scanf()修改常量指针指向数据值的问题
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.035294 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved