0

Given the following header file, if 'a' is defined inside the main body, I get a warning "unused variable 'a'" and linker error "undefined reference to 'a'.

header.h:

#ifndef HEADER_H
#define HEADER_H

#include <iostream>

extern int* a;

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

#endif

main.cpp:

#include "header.h"

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

    f();
}

However, if 'a' is defined outside of main(), the program links with no errors and f() works as expected (prints 10). Why is this?

Example:

int* a = new int(10);

int main()
{
    f();
}
leppie
  • 115,091
  • 17
  • 196
  • 297
concrete_d
  • 370
  • 1
  • 13
  • 4
    You need to learn a thing or two about [variable scoping.](http://en.wikipedia.org/wiki/Scope_%28computer_science%29) This is perhaps one of the most important concepts in programming. – OmnipotentEntity Jun 01 '12 at 05:30
  • if you define `a` inside the `main` it is local to `main`'s scope ! – shan Jun 01 '12 at 05:30
  • As in real life, a name is just a name and can refer to different things in different situations - one person named "John Smith" is not necessarily the same person as another "John Smith". – molbdnilo Jun 01 '12 at 07:13
  • 1
    @OmnipotentEntity Scoping is only indirectly related to his problem. The issue is linkage. (Scoping would be completely irrelevant, except that the default linkage of a variable depends on scope.) – James Kanze Jun 01 '12 at 07:48
  • @JamesKanze, you sure? because it looks like his issue is he doesn't know how to structure programs and doesn't know how to pass variables to functions, or how scoping works. He probably used `extern int* a;` after googling his compiler error and found something that said he should use extern to reference variables in other files, but didn't really understand what's going on, and then finally gave up because he didn't know what to search for. The reason why `a` is declared as a pointer is probably also related to this. It's tough when you're starting out to know which questions to ask. – OmnipotentEntity Jun 01 '12 at 07:51
  • @OmnipotentEntity Given the question, it's probable that there's a lot of things he doesn't understand involving scope, linkage and probably lifetime as well. They are, however, somewhat distinct concepts, and the immediate issue here is linkage; the `a` in the local variable doesn't refer to the same entity as the `a` in the `extern` declaration. – James Kanze Jun 01 '12 at 10:00
  • Thanks all for your answers. I was working out of a book that used a similar structure for a program example, and I got lost in the sample code - it turned out the global definition of the extern variable in another file, e.g. in the namespace scope there is `int* a;`, and in `main` `a = new int(10);`. I didn't notice the global definition so I tried to "fix" it by adding `int*` before `a` thinking that would work correctly. Still a rank beginner, so hope I am using the proper terminology for everything. – concrete_d Jun 02 '12 at 02:28

6 Answers6

3
int* a = new int(10);

for this line, if in the main function, you are defining a local variable.

so the extern int* a; only declare a variable, but not define it. then get a linkage error on that symbol

RolandXu
  • 3,566
  • 2
  • 17
  • 23
2

When you define the variables inside main, it only has scope inside the main function. The global extern cannot resolve to that. In other words the linker cannot match the extern declared globally to the variable definition inside the main function.

Superman
  • 3,027
  • 1
  • 15
  • 10
2

If you define a inside of main, then its scope (visibility) is restricted to main -- an extern declaration will not make it visible anywhere else.

You have to define it at namespace scope (i.e., outside any function) for it to be visible in other translation units.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
2

It's rather annoying to read 4 answers explaining what's wrong, but none explaining how the correct way to fix it. It's probably a safe guess that if the OP doesn't know about scoping he probably also doesn't know about passing variables to a function.

The Problem

You're trying to get at the value of a variable, but the variable is in another function. How can I get at it? Well, the simple answer is, you don't WANT to get at it. You heard me right. The entire reason to use a function is reusability, if you tie your newly created function to another function then you can't use it everywhere. Remember, functions help you be lazy. And a good programmer is a lazy programmer. If you can write a function once and use it in a million places, you're doing it right. ;)

But I still really want to get at the value of that variable

Then you want to use a function parameter to pass the variable to the function.

Functions are named because you can think of them in terms of math. Put variables in, get out useful data after the function has run and done interesting things with those variables. So let's say you have a math function y = f(x), the equivalent of this would be int f(int x) { /*stuff here*/ } then you call it in your main function using int y = f(a) where a is some variable or number.

You want to avoid global variables because they don't always do what you expect (especially if you have a lot of code, it's very easy to accidentally use the same name.)

In your case you want the function to print out the contents of a specific variable, so I think perhaps you're seeking a way use that function with any specific variable. So here's how you do that.

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.)

So what you really want to do is change your code to something like:

header.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

main.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.
}

Functions by value, pointer and reference

So, in your examples I gave I used int rather than int* in your example. The difference between the two is the first one passes the parameter by value. The other by pointer. When you pass a variable to a function a copy of it is ALWAYS made. If you pass it a int, it makes a copy of the int, if you pass it a 4 MB structure it will make a copy of the 4MB structure, if you pass it a pointer to a 4MB structure is will make a copy of the pointer (not the entire structure.) This is important for two reasons:

  1. Performance: Making a copy of a 4MB structure takes some time.
  2. Ability to change contents: If you make a copy of the pointer, the original data is still in the same place and still accessible through the pointer.

What if you want 1 and not 2? Well then you can declare the pointer const. The prototype looks like this: int f(int const* a);

What if you want 2 and not 1? Tough cookies (there no good reason anyway.)

Finally, you can also declare a function to take a reference and not a pointer, the big difference between a reference and a pointer is a reference will not be NULL (and you can't use pointer arithmetic on a reference.) You will want to use either pass by reference or pass by value normally. Needing to pass by pointer is something that I almost never need to do, in my experience it's more of a special case sort of thing.

Pass by reference: int f(int& a);
Pass by const reference: int f(int const& a);

So to sum up:

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);

There's some more cases, but these are the main ones, see this website for more details.

OmnipotentEntity
  • 16,531
  • 6
  • 62
  • 96
  • Thanks for the detailed answer. I am (somewhat) familiar with the concepts you outlined here; appreciate you taking the time to go over best practices. My code fragment was meant to imitate a more complex program where it seems like the global variable is needed (I don't know enough to make a real judgment on that). – concrete_d Jun 02 '12 at 02:37
  • The larger a program is, the less you need global variables. I've never needed a global variable in any program I've ever written. I've thought I did when I was starting out, though. The larger your program is the more you should avoid global variables like a plague. They're not thread-safe, they're unstructured, it's hard to debug around them because anything can change them. If you think you need a global variable then take a step back, there is likely to be a structured solution that addresses your problem much better. – OmnipotentEntity Jun 02 '12 at 02:56
2

You need to learn about name binding, which determines how two declarations of the same name are related. When you define a variable within a function, its name has no linkage; i.e. the entity it refers to is distinct from any other entity in the program.

More generally: a declaration (and in this sense, a definition is also a declaration) associates a symbol with an entity—an object (in which case, the declaration declares a variable), a function, a reference, a type or anything else you can declare in C++. Whether different declarations of the same name associate with the same entity or not is defined by their linkage. C++ recognizes three different types of linkage:

  • external linkage, in which the entity can be referred to by declarations in other transation units,

  • internal linkage, in which the entity can be referred to by other declarations in the same translation unit,

  • and no linkage, in which the entity cannot be referred to by any other declaration.

Variables declared at block scope (i.e. local variables) have no linkage, unless they are explicitly declared extern (and a local variable declared extern cannot be a definition). So the int a in main declares (and defines) an entity which is independent of any other a in the program. Variables declared in namespace scope have external linkage, unless they are declared static, in which case they have internal linkage; when you define int a at namespace scope, it has external linkage, and so refers to the same entity you declared with extern int a in the header.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

When you define the variable in the function main it is valid only in the scope of main. You can define in all function a variable with the name a. But these would be different variables, because each has it's owen scope.

Technically the variable is allocated on the stack while the function is called, therefore each instance has its own storage.

harper
  • 13,345
  • 8
  • 56
  • 105