2013-11-15 3 views
3

Учитывая следующую настройку, я столкнулся с довольно странными явлениями, которые я не могу объяснить. Используя Visual Studio 2005, следующий фрагмент кода приводит к сбою. Я хотел бы знать причину.На примере глобальных встроенных функций в C++

playground.cpp

static int local=-1;  
#include "common.h" 

int main(int arg) 
{ 

    setit();  
    docastorUpdate(); 

    return 0; 
} 

common.h

#include <stdio.h> 
#include <iostream> 

void docastorUpdate(); 

static int *gemini; 

inline void setit() 
{ 
    gemini = &local; 
} 

castor.cpp

static int local = 2; 

#include "common.h" 

void docastorUpdate() { 
    setit(); 

    // crashing here, dereferencing a null pointer 
    std::cout << "castor:" << *gemini << std::endl; 
} 

Дело в том, что е е авария исчезает, когда

  1. Я двигаюсь рядная функция Сетит() неназванные имена
  2. Я делаю это статическим

Выражаясь в двух словах, мне нужна помощь, чтобы понять причины , Любое предложение приветствуется! (Я знаю, что это решение не является одним из лучших partices, просто любопытство.)

+4

Почему странность со всей статикой? –

+0

static in C++ - плохая идея для глобальных переменных (скорее, использование extern): http://stackoverflow.com/questions/15235526/the-static-keyword-and-its-various-uses-in-c – Necrolis

+0

Странно что без статичного сбоя ... – Klaymen

ответ

5

Ваш код вызывает неопределенное поведение , очень тонким способом.

[C++11: 7.1.2/4]: Встроенная функция должна быть определена в каждой единицы перевода, в которой она используется ODR, и должна иметь точно такое же определение в каждом случае. [..]

Хотя определение выглядит так же, потому что это лексический копия вставила в каждый ЕП вашего #include, это не потому, что &local не принимает адрес одной и те же переменного, в каждом конкретном случае ,

В результате все может случиться, когда вы запускаете свою программу, в том числе перенося все свои с трудом заработанные сбережения на мой банковский счет или забрав всех представителей Джона Скита.

Именно поэтому выполнение функции inline решает проблему; также, помещая его в неназванное пространство имен, делает его функцией в каждой единицы перевода.

0

вы можете избежать использования статической во всех местах, вы можете использовать extern в вашем случае:

common.h :

extern int *gemini; 

common.cpp;

int *gemini = nullptr; 

также избегать использования local подобное, вместо этого вы можете сделать это:

inline void setit(int * p) 
{ 
    gemini = p; 
} 

void docastorUpdate() 
{ 
    static int local = 2; 
    setit(&local); 
    std::cout << "castor:" << *gemini << std::endl; 
} 
+1

Да, я знаю, как я уже упоминал в вопросе, что это решение не подходит. Лично я бы тоже не выбрал это решение. Но я хотел бы понять, в чем причина аварии. В любом случае, спасибо за комментарий! – Klaymen

9

Это прерывается, потому что вы нарушаете правило с одним определением.В правиле одного определения говорится, что в программе, во всех единицах перевода, существует только одно определение любой заданной функции. inline является своего рода исключением из этого правила, и это более или менее означает «дорогой компилятор, будет несколько определений этой функции, но все они будут одинаковыми, Я обещаю».

static, когда используются здесь для local, означает «дорогой компилятор, это внутренняя деталь, что только этот ЕП будет когда-нибудь, пожалуйста, не путайте его с переменным имени local из других единиц перевода»

Так вы пообещали компилятору, что все определения setit будут одинаковыми, и попросил компилятор предоставить каждому модулю перевода свою собственную переменную local.

Однако, поскольку функция setit использует любую переменную с именем local в области видимости, конечным результатом является два разных определения setit, каждый из которых использует другую переменную. Вы только что нарушили свое обещание. Компилятор доверял вам, и результат был полностью запутанной программой. Он думал, что это может сделать некоторые вещи с кодом, основанным на ваших обещаниях, но так как вы сломали их за спиной, те вещи, которые он пытался сделать с кодом, не работали вообще.

+0

Это отличный ответ :) – Klaymen

+0

Мне пришлось перевернуть монету, чтобы решить, какой ответ я должен принять как ответ, извините. Но тем не менее, мне очень понравилось читать ваши объяснения! – Klaymen

Смежные вопросы