C语言关于指针赋值的问题

谁之朱斯提提亚 2012-06-19 04:43:45
先看一段代码:
#include<stdio.h>
void main()
{
int*p=10;
printf("%d",p);
}

看看上述代码有什么问题没有?相信清楚指针概念的各位知道,int*p其实划分来看是(int*)p,他其实是一个指针,那么int*p=10;等价于int*p;p=10;,大家都知道,指针就是地址,前面语句的意思是,把常量10的值赋给指针p,按照定义来说,这是不合法的,因为常量不能直接赋值给指针,比如int a=10;int*p=&a;这才是合法的。但是我通过VC6.0编译器编译一下,文件名为al.C(注意了,非CPP后缀),结果编译器毫不报错;输出结果为10。

把代码换一种形式:
#include<stdio.h>
void main()
{
int*p;
p=10;
printf("%d",p);
编译器通过,结果依旧是输出10;

再换个输出:
#include<stdio.h>
void main()
{
int*p;//上面验证了2种情况一样,这里就不验证2次了
p=10;
printf("%d",*p);
}
编译器依旧不报错,只是输出为空,什么都不输出

这完全颠覆了我常量不能直接赋值给地址的概念,我现在有点晕,有没有知道这是为什么的,给我个合理的理解方法,让我理解为什么会是这样,这样到底合不合法,为什么概念说他不合法,常量赋值给指针,计算机是如何完成操作的?为什么P输出的是10?这不科学啊,谁来解释下。
...全文
980 37 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
37 条回复
切换为时间正序
请发表友善的回复…
发表回复
Ever_lover 2012-06-23
  • 打赏
  • 举报
回复
运气真好。。竟然没报错
A_Zhao 2012-06-23
  • 打赏
  • 举报
回复
最后,来解释一下,在楼主您的例子中,为什么会出现“输出为10”这个“奇怪的现象”。

为了解释清楚这个问题,我们把我上面所举例子的代码延伸一下:


#include<stdio.h>

int main(void) {

int *p;

p = (int *) 10;

printf("%d\n", p);

p = (int *) 'a';

printf("%c\n", p);

p = (int *) "I Love C/C++! Oh, Yeah!";

printf("%s\n", p);

return 0;

}

上面的代码,运行结果为:

10
a
I Love C/C++! Oh, Yeah!

怎么样,觉得奇怪吗?

只要我们在 printf函数的第一个参数中,设定了“恰当的”format,程序就会符合我们“奸诈”的预期 —— 我们将p这个指向整数类型数据的指针(这一点自始至终都不会有变化),分别用“%d”、“%c”和“%s”这些format表示出来。而这些format又跟我们对p赋值时的那个“(int *)”右边黏着的“坏东东”的“看上去的样子”相吻合,那么,原来这些“坏东东”看上去是啥样的,现在也原样地被反馈回来。

然而,正如我在上面所强调的那样,p自始至终都是指向整数类型数据的指针,那么,它其实并不是printf函数在被设定了 “%d”、“%c”和“%s”这些format参数之后,所期盼的东东。但那又怎么办呢?我们既然这样写了,老Boss看到了之后,非常生气又无奈地把p的值(并不是p本身)分别强制转换为整数类型、字符类型、指向字符类型数据的指针。在这个过程中,老Boss连骂了三次,您在Warnings中可以看到。

请特别注意,在

printf("%s\n", p);

中,老Boss并不是把p的值,强制转换为所谓的“字符串类型”(根本没有这种类型)。但是,为什么还是达到了我们“奸诈的预期”的结果呢?

当printf函数的第一个参数,被设定为“%s”这个format之后,它所真正期盼的,是一个指向字符类型数据的指针。程序从这个指针所指向的存储器位置开始,连续地读取数据,直到一个被约定为字符串结束标志的数据(\0)为止。

为了使我们奸诈的手段,100%地合法 —— 如同洗钱 —— 我们应当这样写:


#include<stdio.h>

int main(void) {

int *p;

p = (int *) 10;

printf("%d\n", (int)p);

p = (int *) 'a';

printf("%c\n", (char)p);

p = (int *) "I Love C/C++! Oh, Yeah!";

printf("%s\n", (char*)p);

return 0;

}

这麽一来,老Boss就被蒙在鼓里,为我们老老实实地“洗钱”,再也不会骂人了。

之前我一再强调, p自始至终都是指向整数类型数据的指针,那么,在printf函数中,为了“忠实地”反馈出p原本应有的面貌,我们应当选用“%p”这个format。代码如下:


#include<stdio.h>

int main(void) {

int *p;

p = (int *) 10;

printf("%p\n", p);

p = (int *) 'a';

printf("%p\n", p);

p = (int *) "I Love C/C++! Oh, Yeah!";

printf("%p\n", p);

return 0;

}


运行一下,结果是:

0xa
0x61
0x8048508

看到这几行结果,楼主您应当有所感悟吧!

A_Zhao 2012-06-23
  • 打赏
  • 举报
回复
看了楼主您的帖子,实在令人担忧呀……

首先,您说“按照定义来说,这是不合法的,因为常量不能直接赋值给指针”。

哪里有这个“定义”呀?

我慢慢说……到后来,您就会知道,您的这种“定义”是毫无意义的。

先说啥是指针。

您说“大家都知道,指针就是地址”。这种说法是错误的(且害人的)。用这种思路去理解指针,那说明您还没有体会到,关于指针的许多真实情况。

“地址”这个观念,是为了让那些需要向存储器中的某个或某些存储单元进行数据存取的主体(比如处理器)能够找到这些存储单元,而引入的。

显然地,在这些主体看来,那些存储单元的位置(即地址),也是数据。那么,这后一种数据,也要在存储器中被存储、被读写。(从C语言编程语境来看, 这后一种数据的符号,就是指针变量或指针常量的符号。)

而“指针”这个观念的引入,与“地址”的比起来,要复杂一些,或者说,前者的用途与意义更具多样性:

(1)指针变量或指针常量的值,往往可以由一个取地址符(&)作用在一个变量或常量的符号上而获得。

如果您要说“指针的值,不能取常量的地址”的话,那您又错了。如下写法,就可以令指针取到常量的地址:

int const a=12345;
int const *pa=&a;


int const a=12345;
int const *pa;
pa=&a;

从这个角度看来,指针的用途和意义在于:获取程序中变量或常量符号实际对应于存储器的数据的位置。

那么,对于同一个指针量,可以随程序员的意愿,在任何时候,获取任何既有的符号所对应的数据的位置,作为它的值 —— 不过,这里有一个极不可忽略的条件,下面会讲。

地址,则没有上述的意义和用途。一个符号所对应的数据,在存储器中的位置,在符号被声明的当初,就任由老Boss来分配。这个分配过程,对于程序员来说,是透明的 —— 这是高级语言与低级语言之间的一个显著区分。但由于C语言里存在着“指针”这个机制,就使得它“高级得不那么彻底” —— 因为,程序员可以透过指针,来窥探到老Boss和他的存储器情人是怎么约会的。

(2)指针量的值,除了由上面第(1)点中所说的方式获得之外,还可以由第(1)点中的方式所获得的量,再加加减减,即进行所谓“指针运算”来被赋予。让函数返回一个“实用的”存储器中的位置值,通常就是属于这一类。

(3)在第(1)点的例子里,我们已经看到:在声明指针量符号的时候,必须必须同时给出某种数据类型。这个数据类型必须必须,跟这个指针将来要指向的符号在被声明时所设置的数据类型,完全一致!

如果差那么一点儿,但尚在老Boss的理解范围之内的话,那么,老Boss会骂一句(吐出一个Warning),然后他会心不服但手服地,为指针量赋值等号右边的东西,做一些强制的转换。

但是如果差得比较离谱了,老Boss就索性罢工了。

在楼主您的例子中:

int*p=10;

就是属于前一种的“差那么一点儿”的情况。这时候,虽然老Boss没有罢工(没有编译error),但是您不知道,他已经有一些怨气地在暗地里,为您做了一些事情。如果您打开Warning选项,就能听到他的骂声。(千万不要以为“编译通过,程序就是写得100%合乎标准的”!)

如果您把上面的代码,改写成这样:

int *p=(int *)10;

那么,保证老Boss又不骂您、更不会罢工。

上面这样写是什么意思呢?原来您的写法,是把一个整数10赋予了指针量p。我们姑且认为这个10就是整数常量。其实,不管这个10是个啥量啥类型,只要前面顶上一个“(int *)”,它就会被老Boss强制转换为:符号p在声明时所设置的那个类型(即指向整数类型变量的指针)。

我为什么说“不管这个10是个啥量啥类型”呢?

您看:

int *p=(int *)'a';

就是如此,老Boss也不会骂人或罢工。

这里,“a”被括在单引号里,表达了一个字符常量,它依然可以100%合法地被赋予指针量p。

您就是写成:

int *p=(int *)"I Love C/C++! Oh, Yeah!";

—— 这也是100%合法的!为什么?留给楼主您自己思考。

这一切跟“变量还是常量”没有任何关系。起决定性因素的,是类型(指针所指向的数据的类型)!

所以,您所说的“按照定义来说……常量不能直接赋值给指针”,是完全没有意义的。

数据的类型,在C语言里,是如此的重要,以至于忽略了它,整个C语言的合理性和逻辑性,就会完全丧失。

“类型”这个机制,是程序员大脑中的数据(逻辑的),与计算机内部的数据之间(物理的),两者之所以能够沟通的最重要媒介。一旦忽视了它,两头之间,就只剩下混乱了。

顺便说一句,这又体现了:“地址”与“指针”的不同。前者没有“类型”属性,而后者,“类型”是它的内秉属性(与生俱来的、不可或缺的)。

以上又“扯”了一通,仅供参考!

有什么错误,希望各位大虾拍砖指正!谢谢!



laizhiping_rj 2012-06-23
  • 打赏
  • 举报
回复

#include<stdio.h>

int main()
{
int *p;
*p = 10; // 赋值
printf("%p\n",p); // 输出p地址
printf("%d\n",*p); // 输出p值
p = (int *)10; // 赋地址 p = 10
printf("%p\n",p); // 输出p地址
printf("%d\n",*p); // 输出p值
return 0;
}
左眼看到鬼 2012-06-21
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

形如int* p = 10;这样的赋值时相当危险的。因为:
1. 你无法知道地址为10的内存是否可以访问(操作系统会保留一些地址的)
2. 你无法知道,地址为10的内存是否正在被其它的变量或者对象使用,如果贸然修改其中的值,就可能导致其它程序异常。

一句话,尽管语法和语义上那样写都是正确的,但实践中一定要杜绝这么做。
[/Quote]
对的。
想问下楼主,你的的编译器在毫不报错的情况下,连个warning也没有吗?
wangyunbo158 2012-06-21
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

形如int* p = 10;这样的赋值时相当危险的。因为:
1. 你无法知道地址为10的内存是否可以访问(操作系统会保留一些地址的)
2. 你无法知道,地址为10的内存是否正在被其它的变量或者对象使用,如果贸然修改其中的值,就可能导致其它程序异常。

一句话,尽管语法和语义上那样写都是正确的,但实践中一定要杜绝这么做。
[/Quote]
谨慎使用指针
typedef_god 2012-06-21
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

形如int* p = 10;这样的赋值时相当危险的。因为:
1. 你无法知道地址为10的内存是否可以访问(操作系统会保留一些地址的)
2. 你无法知道,地址为10的内存是否正在被其它的变量或者对象使用,如果贸然修改其中的值,就可能导致其它程序异常。

一句话,尽管语法和语义上那样写都是正确的,但实践中一定要杜绝这么做。
[/Quote]
++
补句不好听的,楼主根本就没掌握指针。
还是花时间看看书吧。
jody_go 2012-06-21
  • 打赏
  • 举报
回复 1
int *p = 10;
int *p;
p = 10;
printf("%d\n", p);
这是直接打印p的地址,因为你给p的数据为10,所以当然是打印10,


printf("%d\n", *p);
这是取获取地址为10的内存地址的数据,然而这个数据可能是被保护的,
所以去获取是会报错的。
xiebin133 2012-06-21
  • 打赏
  • 举报
回复
void main()问题是没有,第一,你不知道程序的返回值,第二,你不能给程序传参。谭教授的书就写成void main(),不过那仅仅只能用来教新手而已,用过几年C的人是不会去看他的书的。一般的写法是int main(int argc, char *argv[])。
酱油党 2012-06-21
  • 打赏
  • 举报
回复
还有啊,指针可不是地址 不解释 , 坐等啊!!
酱油党 2012-06-21
  • 打赏
  • 举报
回复
好吧,void main()
坐等啊,顺便说C++标准
各大厂商支持情况!~
瓜子+板凳!~
N0bug 2012-06-21
  • 打赏
  • 举报
回复
[Quote=引用 26 楼 的回复:]
引用 24 楼 的回复:
引用楼主 的回复:
先看一段代码:
#include<stdio.h>
void main()
{
int*p=10;
printf("%d",p);
}

看看上述代码有什么问题没有?相信清楚指针概念的各位知道,int*p其实划分来看是(int*)p,他其实是一个指针,那么int*p=10;等价于int*p;p=10;,大家都知道,指针就是地址,前面……
[/Quote]坐等人喷你的void main()
wgggkgd 2012-06-21
  • 打赏
  • 举报
回复
0x0000000a内存的值是什么,竟然不报错?
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]
引用楼主 的回复:
先看一段代码:
#include<stdio.h>
void main()
{
int*p=10;
printf("%d",p);
}

看看上述代码有什么问题没有?相信清楚指针概念的各位知道,int*p其实划分来看是(int*)p,他其实是一个指针,那么int*p=10;等价于int*p;p=10;,大家都知道,指针就是地址,前面语句的意思是,把常量10的值……
[/Quote]
还有我们老师比你牛逼多了,别在这装了,跟他比,你算什么,笑,可惜他已经不教我们了
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]
引用楼主 的回复:
先看一段代码:
#include<stdio.h>
void main()
{
int*p=10;
printf("%d",p);
}

看看上述代码有什么问题没有?相信清楚指针概念的各位知道,int*p其实划分来看是(int*)p,他其实是一个指针,那么int*p=10;等价于int*p;p=10;,大家都知道,指针就是地址,前面语句的意思是,把常量10的值……
[/Quote]
还有,当然知道指针和指针指向的值的区别了,你的理解能力是不是有点那个了,还有我程序那有问题?请指教。
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]
引用楼主 的回复:
先看一段代码:
#include<stdio.h>
void main()
{
int*p=10;
printf("%d",p);
}

看看上述代码有什么问题没有?相信清楚指针概念的各位知道,int*p其实划分来看是(int*)p,他其实是一个指针,那么int*p=10;等价于int*p;p=10;,大家都知道,指针就是地址,前面语句的意思是,把常量10的值……
[/Quote]

你的脑子进水了,还是怎么滴,void main()有什么问题吗?请指教!要是说不出,别在这论坛出现了,丢人!还是,指针即地址,看看楼下怎么说,再翻翻各种C语言书籍,我相信你看了之后,你就不来这论坛了,说明脸皮够厚
sunfen0307 2012-06-21
  • 打赏
  • 举报
回复
[Quote=引用 24 楼 的回复:]
引用楼主 的回复:
先看一段代码:
#include<stdio.h>
void main()
{
int*p=10;
printf("%d",p);
}

看看上述代码有什么问题没有?相信清楚指针概念的各位知道,int*p其实划分来看是(int*)p,他其实是一个指针,那么int*p=10;等价于int*p;p=10;,大家都知道,指针就是地址,前面语句的意思是,把常量10的值……
[/Quote]大家都知道,指针就是地址........此时指针的值就是地址的值。。。
yangkai7758 2012-06-21
  • 打赏
  • 举报
回复
谁说指针不能赋常量?
指针值就是地址,一个常量当然可以是一个地址,一个地址也可以是一个常量

printf是个变参,它只是按照你指定参数来解释输入而已,你写%d, 它就当p是个整形,输出它的值,当然是10……



ForestDB 2012-06-21
  • 打赏
  • 举报
回复
弄清指针和指针所指的对象。
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]
第一个输出的是内存p的地址,第二个输出内存p的数据,为10,第三个输出的是地址10指向的内存块,为0
[/Quote]
我要疯了,我问的不是这个吗,难道我不知道输出的是什么吗.....
加载更多回复(16)
1. C 语言中的指针和内存泄漏 5 2. C语言难点分析整理 10 3. C语言难点 18 4. C/C++实现冒泡排序算法 32 5. C++中指针和引用的区别 35 6. const char*, char const*, char*const的区别 36 7. C中可变参数函数实现 38 8. C程序内存中组成部分 41 9. C编程拾粹 42 10. C语言中实现数组的动态增长 44 11. C语言中的位运算 46 12. 浮点数的存储格式: 50 13. 位域 58 14. C语言函数二维数组传递方法 64 15. C语言复杂表达式的执行步骤 66 16. C语言字符串函数大全 68 17. C语言宏定义技巧 89 18. C语言实现动态数组 100 19. C语言笔试-运算符和表达式 104 20. C语言编程准则之稳定篇 107 21. C语言编程常见问题分析 108 22. C语言编程易犯毛病集合 112 23. C语言缺陷与陷阱(笔记) 119 24. C语言防止缓冲区溢出方法 126 25. C语言高效编程秘籍 128 26. C运算符优先级口诀 133 27. do/while(0)的妙用 134 28. exit()和return()的区别 140 29. exit子程序终止函数与return的差别 141 30. extern与static存储空间矛盾 145 31. PC-Lint与C\C++代码质量 147 32. spirntf函数使用大全 158 33. 二叉树的数据结构 167 34. 位运算应用口诀和实例 170 35. 内存对齐与ANSI C中struct内存布局 173 36. 冒泡和选择排序实现 180 37. 函数指针数组与返回数组指针的函数 186 38. 右左法则- 复杂指针解析 189 39. 回车和换行的区别 192 40. 堆和堆栈的区别 194 41. 堆和堆栈的区别 198 42. 如何写出专业的C头文件 202 43. 打造最快的Hash表 207 44. 指针与数组学习笔记 222 45. 数组不是指针 224 46. 标准C中字符串分割的方法 228 47. 汉诺塔源码 231 48. 洗牌算法 234 49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. 连连看AI算法 274 56. 连连看寻路算法的思路 283 57. 重新认识:指向函数的指针 288 58. 链表的源码 291 59. 高质量的子程序 295 60. 高级C语言程序员测试必过的十六道最佳题目+答案详解 297 61. C语言常见错误 320 62. 超强的指针学习笔记 325 63. 程序员之路──关于代码风格 343 64. 指针、结构体、联合体的安全规范 346 65. C指针讲解 352 66. 关于指向指针指针 368 67. C/C++ 误区一:void main() 373 68. C/C++ 误区二:fflush(stdin) 376 69. C/C++ 误区三:强制转换 malloc() 的返回值 380 70. C/C++ 误区四:char c = getchar(); 381 71. C/C++ 误区五:检查 new 的返回值 383 72. C 是 C++ 的子集吗? 384 73. C和C++的区别是什么? 387 74. 无条件循环 388 75. 产生随机数的方法 389 76. 顺序表及其操作 390 77. 单链表的实现及其操作 391 78. 双向链表 395 79. 程序员数据结构笔记 399 80. Hashtable和HashMap的区别 408 81. hash 表学习笔记 410 82. C程序设计常用算法源代码 412 83. C语言有头结点链表的经典实现 419 84. C语言惠通面试题 428 85. C语言常用宏定义 450

70,023

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧