【问题标题】:Need an explanation in which occasions we should use & with printf in C需要解释在哪些情况下我们应该在 C 中将 & 与 printf 一起使用
【发布时间】:2018-08-29 17:45:55
【问题描述】:

据我所知,在 c 中使用 printf() 时,我们不使用 &.Right?但是在我的程序中,如果我不在显示功能中使用它,它会给我一个错误。有人可以解释一下吗?谢谢你

#include<stdio.h>
#define max 100
void enqueue();
char dequeue(void);
int front=-1,rear=-1,option;
char name[max][max],val;
void display(void);


void enqueue() {

    printf("Enter the name of the paitent : ");
    if(rear==max-1)
        printf("Line is full");
    else if (front==-1 && rear==-1)
        front=rear=0;
    else
        rear++;
    scanf("%s",&name[rear][rear]);

}

char dequeue(void) {
    char val;
    if(front==-1 || front >rear )
        printf("Line is empty");
    else
    {
        val=name[front];
        front++;
        if(front>rear)
            front=rear=-1;
        return val;
    }
}
void display(void) {
    int i;
    if(front==-1|| front >rear)
        printf("The queue is empty");
    else
    {
        for(i=front; i<=rear; i++) {
            printf("%s\t",&name[i][i]);
        }
    }

}



int main () {
    printf("\n\n*******Medical Cneter********");
    printf("\n\t1. Insert a paitent");
    printf("\n\t2. Remove a paitent");
    printf("\n\t3. Check Paitent list");
    printf("\n\t4. Display");
    printf("\n\t5. Exit");
    do {
        printf("\nEnter your option: ");
        scanf("%d",&option);
        switch(option) 
       {
        case 1:
            enqueue();
            break;
        case 2:
            dequeue();
            break;
        //  case 3:
        case 4:
            display();
            break;
        }

    } while(option !=5);

}

如果我不使用 & 程序会崩溃。据我所知,在 c 中使用 printf() 时我们不使用。但是在我的程序中,如果我不在显示功能中使用它,它会给我一个错误。有人可以解释一下吗?谢谢你

【问题讨论】:

  • 你不需要了解场合,只需要了解这个运算符的含义。对场合的了解将作为奖励。
  • 您应该阅读scanfprintf 的文档
  • 真正使用printfscanf 你只需要知道格式说明符想要什么数据类型。文档中对此进行了解释。 scanf 需要 &amp;printf 不需要,这不是一个简单的规则。除此之外,更一般地说,无论printfscanf,您都需要了解何时以及如何使用指针,尤其是涉及数组时。
  • 您的代码也无法针对其他一些问题进行编译。首先,消除这些错误并再次粘贴您的代码,其中仅包含您所指出的一个问题。
  • 如果您将printf("%s\t",&amp;name[i][i]); 更改为printf("%c\t",name[i][i]); 会怎样。二维数组name 的每个元素都是一个字符。所以,使用%s 说明符是错误的。

标签: c printing printf output


【解决方案1】:

为了回答您的问题,让我们回顾一下&amp; 运算符的作用,以及我们需要传递给printf 的值类型,以及为了比较,我们需要传递给scanf 的值类型.

如果你有一个东西x,那么表达式&amp;x 会给你一个指向x 的指针。如果xint,则&amp;x 给出指向int 的指针。如果xchar,则&amp;x 给出指向char 的指针。

例如,如果我写

int i;
int *ip;
ip = &i;

我已经声明了一个int 变量i 和一个指向int 变量ip。我使用&amp; 运算符创建了指向i 的指针,并将指向int 的指针存储在变量ip 中。这一切都很好。

您可能知道,当您调用scanf 时,您总是必须将指针传递给您希望scanf 为您填写的变量。你不会写

scanf*"%d %d", x, y);    /* WRONG */

因为这会将变量xy 的值传递给scanf。但是您不想将值传递scanf,您希望scanf 从用户那里读取一些值,并将它们传回给您。事实上,您希望scanf写入您的变量xy。这就是为什么您将指针传递给xy,以便scanf 可以使用指针来填充您的变量。这就是为什么你几乎总是在scanf 调用中的参数上看到&amp;

但这些原因都不适用于printf。当您调用printf 时,您确实想将普通值传递给它。您不会(通常)要求printf 将任何数据传回给您。所以大多数printf 格式说明符被定义为接受普通值,not 指向值的指针。所以这就是为什么你在printf 电话中几乎看不到&amp; 的原因。

现在,您可能会将&amp; 视为将事物“转换”为指针的运算符,但这并不是一个很好的思考方式。正如我所说,给定一个对象x,表达式&amp;x构造一个指向x的指针。它不会“转换”任何东西;它当然不会“转换” x。它构造了一个全新的指针值,指向x

在您发布的代码中,看起来您可能使用了&amp; 来尝试执行这样的“转换”。你有一个数组name,类型为array-of-array-of-char,或一个二维字符数组。你试图用%s 打印一个字符串。你知道,或者你的编译器警告你,%s 需要一个指向字符的指针,并且你知道(或者你的编译器告诉你)表达式name[i][i] 给出了一个char 类型的值。现在,将&amp; 放在name[i][i] 前面确实得到了一个指向char 类型的值,正如%s 所要求的那样,它甚至可能看起来有效,但这是一个相当随意的解决方案。

确实printf%s 需要一个指向char 的指针,但它不需要任何 指向char 的指针;它需要一个指向char 的指针,该指针指向一个有效的、以空值结尾的字符串。这就是为什么,即使%s 需要一个指针,您仍然通常不会在printf 调用中看到&amp;。您可以在单个字符变量上使用 &amp; 来获取指向该字符的指针,如下所示:

char c = 'x';
printf("%s", &c);    /* WRONG */

但这是损坏的代码,无法正常工作,因为你得到的指针不是一个有效的字符串,因为没有空终止。

在您的代码中,您可能想要更改行

printf("%s\t",&name[i][i]);

printf("%s\t",name[i]);

namechar 的二维数组,但是由于C 中的字符串是char 的数组,因此您也可以将name 视为字符串的(一维)数组,我认为这就是您尝试使用它的方式。

同样,我怀疑你想换行

scanf("%s",&name[rear][rear]);

scanf("%s", name[rear]);

但在你说“我认为scanf 总是需要&amp;!”之前,请记住,规则是scanf 需要一个指针。由于name[i] 是一个数组,当你将它传递给scanf 时(或者实际上当你在任何表达式中使用它时),你会自动获得指向它的第一个元素的指针。 (这也是printf("%s\t",name[i])背后的原因。)

如果你想使用显式的&amp;,你想要的是一个指向你想要scanf 填充的字符串数组的开始的指针,所以我想你会想要

scanf("%s", &name[rear][0]);

而不是你的表达方式。

(看起来您可能不小心将字符串沿name 数组的对角线而不是左边缘向下运行。您声明了完美的方形数组也很奇怪

char name[max][max];

即 100 个字符串,每个字符串 100 个字符。没有错,它会起作用,但它很好奇,而且它更容易混淆行和列。)

`

【讨论】:

    【解决方案2】:

    我尝试编译您的代码,但似乎出现错误:

    warning: assignment makes integer from pointer without a cast [-Wint-conversion]
     val=name[front];
        ^
    

    原因是name 是一个二维数组,而您将一个指向一维字符数组的指针分配给一个字符。

    我认为,为了完全理解这个问题,必须理解 C 是如何处理内存和指针的。

    “&”符号表示您正在获取某物的地址(不要与 c++ 引用混淆)。

    这意味着如果函数具有如下参数:

    void foo(char* bar);
    

    如果您想将变量 char var; 传递给函数,则必须使用“&”符号调用函数以明确告诉编译器您要将 var 的地址作为参数传递给函数。

    foo(&var);
    

    如果您想在函数foo 中访问var 的值,则必须“取消引用”指针,以便编译器了解您希望将值存储在刚刚传递给函数的地址中。

    为了取消引用指针,您使用“*”符号。

    char some_char_in_foo = *var;
    

    我建议阅读指针以进一步澄清问题。

    【讨论】:

      【解决方案3】:

      printfscanf 中的%s 转换说明符期望其对应的参数类型为char *,并指向字符串 中的第一个字符。

      请记住,在 C 中,字符串 是包含 0 值终止符的字符序列。字符串存储char 的数组中(或wchar_t 用于“宽”字符串)。

      您的name 数组可以存储max 字符串,每个字符串最长可达max-1 个字符,不包括字符串终止符。

      当数组表达式 不是sizeof 或一元&amp; 运算符的操作数,或者不是用于在声明中初始化字符数组的字符串文字时,表达式为将类型“char”的类型隐式转换(“decays”)为“指向char”的指针,表达式的值是第一个元素的地址。

      name 的类型为“max-max 的元素数组-char 的元素数组”。这意味着每个name[i] 的类型为“max-元素数组char”,如果它不是&amp;sizeof 的操作数,则“衰减”为char *

      这基本上是一种冗长的说法,即表达式 max[i] 等同于&amp;max[i][0]。所以你可以写你的printf 声明为

      printf( "%s\t", name[i] ); 
      

      同样,您可以将enqueue 函数中的scanf 语句重写为

      scanf( "%s", name[i] ); 
      

      虽然老实说,对于用户输入,使用fgets 更安全:

      if ( fgets( name[i], sizeof name[i], stdin ) )
        // successful input
      else
        // EOF or error on read
      

      【讨论】:

        【解决方案4】:

        &amp; 运算符的意思是“引用”。 scanf 修改其参数,因此,您需要提供对要修改的参数的引用。 printf 不会修改其参数,因此,您可以按值传递其参数,因为它可以通过副本完成其工作。

        但是,这不是最终规则,因为格式字符串%p 将打印一个指针(即引用)。所以printf 在某些情况下可能需要引用变量。

        更一般的规则是:scanf 通过引用获取参数,printf 通过值获取参数。

        【讨论】:

        • &amp; 运算符的意思是“引用” 不,&amp; 不做“引用”。 C没有“参考”。 &amp;address-of operator:“一元 & 运算符产生其操作数的地址。如果操作数的类型为 ''type'',则结果的类型为 ''pointer to type''。”某物的“地址”是指针中的内容。
        • 在 C 中,指针、引用和地址几乎是一回事。从技术上讲,您是对的,如果您为 C/C++ 制定此规则。对于初学者来说,应该够清楚了。
        • 问题是引用和指针在语法上是不同的。直接修改int &amp;x 形式的C++ 引用,如x = ...;,而必须取消引用C 或C++ 指针才能修改原始对象,如int *x 导致*x = ... 修改原始int目的。最容易对此感到困惑的是初学者。
        • 但是,问题仅限于 C,没有任何区别。
        • 除了将其称为“引用”之外,您将其称为不存在的东西 - C“引用”。至少称它为“类”会不那么令人困惑。您在精确度很重要的领域中使用了错误的术语,这个答案会让人们感到困惑。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-16
        相关资源
        最近更新 更多