【问题标题】:printf("%s", &b) in C prints target string. Why &b and not b?C 中的 printf("%s", &b) 打印目标字符串。为什么是 &b 而不是 b?
【发布时间】:2021-03-15 12:34:09
【问题描述】:

我将 b 定义为指向字符串的指针:

char a[] = "hello";
char *b;
strcpy(&b, a);

到目前为止一切都很好(虽然我不太明白为什么 b=&a 不起作用,考虑到 a 和 b 都是指针)。

现在当我想打印 b 指向的字符串时,我必须使用 &b:

printf("%s", &b);

为什么我需要给出一个指向 printf() 的指针的地址,以便打印该指针指向的变量的内容?为什么 printf("%s", *b) 不起作用?

将此与指向整数的指针的语法进行对比:

int c = 5;
int *d;
d = &c;
printf("%d\n", *d);

说得通。

我知道字符串语法在某种意义上是不同的,字符串名称也是一个指针。不知道我明白为什么需要这种特殊的字符串规则,但是很好,在我的头撞到墙上几次之后,我强迫学习了这一点。但是 printf("%s", &b),真的吗?

我可以将此作为“规则”来学习,但是如果有人帮助我理解它,我将不胜感激。

【问题讨论】:

  • 您的第一个 sn-p 不正确。 b 已经是一个指针。使用&b 导致指向指针的指针不是strcpy 所期望的。这将导致未定义的行为。您在printf 中使用&b 的进一步示例也是错误的,并且是第一个错误的结果。
  • 编译器认为 strcpy(&b, a); 不正确:警告 C4047:'function':'char *' 与 'char **' 的间接级别不同。 printf("%s", &b); 的类似警告,你需要 printf("%s", b) 两个函数都需要一个指针,来自 char *b;b 是。
  • @WeatherVane 的评论有点误导。 &b 也是一个指针,所以仅仅说“两个函数都需要一个指针”是奇怪的。问题是&b 是错误类型的指针。 &bchar **,strcpy 期望第一个参数的类型为 char *
  • 感谢 cmets!如果我写的是 而不是 ,编译器如何知道 b 是单个字符还是指向字符串的指针。只有在我初始化b之后它才会弄清楚吗?将 b 指向字符串 a 并使用 b 作为 printf() 的参数打印该字符串的正确代码是什么?
  • @Cyber​​Sinke 声明 char b 已经告诉编译器 b 是基本数据类型 char 的变量。不,它(编译器)会在到达char b; 行时立即计算出来。 正确代码: char *b = a;,然后是 printf("%s", b);。 :-)

标签: c string pointers syntax ampersand


【解决方案1】:

您正在调用未定义的行为。在代码中

char a[] = "hello";
char *b;
strcpy(&b, a);  /* ERROR */

您正试图将a 的内容写入很可能不足以容纳它的变量。如果 char * 是 6 个字节或更多字节,则代码将“工作”,但它根本没有按照您的想法进行。 b 应该包含 char 的地址,但它现在包含您正在解释为字符串的垃圾数据。当您编写printf("%s", b) 时,编译器会尝试将该字符串解释为地址,并且输出将是垃圾。但是当您编写printf("%s", &b) 时,您实际上是在对编译器撒谎并告诉它将b 的内容视为字符串。编译器几乎肯定会为此发出警告,如果您没有收到警告,则需要打开诊断程序。 (例如,添加-Wall -Wextra)。

你真正想要的很可能是这样的:

char a[] = "hello";
char *b = malloc(strlen(a) + 1);  /* Error check omitted */
strcpy(b, a);
printf("%s", b);

【讨论】:

    【解决方案2】:

    虽然我不太明白为什么 b=&a 不起作用,

    在这种情况下将数组的地址分配给指针是错误的,只需b = a就足够了,因为a表示数组的基地址。

    现在当我想打印 b 指向的字符串时,我必须使用 &b:

    printf("%s", &b);

    上面的用法也是错误的,char *b;是一个指针,你必须在使用指针之前分配内存,默认情况下它们不指向正确的内存位置

    strcpy(&b, a);

    即使这也是错误的,看看strcpy,它接受char*const char*作为目标和源,你传递的是char**(&b)和@987654328 @

    为什么 printf("%s", *b) 不起作用?

    *b 在这里是char,所以这可能会崩溃。

    int c = 5;
    int *d;
    d = &c;
    

    上述情况很好,因为指针变量d 的地址为c。 使用*d 而不分配任何地址是错误的。

    上面的例子中使用字符串的正确方法是:

    #include <stdio.h>
    #include <string.h>
    int main()
    {
        char str[] = "hello";
        char *pstr = str;
        printf("str = %s and pstr = %s\n", str, pstr);
        return 0;
    }
    
    

    char *p = NULL;
    p = malloc(6);
    if(p)
     strcpy(p,"hello");
    printf("p = %s\n", p);
    

    【讨论】:

      【解决方案3】:

      我喜欢这个!

      把第一个例子放在一个函数中。我敢打赌,strcpy(&amp;b, a); 之后的返回会使你崩溃,堆栈回溯将是无用的。在那个strcpy 调用中,您告诉它将字符串复制到变量b 的地址。但是b 是一个局部变量,所以在堆栈上。它的地址也将在堆栈上。通过写入超过 b 的大小(即 4 或 8 字节,取决于您运行的是 32 位还是 64 位环境),您开始覆盖堆栈的其他内容,最终破坏返回地址。当你退出你的函数时(这就是我说把它放在一个函数中的原因)CPU从堆栈中读取返回地址并跳转到它。但是你破坏了它,所以它会尝试跳转到新的位置,导致段错误或非法指令。请将“hello”替换为更长的词,例如“wellcome in hell!” (18+1 轮空),看看会发生什么。

      执行printf("%s", &amp;b) 当然可以,因为使用strcpy(&amp;b, a); 您将字符串复制到堆栈。它只会在你退出发生这一切的函数时崩溃。

      我相信现在你已经明白为什么第三段代码和第一段和第二段一样有问题了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-29
        • 1970-01-01
        • 2011-08-01
        • 2020-02-04
        • 1970-01-01
        相关资源
        最近更新 更多