C语言指针疑问:scanf传址、赋值及字符串输入逻辑困惑
C语言指针与底层架构的对应解释
问题1:为何scanf("%d", &c)可行,c = &p却报错?
先看代码核心差异:
- 第一个片段中,
&c是取变量c的内存地址,类型为int*(指向int的指针),而scanf的%d格式符要求传入一个能存储int值的内存地址——两者类型完全匹配。 - 第二个片段中,
c是int类型变量,用于存储整数数值;&p是int*类型,存储的是变量p的内存地址。C语言的强类型系统不允许直接把地址(指针类型)赋值给普通整数变量,编译阶段就会触发类型不匹配错误。
结合CDU(控制解码单元)、MAR(内存地址寄存器)、*MDR(内存数据寄存器)*的底层逻辑:
当CPU执行scanf("%d", &c)时:
- CDU解析
scanf调用指令,识别到需要向指定内存地址写入数据。 &c的数值(即c的内存地址)被加载到MAR,MAR负责通过地址总线传递要访问的内存位置。- 用户输入的整数通过输入设备传入MDR,MDR负责通过数据总线暂存要写入内存的数据。
- CDU发出一系列微指令,控制将MDR中的数据写入MAR指向的内存单元(也就是
c对应的内存位置),完成赋值操作。
而执行c = &p时:
CDU在编译阶段(或汇编阶段)就会发现源操作数是指针类型(地址值),目标操作数是int类型(数值),两者的位宽和语义不匹配——CPU的微指令序列没有设计这种跨类型的直接赋值逻辑,因此编译直接报错,根本不会生成可执行的机器码。
问题2:为何字符数组输入无需使用&?
你的示例中char val[2]; scanf("%c", val);能正常运行,核心原因是:数组名val在作为函数参数时,会被隐式转换为指向数组首元素的指针,也就是val等价于&val[0]——本质上还是传入了内存地址,完全符合scanf对参数的要求。
底层架构层面的解释:
- 当
val作为参数传入scanf时,CDU解析到这个参数是数组名,自动将其转换为数组首元素的内存地址,加载到MAR中。 - 用户输入的字符通过输入设备传入MDR,CDU执行微指令,将MDR中的字符数据写入MAR指向的内存单元(即
val[0]的位置)。 - 如果是普通字符变量
char c;,必须用&c取地址——因为变量名c直接关联的是它存储的数值,而非内存地址;而数组名本身就是内存块的起始地址标识,无需额外取地址操作。
注意:如果是用%s读取字符串(比如scanf("%s", val);),原理完全一致——val作为首元素指针传入,scanf会把输入的字符序列依次写入从该地址开始的连续内存单元,直到遇到终止符\0。
内容的提问来源于stack exchange,提问作者Bitwoded S.Demissie
相关产品推荐
相关产品推荐

