0

我试图理解这两个运算符的重要性,所以我为此编写了这段代码。

#include <stdio.h>
#include <string.h>

int main()
{
    char *mnemonic, *operands;

    mnemonic = "add";
    operands = "five to two";

    analyse_inst(mnemonic, operands);

}

void analyse_inst(char mnemonic, char operands)
{
    printf("%s", mnemonic);
    printf("%s", operands);
}

但是,我注意到除非我将 function 的参数更改为 ,否则它将不起作用analyse_inst()analyse_inst(char * mnemonic, char * operands)这意味着我将传递指向该函数的指针。但为什么需要这样做?

另外,我查找了“通过引用传递”。根据tutorialspoint.com,它的定义:

将参数传递给函数的引用调用方法将参数的地址复制到形式参数中。在函数内部,地址用于访问调用中使用的实际参数。这意味着对参数所做的更改会影响传递的参数。

由此,我得到通过引用传递一个变量然后修改该值将意味着函数外部的相同变量也将被更改;而按值传递变量不会更改位于函数外部的相同变量。

我哪里出错了?

如何修改我的代码,以便通过引用传递两个变量?

(PS 我已经阅读了关于同一主题的其他 Stack Overflow 线程,但如果有人能在我编写的代码的上下文中解释它,我将不胜感激)

4

4 回答 4

2

当您编写类似的内容时char *mnemonic,意味着您正在创建一个指针变量(将保存另一个变量的地址的变量),但是由于mnemonicis的数据类型char它将char仅保存具有数据类型的变量的地址。

现在,在您编写的代码中mnemonic = "add",这里的“add”是字符串,它是字符数组,助记符指向该数组的基地址。

并且在调用函数时,您正在传递这些的引用char arrays,因此您需要更改void analyse_inst(char mnemonic, char operands)void analyse_inst(char *mnemonic, char *operands)以获取这些各自指针变量中的引用。原因是一样的我们需要指针变量来保存引用

并且&返回变量的地址,这意味着对存储变量的内存位置的引用。

希望这会有所帮助。

于 2019-02-27T12:01:35.760 回答
2

这意味着我将传递指向该函数的指针。但为什么需要这样做?

因为您在 main 中拥有的是指针,而printf("%s"期望的是char*.

“通过引用传递”是编程中的一个广义术语,意味着传递一个地址而不是对象的副本。在您的情况下,您将指针传递给每个字符串的第一个元素,而不是制作整个字符串的副本,因为这会浪费执行时间和内存。

因此,虽然字符串本身可以说是“按引用传递”,但严格来说,C 实际上只允许按值传递参数。指针本身是按值传递的。您的函数参数将是您在 main() 中分配的指针的副本。但它们指向与 main() 中的指针相同的字符串。

由此,我得到通过引用传递一个变量然后修改该值将意味着函数外部的相同变量也将被更改;

实际上,您可以通过指针从函数内部更改字符串,然后它会影响 main() 中的字符串。但是在这种情况下,您没有分配任何内存来修改 - 您将尝试修改字符串文字"...",这将是一个错误。如果要修改字符串,则应该在 main() 中将它们声明为数组:char mnemonic[] = "add";

现在事实证明,每当您在表达式中使用我示例中的数组时,它都会“衰减”为指向第一个元素的指针。所以我们实际上不能将数组按值传递给函数,因为 C 语言会在行之间将其更改为指向第一个元素的指针。

您可以使用以下代码:

#include <stdio.h>
#include <string.h>

void analyse_inst(char* mnemonic, char* operands);

int main()
{
    char mnemonic[] = "add";
    char operands[] = "five to two";

    analyse_inst(mnemonic, operands);
    printf("%s\n", mnemonic);
}

void analyse_inst(char* mnemonic, char* operands)
{
    printf("%s ", mnemonic);
    printf("%s\n", operands);

    strcpy(mnemonic, "hi");
}
于 2019-02-27T11:58:21.963 回答
1

我将在您的代码上下文中讨论一些事情,但我想先了解一些基础知识。

声明中,一元运算*符表示被声明的事物具有指针类型:

T *p;       // for any type T, p has type "pointer to T"
T *p[N];    // for any type T, p has type "N-element array of pointer to T"
T (*p)[N];  // for any type T, p has type "pointer to N-element array of T"
T *f();     // for any type T, f has type "function returning pointer to T"
T (*f)();   // for any type T, f has type "pointer to function returning T"

一元运算*符的优先级低于后缀[]下标和()函数运算符,因此如果您想要指向数组或函数的指针,则*必须与标识符显式分组。

表达式中,一元运算*取消引用指针,允许我们访问指向的对象或函数:

int x;
int *p;
p = &x;  // assign the address of x to p
*p = 10; // assigns 10 to x via p - int = int

上述代码执行后,以下情况属实:

 p == &x       // int * == int *
*p ==  x == 10 // int   == int   == int

表达式 p&x具有类型int *(指向 的指针)int,它们的值是 的(虚拟)地址x表达式 *px具有类型,它们的int值为10

一个有效的1对象指针值是通过以下三种方式之一获得的(函数指针也是一个东西,但我们不会在这里深入探讨):

  • &在左值2 ( ) 上使用一元运算符p = &x;
  • malloc()通过、calloc()或分配动态内存realloc()
  • 并且,与您的代码相关的内容是使用不带或运算符的数组表达式&sizeof

除非它是sizeofor 一元运算符的操作数&,或者是用于在声明中初始化字符数组的字符串文字,否则“N 元素数组”类型的表达式T将被转换(“衰减”)为类型的表达式“指针T”,表达式的值是数组3的第一个元素的地址。所以,如果你创建一个像

int a[10];

并将该数组表达式作为参数传递给类似的函数

foo( a );

那么在函数被调用之前,表达式a从“10-element array of int”类型转换为“pointer to int”,其值为a的地址a[0]。所以函数实际接收的是一个指针值,而不是一个数组:

void foo( int *a ) { ... }

字符串字面量如"add""five to two"是数组表达式 -"add"类型为“4 元素数组char”,"five to two"类型为“12 元素数组char”(由于字符串终止符,N 个字符的字符串需要至少 N+1 个元素来存储)。

在声明中

mnemonic = "add";
operands = "five to two";

字符串文字都不是sizeofor 一元运算符的操作数&,并且它们不用于在声明中初始化字符数组,因此两个表达式都转换为类型char *,它们的值是每个数组的第一个元素的地址。两者mnemonicoperands都被声明为char *,所以这很好。

由于 和 的类型mnemonic都是operands,所以char *当您调用

analyse_inst( mnemonic, operands );

函数形式参数的类型也必须是char *

void analyse_inst( char *mnemonic, char *operands ) 
{
  ...
}

至于“通过引用传递”位......

C按值传递所有函数参数。这意味着函数定义中的形式参数与函数调用中的实际参数在内存中是不同的对象,并且对形式参数所做的任何更改都不会反映在实际参数中。假设我们写一个swap函数为:

int swap( int a, int b )
{
  int tmp = a;
  a = b;
  b = tmp;
}

int main( void )
{
  int x = 2;
  int y = 3;

  printf( "before swap: x = %d, y = %d\n", x, y );
  swap( x, y );
  printf( "after swap: x = %d, y = %d\n", x, y );
  ...
}

如果您编译并运行此代码,您将看到 and 的值xy调用之后不会改变swap- 对 and 的更改对aandb没有影响xy因为它们是内存中的不同对象。

为了使swap函数工作,我们必须传递指向xand的指针y

void swap( int *a, int *b )
{
  int tmp = *a;
  *a = *b;
  *b = tmp;
}

int main( void )
{
  ...
  swap( &x, &y );
  ...
}

在这种情况下,表达式 and in与表达式*aand *binswap引用相同的对象,因此对xand的更改反映在and中:ymain*a*bxy

 a == &x,  b == &y
*a ==  x, *b ==  y

所以,一般来说:

void foo( T *ptr ) // for any non-array type T
{
  *ptr = new_value(); // write a new value to the object `ptr` points to
}

void bar( void )
{
  T var;
  foo( &var ); // write a new value to var
}

对于指针类型也是如此——用指针类型替换TP *我们得到以下结果:

void foo( P **ptr ) // for any non-array type T
{
  *ptr = new_value(); // write a new value to the object `ptr` points to
}

void bar( void )
{
  P *var;
  foo( &var ); // write a new value to var
}

在这种情况下,var存储一个指针值。如果我们想向varthrough写入一个新的指针值foo,那么我们仍然必须传递一个指针var作为参数。既然var有类型P *,那么表达式 &var就有类型P **


  1. 如果指针值在该对象的生命周期内指向该对象,则它是有效的。
  2. 左值是引用对象的表达式,以便可以读取或修改对象的值。
  3. 信不信由你,这条规则是有充分理由的,但这意味着在大多数情况下,数组表达式失去了它们的“数组性”,导致第一次学习该语言的人很困惑。

于 2019-02-27T18:23:57.060 回答
1

'\0'C 中的字符串存储为字符数组,以具有值(“NIL”)的字符终止。您不能直接传递数组,因此使用指向第一个字符的指针,这就是为什么必须将char *s 传递给函数才能访问字符串的原因。

字符通常比指针小得多(想想 8 位与 32/64 位),因此您不能将指针值压缩到单个字符中。

C 没有引用传递;它只是按值传递。有时,该值语言所能达到的引用一样接近(即指针),但随后该指针又按值传递。

考虑一下:

static void put_next(const char *s)
{
  putchar(*s++);
}

int main(void)
{
  const char *string = "hello";
  put_next(string);
  put_next(string);
}

这将打印hh,因为它string每次都被传递相同的值s,所以作为保存相同值副本的不同变量,在函数内部递增的事实并不重要。增加的值是函数的本地值,一旦超出范围就会被丢弃。

于 2019-02-27T11:54:28.490 回答