在C语言中,“*”和“&”有什么区别?

43

我正在学习C语言,但我仍然不确定自己是否理解了&*之间的区别。

请允许我尝试解释一下:

int a; // Declares a variable
int *b; // Declares a pointer
int &c; // Not possible

a = 10;
b = &a; // b gets the address of a
*b = 20; // a now has the value 20

我得到了这些,但后来变得令人困惑。

void funct(int a) // A declaration of a function, a is declared
void funct(int *a) // a is declared as a pointer
void funct(int &a) // a now receives only pointers (address)

funct(a) // Creates a copy of a
funct(*a) // Uses a pointer, can create a pointer of a pointer in some cases
funct(&a) // Sends an address of a pointer

所以,funct(*a)funct(&a)都是正确的,对吗?有什么区别?


3
当你的问题不是特别涉及多种语言时,你不应该使用多种语言标签。你的“// 不可能”评论完全取决于语言。 - chris
1
在 C 语言中,你不能将一个变量作为引用传递。 - Jammerx2
2
void funct(int &a)在C语言中是无效的,但在C++中是有效的,就像int &c;一样;因此你的问题有些混淆。 - Clifford
2
@Clifford int &c; 不是有效的 C++ 代码。 - emlai
如果你有关于C语言的问题,请不要仅仅假设“它可能也适用于C++”,然后同时打上C++标签。如果你有关于两者差异的问题,请使用两个标签。 - stefan
显示剩余4条评论
7个回答

65

*&作为类型修饰符

  • int i声明一个整型变量。
  • int* p声明一个指向整型的pointer
  • int& r = i声明一个整型的reference,并将其初始化为引用i
    C++专用。请注意,引用必须在初始化时分配,因此不可能使用int& r;

类似地:

  • void foo(int i)声明一个函数,接受一个整数(按值传递,即作为副本)。
  • void foo(int* p)声明一个函数,接受一个指向整型的指针。
  • void foo(int& r)声明一个函数,接受一个整型的引用。(C++专用)

*&作为运算符

  • foo(i)调用foo(int)。参数作为副本传递。
  • foo(*p)对int指针p进行解引用,并使用p指向的int调用foo(int)
  • foo(&i)获取int i的地址,并使用该地址调用foo(int*)

(tl;dr) 总之,根据上下文:


13
funct(int a) 

创建 a 的一个副本。

funct(int* a) 

以 int 指针为输入,但会复制一份指针。

funct(int& a)

按引用接受一个整数,a现在是给定的完全相同的整数。不是副本。不是指针。


5

void funct(int &a) 声明了一个函数,该函数接受一个引用。引用在概念上类似于指针,因为函数可以修改传递的变量,但在语法上像值一样使用(因此您不必始终对其进行解引用才能使用它)。


5

在C语言中,只有指针而没有引用。然而,我们经常只是想访问一个值而不是复制它,传递地址而不是实际值是一个无关紧要的细节。

C++引入了引用来抽象指针的底层细节。如果你想要向函数“展示”一个值,在C++中使用引用更好。函数保证引用不为空,并且可以像访问值本身一样访问它。指针仍然是必要的,例如,您可以重新定位指针或使用指针删除delete,但是不能使用引用进行这些操作。

它们的功能有所重叠,如果没有历史背景,很容易混淆为什么我们同时拥有两者。

因此,直接回答你的问题,很多时候两者没有区别。话虽如此,如果要使函数能够检查指针是否为空,则使用f(int*)可能很有用。如果使用C,则只有指针是唯一的选择。


5
*的含义取决于上下文。当在数据或函数参数声明中时,它是一种数据类型限定符,而不是运算符。例如int*本身就是一种数据类型。因此,也许写成这样会更有用:
int* x ;

而不是:

int *x ;

它们是相同的,但第一个形式强调*是类型名称的一部分,并在使用解引用运算符时在视觉上进行区分。
当应用于实例化的指针变量时,它是解引用运算符,并产生指向的值。
在C中,&仅是运算符,它产生一个对象的地址(或指针)。它不能在声明中使用。在C++中,它是引用的类型限定符,类似于指针,但具有更严格的行为,因此通常更安全。
您在这里的评论中提出的建议:
funct(&a) // Sends an address of a pointer

这并不正确。传递的是a的地址; 如果a本身就是指针,那只会是"指针的地址"。指针就是一个地址。指向int指针地址的类型将是int **(指向指针的指针)。

也许需要解释一下指针和值变量的基础知识?指针描述了变量在内存中的位置,而描述了内存位置的内容。

  • <typename>*是指向<typename>数据类型的指针。
  • &*<value-variable>生成<variable>的地址或位置(即指向<variable>的指针),
  • **<pointer-variable>取消引用指针以获取由指针表示的地址上的值。

所以例如给定:

int a = 10 ;
int* pa = &a ; 

那么

*pa == 10

4

当你使用 func(&a) 这样的写法时,这被称为“按引用传递”,这意味着你的参数“a”可以在函数内部被修改,并且任何所做的更改都将在调用程序中可见。 如果你想要从一个函数中返回多个值,那么这是一种有用的方式。

int twoValues(int &x)
{
  int y = x * 2; 
  x = x + 10; 
  return y; 
}

现在,如果您像这样从主程序调用此函数:
 int A, B; 
 B = 5; 
 A = twoValues(B); 

这将导致:
 A holding the value 10 (which is 5 * 2) 
 and B will hold the value 15 (which is 5 + 10).

如果函数签名中没有&符号,那么你对传递给"twoValues"函数的参数所做的任何更改只在该函数内部可见,对于调用程序(例如main),它们将保持不变。
现在,当您想要传递值数组或列表时,使用指针参数调用函数是最有用的。例如:
float average ( int *list, int size_of_list) 
{
  float sum = 0; 
  for(int i = 0; i < size_of_list; i++)
   {
    sum += list[i];
    }
   return (sum/size_of_list);
}

请注意,size_of_list参数仅是您传递的数组中元素的数量(而不是以字节为单位的大小)。
希望这可以帮助您理解。

它会创建列表的副本吗? - Cristian Ceron
在C++中,默认情况下,数组按引用传递,因此对列表元素的更改实际上可以被调用线程看到。 - MaYa
1
数组不是通过引用传递的;它们会衰减为指针,然后传递指针。 - user253751

1

C++与C在许多方面都不同,其中引用是其中的一部分。

从C++的上下文来看:

void funct(int *a) // a被声明为指针 这与C中指针的使用有关..所以,您可以将此特性与C进行比较。

void funct(int &a) // 现在a只接收指针(地址) 这将导致在C++中使用引用... 您不能将其与C进行比较..

这里有一个很好的问答澄清了这两者之间的区别。 在C++中指针变量和引用变量之间有什么区别?


“a现在只接收指针(地址)”不是,a只接收整数(按引用传递)。 - emlai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接