Перейти к тексту

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

Профиль создается, когда вы в первый раз заходите в developerWorks. Выберите данные в своем профиле (имя, страна/регион, компания) которые будут общедоступными и будут отображаться, когда вы публикуете какую-либо информацию. Вы можете изменить данные вашего ИБМ аккаунта в любое время.

Вся введенная информация защищена.

  • Закрыть [x]

При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

Вся введенная информация защищена.

  • Закрыть [x]

Решение проблем, связанных с временными переменными, при портировании приложений с Solaris на AIX

Нам Кеунг, Старший программист, IBM
Нам Кеунг (Nam Keung) -- старший программист, работавший в области развития AIX коммуникаций, AIX multimedia, разработки SOM/DSOM и производительности java. В настоящее время его назначение -- помощь ISV в разработке приложений, внедрение приложений, настройка производительности и обучение платформе IBM pSeries. Он работает программистом в IBM с 1989. Связаться с ним можно по адресу namkeung@us.ibm.com.
Говард Насгаард, аналитик, IBM
Говард Насгаард (Howard Nasgaard) - аналитик IBM в Toronto Software Development Lab, консультирующий программистов. Он занимается разработкой программного обеспечения 18 лет, последние семь лет ведет разработку компилятора C++. В настоящее время он является инженером-разработчиком внешнего интерфейса C++. Связаться с Говардом можно по адресу nasgaard@ca.ibm.com.
Брэд Кобб, ведущий консультант по техническим вопросам, Центр сотрудничества AIX, IBM
Больше чем 10 лет Брэд Кобб (Brad Cobb) помогал поставщикам решений портировать, настраивать, отлаживать и улучшать их приложения в средах разработки IBM AIX. Кроме помощи поставщикам решений, Брэд регистрировал патенты, публиковал статьи и выступал на конференциях разработчиков. Вы можете написать ему на адрес bcobb@us.ibm.com.

Описание:  В этой статье рассматриваются различия между AIX и Solaris при использовании временных переменных, которые нужно учитывать при портировании приложений для Solaris на AIX.

Дата:  26.11.2008
Уровень сложности:  простой
Активность:  1388 просмотров
Комментарии:  


Обзор

Компилятор Forte от Sun в меньшей степени, чем компилятор IBM VisualAge для C/C++, соответствует стандарту ANSI/ISO C. Это различие может стать причиной проблем при портировании приложений с Solaris на AIX. Особенно сложной проблемой является наличие разных механизмов управления временными переменными, реализованных в этих двух компиляторах. Данная статься посвящена этой проблеме и возможным способам ее решения.

Большинство корректно написанных C и C++ программ компилируются и запускаются на AIX без изменений. Под корректно написанными программами понимаются те, которые удовлетворяют следующим критериям:

  • Программа соответствует стандарту ANSI/ISO C.
  • На всех этапах цикла разработки ПО (проектирования, реализации и обслуживания) особое внимание уделялось переносимости приложения.
  • В программном коде активно используются прототипы функций.

Корректировка специфичных для конкретного компилятора фрагментов исходного кода - весьма сложная и трудоемкая процедура. Некоторые программисты быстро "сдаются" и начинают во всех бедах обвинять компилятор, не задумываясь над тем, не является ли корнем проблемы некорректно написанный код. Давайте взглянем правде в глаза - соблюдать стандарты трудно, и в большинстве случаев, когда возникает проблема, программисты не догадываются, что пишут нестандартный программный код C/C++. Если при компиляции не возникло ошибок, то это еще не означает, что поведение исполняемого кода будет корректным, так как компилятор может не соответствовать последним стандартам.

Мы приведем несколько примеров, которые показывают ошибки, связанные с неправильным употреблением временных переменных, и продемонстрируем различия в обработке компиляторами временных переменных. Также мы покажем, как использовать стандартные правила программирования для предотвращения этих проблем.

Неправильное использование временных переменных, пример 1

Предположим, что необходимо портировать этот блок кода из Solaris в AIX:

#include <strings.h>
#include <iostream.h>

class  Obj
{
        private:
            char* ptr;

        public:
                Obj();
                ~Obj();

                operator const char*() const;
                Obj& operator=(const Obj&);

                Obj(const Obj&);   
};

Obj::Obj()
{
        cout << "Obj::Obj() this=" << (int)this << endl;
        ptr = strdup("hello world");
}

Obj::Obj(const Obj& rhs)
{
        cout << "Obj::Obj(const Obj&) this=" << (int)this << endl;
        ptr = rhs.ptr ? strdup(rhs.ptr) : 0;
}

Obj& Obj::operator=(const Obj& rhs)
{
        cout << "Obj::operator=(const Obj&) this=" << (int)this << endl;
        ptr = rhs.ptr ? strdup(rhs.ptr) : 0;
        return *this;
}

Obj::~Obj()
{
        cout << "Obj::~Obj() this=" << (int)this << endl;
        delete ptr;
}

Obj::operator const char*() const
{
        cout << "Obj::operator const char*() this=" << (int)this << endl;
        return ptr;
}

Obj func()
{
        cout << "in func" << endl;
        Obj obj;
        cout << "obj created in func" << endl;
        return obj;
}

main()
{
        cout << "in main" << endl;
        const char *val  = func();
        cout << "val=" << val << endl;
}

Для понимания проблемы необходимо понять, что компилятор делает со строкой "const char val = func();". func() возвращает значение экземпляра объекта Obj. Чтобы сделать это, компилятор создает временную переменную для хранения возвращаемого значения. Другой тонкий аспект состоит в том, что компилятор генерирует вызов из временного объекта к Obj::operator const char *, который принимает копию указателя, ptr, и присваивает ему значение val.

Выходные данные компилятора Solaris Forte

Ниже представлены выходные данные компилятора Solaris Forte:

>/opt/bin/CC testptr.cpp
>a.out
in main
in func
Obj::Obj() this=-4261612
obj created in func
Obj::Obj(const Obj&) this=-4261500
Obj::~Obj() this=-4261612
Obj::operator const char*() this=-4261500
val=hello world
Obj::~Obj() this=-4261500

Выходные данные показывают, что деструктор временной переменной (типа Obj) вызывается после последней строки пользовательского кода в программе.

Выходные данные компилятора AIX VisualAge C++

Теперь сравним выходные данные компилятора Solaris с выходными данными компилятора VisualAge C++:

>xlC testptr.cpp
>a.out
in main
in func
Obj::Obj() this=804398976
obj created in func
Obj::operator const char*() this=804398976
Obj::~Obj() this=804398976
val=  ?

В данном случае деструктор временной переменной вызывается после строки, в которой временная переменная была создана. Проблема возникает в том случае, если переменная, предназначенная для хранения возвращаемого значения и вызываемая функцией func(), удалена до того как она должна была использоваться в качестве указателя, который, в свою очередь, извлекался при вызове оператора const char*(). Такое поведение временной переменной соответствует стандарту. В разделе 12.2, Temporary Objects, пункт 3, стандарта ANSI/ISO C говорится: "… Временные объекты уничтожаются на последнем этапе оценки законченного выражения, которое (лексически) содержит место, где эти объекты были созданы." (см. http://www.ansi.org).

Как решить эту проблему?

Данная проблема решается несколькими способами. Простейшим способом является перемещение момента создания временной переменной в ту строку, где переменная необходима:

main()
{
        cout << "in main" << endl;
        cout << "val=" << (const char*)func() << endl;
}

Есть и другой способ скорректировать некорректную работу кода. Присвойте результат вызова реальному объекту и используйте этот объект для того чтобы избавиться от необходимости наличия временного значения. Результат вызова метода func() присваивается объекту val, который продолжает существовать до конца выполнения метода main. Фактически использование этого способа решения проблемы с компиляторами IBM исключает необходимость во временной переменной. Вызов func() создает экземпляр val объекта Obj:

main()
{
        cout << "in main" << endl;
        Obj val = func();
        cout << "val=" <<  val << endl;
}

Неправильное использование временных переменных, пример 2

В этом примере приложение определяет свой собственный строковый класс:

>cat myString.h
#include <string>
#define MY_SL_STD(MY_NAME) ::std::MY_NAME

class MYString
{
public:
  // стандартные конструкторы
  MYString()                                                    {}
  MYString(const MY_SL_STD(string&) data)  : data_(data)        {}
  MYString(const MYString& str)            : data_(str.data_)   {}
  MYString(char c, size_t N)               : data_(N, c)        {}
  MYString(const char* s)                  : data_(s)           {}
  MYString(const char* s, size_t N)        : data_(s,N)         {}

  // специальный конструктор на базе char
  MYString(char c)                         : data_(1,c)         {}

  ~MYString() {}
 const char*       data() const { return data_.c_str(); }

  // преобразование типов:
  operator const char*() const { return data_.c_str(); }

protected:

  MY_SL_STD(string) data_;
};

>cat myString.C

#include <iostream.h>
#include "mystring.h"

class A
{
public:
    A(const char * str_)
    {
        strcpy(str, str_);
    }
    MYString getStr()
    {
        return str;
    }
    void print()
    {
        cout<<"object A " << str <<endl;
    }

private:
    char str[2000];
};

void foo(const char* s)
{
    cout<<"foo: "<<s<<endl;
}

int main()
{
    A a("This is a test");
    a.print();
    const char * p = (const char*) a.getStr(); 
    cout << "p=" << p << endl;
    return (0);
}

Выходные данные компилятора Solaris Forte

Ниже представлены выходные данные компилятора Solaris Forte:

>CC myString.C
>a.out
object A This is a test
p= This is a test

Выходные данные компилятора AIX VisualAge

Теперь сравним с выходными данными компилятора AIX VisualAge:

>xlC myString.C
>a.out
object A This is a test
p=  e@

В этом примере массив, состоящий из элементов типа char, в объекте типа object конвертируется в MYString, причем доступ к этому массиву осуществляется через метод getStr. Метод A::getStr создаст объект A::MYString. Всякий раз, когда используется getStr, программа генерирует неправильные результаты, тем самым указывая на проблему.

Когда вызывается getStr(), этот метод берет буфер str и создает объект MYString. Возвращается объект типа MYString, и далее программный код вызывает метод MYString для конвертации в тип const char *. Этот метод возвращает указатель на временную копию строки "This is a test". Как только указатель был получен и записан в переменную p, для временного объекта вызывается деструктор. Теперь область памяти, на которую ссылается p, не содержит "This is a test." Так как строка символов является временной копией, она существует только в исходном местоположении внутри объекта. При любом использовании p будет выполнено обращение к "мусору" (garbage).

Поскольку для создания этих временных объектов используется динамическая память, нельзя зависеть от объекта, существующего дольше, чем выражение, в котором он используется.

Как решить эту проблему?

Для решения этой проблемы есть несколько путей:

int main()
{
   A a(This is a test");
   a.print();
   cout<<"Problem= "<<(const char*) a.getStr()<<endl; 
   MYString testStr = a.getStr(); 
   cout<<"(const char *) "<<(const char*) testStr<<endl; 
   cout<<"testStr " <<testStr<<endl;
   cout<<"data() "<<testStr.data()<<endl;
   foo((const char *) a.getStr());
   return (0);
}

Для объекта testStr деструктор временного объекта вызван в точке следования (sequence point) - точка с запятой в конце выражения - после выполнения операторов cout. Применение testStr приводит к созданию нового объекта, который будет существовать до конца выполнения функции и поэтому может использоваться где угодно.

Выходные данные будут подобными следующим:

>xlC myString.C
>a.out
object A This is a test
Problem= This is a test
(const char *) This is a test
testStr This is a test
data() This is a test
foo: This is a test


Заключение

Различия, создаваемые компилятором, из всех проблем часто являются самыми сложными и запутанными. Первой реакцией в такой ситуации является "сваливание" всех проблем на компилятор и его поставщика, а не анализ ошибок самого кода. В этой статье мы описали две похожие ситуации, которые показывают, как различные компиляторы реализуют управление временными переменными. Мы также объяснили, как исправить эти проблемы с некорректной работой при помощи стандартных подходов в программировании.


Ресурсы

Об авторах

Нам Кеунг (Nam Keung) -- старший программист, работавший в области развития AIX коммуникаций, AIX multimedia, разработки SOM/DSOM и производительности java. В настоящее время его назначение -- помощь ISV в разработке приложений, внедрение приложений, настройка производительности и обучение платформе IBM pSeries. Он работает программистом в IBM с 1989. Связаться с ним можно по адресу namkeung@us.ibm.com.

Говард Насгаард (Howard Nasgaard) - аналитик IBM в Toronto Software Development Lab, консультирующий программистов. Он занимается разработкой программного обеспечения 18 лет, последние семь лет ведет разработку компилятора C++. В настоящее время он является инженером-разработчиком внешнего интерфейса C++. Связаться с Говардом можно по адресу nasgaard@ca.ibm.com.

Больше чем 10 лет Брэд Кобб (Brad Cobb) помогал поставщикам решений портировать, настраивать, отлаживать и улучшать их приложения в средах разработки IBM AIX. Кроме помощи поставщикам решений, Брэд регистрировал патенты, публиковал статьи и выступал на конференциях разработчиков. Вы можете написать ему на адрес bcobb@us.ibm.com.

Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Спасибо. Эта запись была помечена для модератора.


Помощь по сообщениям о нарушениях

Сообщение о нарушениях

Сообщение о нарушении не было отправлено. Попробуйте, пожалуйста, позже.


developerWorks: вход


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


При первом входе в developerWorks для Вас будет создан профиль. Выберите информацию отображаемую в Вашем профиле — скрыть или отобразить поля можно в любой момент.

Выберите ваше отображаемое имя

При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

(Должно содержать от 3 до 31 символа.)


Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Оценить эту статью

Комментарии

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=AIX и UNIX
ArticleID=354879
ArticleTitle=Решение проблем, связанных с временными переменными, при портировании приложений с Solaris на AIX
publish-date=11262008
author1-email=namkeung@us.ibm.com
author1-email-cc=
author2-email=nasgaard@ca.ibm.com
author2-email-cc=
author3-email=bcobb@us.ibm.com
author3-email-cc=