【问题标题】:Why printf( "%s" , ptr ) is able to dereference a void*?为什么 printf("%s", ptr) 能够取消引用 void*?
【发布时间】:2023-04-10 17:46:01
【问题描述】:

当我们谈到解引用时,有必要在其中使用*吗?如果我们以其他方式访问指针的所指对象,是否可以认为是解引用指针,例如:

char *ptr = "abc" ;
printf( "%c" , *ptr ); // Here pointer is dereferenced.
printf( "%s" , ptr );  // What about this one?

这是我问题的第一部分。

现在如果printf( "%s" , ptr ) 是取消引用的一个例子,那么也请回答我的问题的以下部分。

K&R 说

“指向无效的指针”用于保存 任何类型的指针,但不能是 取消引用自身

因此,

char a = 'c' ;
char *p = &a ;
void *k = &a;
printf( "\n%c\n" , *p );
printf( "\n%c\n" , *k );

不编译,编译器报错

在函数'main'中:警告: 取消引用“void *”指针错误: 无效表达式的使用无效

但是如果我们使用

char *a = "c" ;
char *p = a ;
void *k = a;
printf( "\n%c\n" , *p );
printf( "\n%s\n" , k );

它编译并工作。这意味着可以取消引用 void 指针 - 我们得到了对象指针所指的对象。
如果是这样,那么上面提到的 K&R 在这种情况下是什么意思?

感谢您的宝贵时间。

【问题讨论】:

  • 我没有看到您在哪里成功取消引用 void*。请重新阅读您的代码。

标签: c pointers dereference


【解决方案1】:

没有。你所拥有的是“未定义的行为”——C 语言标准没有说明应该发生什么。在您的情况下,它“有效”,但对于另一个用户/编译器/平台,它可能不会。您的声明:

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

相当于:

int k = 42;
printf( "\n%s\n" , k );

同样是未定义的。

【讨论】:

  • 我明白你的意思。谢谢。问题的第一部分呢? printf( "%s" , ptr ) 是否也被视为指针的解引用?
  • @andrew printf() 肯定必须取消对指针的引用才能为“%s”格式化程序执行其操作。 OTOH,如果您在同一个指针上使用了“%p”格式化程序,则不需要遵守。
  • 我不知道标准如何定义printf 或者我是否遗漏了什么,但可以想象void* 被转换为char*,因为printf 可以通过%s 推断它是一个字符串。再说一次,问题可能是可变参数问题。我不想深入研究它。
  • @ThomasEding:(是的,我迟了几年才回复。)没有 cast (这是一个指定转换的显式运算符)。该语言保证void*char* 具有相同的表示。它不能保证它们以相同的方式传递给可变参数函数,但很有可能它们是。行为未定义。 printf 假设参数的类型是char*;既然不是,调用者对它撒了谎,它的行为是未定义的..
【解决方案2】:

在给出的例子中:

char *a = "c";
char *p = a;
void *k = a;
printf( "\n%c\n" , *p );
printf( "\n%s\n" , k );

发生了什么,在第一个 printf 中,值 'c' 是通过取消引用 char 指针传入的,printf 只知道你给了它一个 char 因为%c 格式标签。

现在,void* 只是一个具有未知类型的原始指针,您无法取消引用它,因为编译器不知道要从中读取什么类型,除非您将其转换为其他类型。

在第二个 printf 中,void* 指针被传入。printf 只是将其视为一个普通数字,并且在读取给定的格式标记之前不知道它是什么。通过使用%s,您是在告诉 printf 函数您实际上传入了char*,因此它会相应地对其进行强制转换并将其作为字符串正确读取,即它取消引用char* 指针——而不是void*指针。

只要void* 指针指向一个以空结尾的char 数组,它就是有效的代码。如果 void* 指针指向不同的东西,printf 函数仍然会很高兴地尝试将其读取为 char* 指针(如果您指定 %s)并导致未定义的行为。

另一个坏例子:

char *a = "ce";
int b = (int)a;
printf( "\n%s\n" , b );

你也不能取消引用一个整数,但我告诉printf它是一个char*,所以它可以工作。

【讨论】:

    猜你喜欢
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    • 2019-12-12
    • 1970-01-01
    • 1970-01-01
    • 2013-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多