0

给定以下头文件,如果在主体内定义了“a”,我会收到警告“未使用的变量 'a'”和链接器错误“未定义对 'a' 的引用。

标头.h:

#ifndef HEADER_H
#define HEADER_H

#include <iostream>

extern int* a;

void f()
{
    std::cout<<*a <<std::endl;
    return;
}

#endif

主.cpp:

#include "header.h"

int main()
{
    int* a = new int(10);

    f();
}

但是,如果在 main() 之外定义了“a”,则程序链接没有错误,并且 f() 按预期工作(打印 10)。为什么是这样?

例子:

int* a = new int(10);

int main()
{
    f();
}
4

6 回答 6

3
int* a = new int(10);

对于这一行,如果在 main 函数中,您正在定义一个局部变量。

所以extern int* a;只能声明一个变量,而不是定义它。然后在该符号上出现链接错误

于 2012-06-01T05:33:31.263 回答
2

当您在 main 中定义变量时,它仅在 main 函数中具有作用域。全局外部无法解决这个问题。换句话说,链接器无法将全局声明的 extern 与主函数内的变量定义相匹配。

于 2012-06-01T05:29:59.667 回答
2

如果定义ainside of main,那么它的范围(可见性)被限制为main——extern声明不会使它在其他任何地方可见。

您必须在命名空间范围内(即,在任何函数之外)定义它,才能在其他翻译单元中看到它。

于 2012-06-01T05:30:51.567 回答
2

阅读 4 个解释问题所在的答案相当烦人,但没有解释如何正确解决问题的方法。这可能是一个安全的猜测,如果 OP 不知道范围,他可能也不知道将变量传递给函数。

问题

您试图获取变量的值,但该变量在另一个函数中。我怎样才能得到它?嗯,简单的答案是,你不想得到它。你没听错。使用函数的全部原因是可重用性,如果你将新创建的函数绑定到另一个函数,那么你就不能在任何地方使用它。请记住,函数可以帮助你变得懒惰。一个好的程序员是一个懒惰的程序员。如果您可以编写一次函数并在百万个地方使用它,那么您就做对了。;)

但我仍然很想得到那个变量的值

然后您想使用函数参数将变量传递给函数。

函数被命名是因为你可以从数学的角度来思考它们。放入变量,在函数运行后取出有用的数据,并用这些变量做一些有趣的事情。所以假设你有一个数学函数y = f(x),这相当于int f(int x) { /*stuff here*/ }你在你的主函数中调用它,使用int y = f(a)wherea是一些变量或数字。

您希望避免使用全局变量,因为它们并不总是符合您的预期(特别是如果您有很多代码,很容易不小心使用相同的名称。)

在您的情况下,您希望该函数打印出特定变量的内容,所以我认为您可能正在寻找一种方法将该函数与任何特定变量一起使用。所以这就是你如何做到的。

void f(); //hi, I'm a function prototype
void f(int a); //hi, I'm a function prototype that takes a parameter
void f(int a, int b); //hi, I'm a function prototype that takes two parameters (both ints)
void f(int a, ...); //hi, I'm a function prototype that takes an int then any number of extra parameters (this is how printf works.)

所以你真正想要做的是将你的代码更改为:

标头.h:

#ifndef HEADER_H
#define HEADER_H

#include <iostream>

// extern int* a; // We don't need this

void f(int* a)
{
  if (a != NULL) //always, always check to make sure a pointer isn't null (segfaults aren't fun)
    std::cout<<*a <<std::endl;
    //return;  //Don't really need this for a function declared void.
}

#endif

主.cpp:

#include "header.h"

int main()
{
    int* a = new int(10);

    f(a);
    return 0; //main is declared as returning an int, so you should.
}

按值、指针和引用的函数

因此,在您给出的示例中,我使用了int而不是int*在您的示例中。两者的区别在于第一个是按值传递参数。另一个由指针。当您将变量传递给函数时,总是会制作它的副本。如果你传递一个int,它会复制一个int,如果你传递一个4 MB的结构,它将复制一个4MB的结构,如果你传递一个指向4MB结构的指针,它将复制一个指针(不是整个结构。)这很重要,原因有两个:

  1. 性能:制作 4MB 结构的副本需要一些时间。
  2. 更改内容的能力:如果复制指针,原始数据仍然在同一个地方,仍然可以通过指针访问。

如果你想要 1 而不是 2 怎么办?那么你可以声明指针const。原型如下所示:int f(int const* a);

如果你想要 2 而不是 1 怎么办?坚韧的饼干(无论如何都没有充分的理由。)

最后,您还可以声明一个函数来获取引用而不是指针,引用和指针之间的最大区别是引用不会为 NULL(并且您不能在引用上使用指针算术。)您会想要通常使用按引用传递或按值传递。需要通过指针传递是我几乎不需要做的事情,根据我的经验,它更像是一种特殊情况。

通过引用传递:int f(int& a);
通过 const 引用传递:int f(int const& a);

所以总结一下:

if you have function that needs parameters:
  then:
    if you do not need to modify the contents:
      then:
        if the size of the variable is small:
          pass by value: int f(int a);
        else if the size of the variable is large:
          then:
            if the value of the address can be NULL:
              pass by const pointer: int f(int const* a);
            else:
              pass by const reference: int f(int const& a);
    else if you do need to modify the contents:
      then:
        if the value of the address can be NULL:
          pass by pointer: int f(int* a);
        else:
          pass by reference: int f(int& a);

还有一些案例,但这些是主要案例,请参阅此网站了解更多详细信息。

于 2012-06-01T05:43:42.187 回答
2

您需要了解名称绑定,它决定了两个同名声明之间的关系。在函数中定义变量时,其名称没有链接;即它所指的实体不同于程序中的任何其他实体。

更一般地说:声明(在这个意义上,定义也是声明)将符号与实体相关联——对象(在这种情况下,声明声明变量)、函数、引用、类型或其他任何东西您可以在 C++ 中声明。同名的不同声明是否与同一个实体相关联是由它们的链接定义的。C++ 识别三种不同类型的链接:

  • 外部链接,其中实体可以被其他交易单元中的声明引用,

  • 内部链接,其中实体可以被同一翻译单元中的其他声明引用,

  • 并且没有链接,其中实体不能被任何其他声明引用。

在块范围内声明的变量(即局部变量)没有链接,除非它们被显式声明extern(并且声明的局部变量extern不能是定义)。因此int ain main声明(并定义)了一个独立a于程序中任何其他实体的实体。在命名空间范围内声明的变量具有外部链接,除非它们被声明static,在这种情况下它们具有内部链接;当您int a在命名空间范围内定义时,它具有外部链接,因此引用您 extern int a在标头中声明的同一实体。

于 2012-06-01T07:47:03.950 回答
1

当您在函数 main 中定义变量时,它仅在 main 范围内有效。您可以在所有函数中定义一个名为 a 的变量。但这些将是不同的变量,因为每个变量都有自己的范围。

从技术上讲,变量是在调用函数时在堆栈上分配的,因此每个实例都有自己的存储空间。

于 2012-06-01T05:30:58.067 回答