c - 指向 int == 指向 char 的指针(有点)?
问题描述
在下面给出的这段代码中,我声明了一个指向 int 的指针,我们都知道 memcpy 返回一个指向目标字符串的 void 指针,所以如果 ptr 是指向 int 的指针,那么为什么printf("%s",ptr); 是完全有效的, ptr 毕竟不是指向 char 的指针。
#include <stdio.h>
#include <string.h>
//Compiler version gcc 6.3.0
int main()
{
char a1[20] ={0} , a2[20] ={0};
int *ptr;
fgets(a1,20,stdin);
fgets(a2,20,stdin);
ptr = memcpy(a1,a2,strlen(a2)-1);
printf("%s \n",ptr);
if(ptr)
printf("%s",a1);
return 0;
}
解决方案
首先考虑ptr = memcpy(a1,a2,strlen(a2)-1);
。memcpy
被声明为void *memcpy(void * restrict, const void * restrict, size_t)
,因此它接受a1
并a2
传递给它,因为指向任何非限定对象类型的指针都可以转换为void *
或const void *
。(指向对象类型的指针const
也可以转换为const void *
。)这遵循 C 2018 6.5.2.2 7(参数转换为参数类型,就像通过赋值一样)和 6.5.16 1(一个操作数是a 可能限定void *
且左侧具有右侧的所有限定符)和 6.5.16 2(右侧操作数转换为左侧的类型)。
然后memcpy
返回 avoid *
作为它的第一个参数(在转换为 之后void *
,我们尝试将其分配给ptr
。这满足分配的约束(其中一个操作数是 a void *
),因此它将指针转换为 的类型ptr
,即int *
。这受 6.3.2.3 7 的约束:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用的类型,则行为未定义。否则,当再次转换回来时,结果将等于原始指针......</p>
由于a1
是一个char
没有要求对齐的数组,它可以有任何对齐。它可能不适合int
. 如果是这样,那么 C 标准没有根据上述定义程序的行为。
如果a1
恰好为 a 适当对齐int
或 C 实现成功转换它,我们继续printf("%s \n",ptr);
.
printf
被声明为int printf(const char * restrict, ...)
。对于与 对应的...
参数,没有要转换为的参数类型。而是执行默认参数提升。这些影响整数和float
参数,但不影响指针参数。所以ptr
被传递给printf
不变,作为一个int *
.
对于%s
转换,printf
7.21.6.1 8 中的规则说“参数应该是指向字符类型数组的初始元素的指针。” 虽然ptr
指向内存中与初始元素相同的位置,但它是指向 an 的指针int
,而不是指向初始元素的指针。因此,这是错误的论证类型。
7.21.6.1 9 说“......如果任何参数不是相应转换规范的正确类型,则行为未定义。” 因此,C 标准没有定义该程序的行为。
在许多 C 实现中,指针是内存中的简单地址,int *
并且char *
具有相同的表示形式,并且编译器将允许传递一个int *
for%s
转换。在这种情况下,printf
接收它所期望的地址并将在a1
. 这就是为什么你观察到你所做的结果。C 标准不需要这种行为。因为printf
是标准 C 库的一部分,所以 C 标准允许编译器在使用外部链接调用它时对其进行特殊处理。假设编译器可以将参数视为具有正确的类型(即使它没有)并将printf
调用更改为一个循环,ptr
就像它是一个char *
. 我不知道在这种情况下会生成不需要的代码的任何编译器,但关键是 C 标准并没有禁止它。
推荐阅读
- ios - 根据 UIView 和 UITextView 的整体大小动态调整 UIScrollView contentSize
- apache-spark - 具有 nutch 数据的本地模式下的 Apache Mahout 与 Apache Spark
- java - Spring Boot 控制器不重定向
- python-3.x - pytesseract 无法从图像中识别复杂的数学公式
- tensorflow - 在 tf.data.Dataset.map 中使用 tf.keras.applications.resnet50.preprocess_input 时出错
- python - ValueError:检查目标时出错:预期 dense_1 具有形状 (7, 7) 但得到的数组具有形状 (7, 1)
- android - Retrofit2 使用 Hashmap 发布和获取 Json 数组数据
- c# - 具有 basicHttpBinding、传输安全性和基本客户端凭据类型的自定义 WCF 凭据验证器
- ios - 实施新的谷歌纸板
- java - 在 JAVA 中查找 JSON 字符串的深度