void func(char *&buff) { buff = "abc"; } почему работает?
От:
Аноним
Дата:
22.08.07 20:17
Оценка:
Всем привет.
Мне нужно было передать парамент — указтель в функцию, и чтоб функция адрес этого указателя изменила — методом тыка добился, но не пойму почему работает?
В функции main есть переменная p. При вызове функции func ссылка buff привязывается (bind-ится) к переменной p. Функция func пишет в переменную p (косвенно, через ссылку buff) указатель на первый char строкового литерала "abc", этот первый char хранит код буквы «a» (в кодировке ASCII буква «a» кодируется числом 97). При выходе из функции func строковый литерал "abc" не исчезает, так как размещён статически, и переменная p продолжает указывать на него. Это позволяет засунуть строку в текстовый поток вывода std::cout, который выводит строку на консоль (по умолчанию).
А вообще лучше пользуйтесь каким-нибудь строковым классом, например std::string.
Здравствуйте, Alex Dav, Вы писали:
AD>Здравствуйте, Пётр Седов, Вы писали:
ПС>>...При выходе из функции func строковый литерал "abc" не исчезает, так как размещён статически, и переменная p продолжает указывать на него. ...
AD>объясните, пожалуйста, почему? Вроде же в стеке должно было размещаться и при выходе из блока уничтожаться? AD>Спасибо.
На стэк кладутся локальные переменные, а строковые литералы находятся в статической памяти и существуют
все время жизни программы.
Здравствуйте, Alex Dav, Вы писали:
AD>Здравствуйте, Пётр Седов, Вы писали:
ПС>>...При выходе из функции func строковый литерал "abc" не исчезает, так как размещён статически, и переменная p продолжает указывать на него. ...
AD>объясните, пожалуйста, почему? Вроде же в стеке должно было размещаться и при выходе из блока уничтожаться? AD>Спасибо.
Для того, чтобы разместить массив символов на стеке, его нужно определить внутри функции именно как массив и, в этом случае, возврат ссылки или указателя на этот массив будет грубой ошибкой. Строковые литералы же располагаются в статической памяти программы и указатель на эти данные остается валидным как внутри, так и снаружи функции.
void func(char *&buff)
{
char buffer[] = "Hello"; //массив символов размером в 6 элементов (не забываем про нулевой символ в конце)
//создается в стеке и инициализируется литералом, хранящимся в статической памяти.
buff = buffer; //Ошибка: выдача наружу указателя на данные, хранящиеся в стеке
buff = "Hello"; //Ok - "Hello woird" хранится не в стеке.
}
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Аноним, Вы писали:
А>А как-нибуть еще можно задачу эту по другому решить? (только не возвращая указатель)
Какую задачу? Если строку возвратить, то вот
Здравствуйте, Alex Dav, Вы писали:
AD>Здравствуйте, Пётр Седов, Вы писали:
ПС>>...При выходе из функции func строковый литерал "abc" не исчезает, так как размещён статически, и переменная p продолжает указывать на него. ...
AD>объясните, пожалуйста, почему? Вроде же в стеке должно было размещаться и при выходе из блока уничтожаться? AD>Спасибо.
Уже ответили. Добавлю
char*p = "abc";
p[1] = 'B';
работает не так, как ожидалось (что "abc" находится в стеке).
Здравствуйте, Аноним, Вы писали: А>А как-нибуть еще можно задачу эту по другому решить? (только не возвращая указатель)
В C++ работать с текстом можно с помощью стандартных классов std::string и std::ostringstream, например:
#include <assert.h>
#include <string> // нет ".h"#include <sstream>
using namespace std; // чтобы не писать std::string, std::ostringstream
// строка возвращается через результат функции
string MakeCowsText(int NumCows)
{
assert(NumCows >= 0); // пред-условиеswitch (NumCows)
{
case 0: return"no cows"; // строковый литерал неявно преобразуется в std::stringcase 1: return"1 cow";
default:
{
ostringstream s;
s << NumCows << " cows"; // используется так же как std::coutreturn s.str(); // метод ostringstream::str возвращает накопленный текст как std::string
}
}
}
// строка возвращается через out-параметр (pText)void MakeCowsText2(string* pText, int NumCows)
{
assert(NumCows >= 0);
switch (NumCows)
{
case 0: *pText = "no cows"; break;
case 1: *pText = "1 cow"; break;
default:
{
ostringstream s;
s << NumCows << " cows";
*pText = s.str();
}
}
}
int main()
{
assert(MakeCowsText(/*NumCows:*/0) == "no cows");
assert(MakeCowsText(/*NumCows:*/1) == "1 cow");
assert(MakeCowsText(/*NumCows:*/2) == "2 cows");
string Text;
MakeCowsText2(&Text, /*NumCows:*/0);
assert(Text == "no cows");
MakeCowsText2(&Text, /*NumCows:*/1);
assert(Text == "1 cow");
MakeCowsText2(&Text, /*NumCows:*/2);
assert(Text == "2 cows");
return 0;
}
Здравствуйте, alzt, Вы писали:
A>Здравствуйте, Smal, Вы писали:
A>>>
A>>>char*p = "abc";
A>>>p[1] = 'B';
A>>>
A>>>работает не так, как ожидалось (что "abc" находится в стеке). S>>Не работает, ибо менять данные строкового литерала нельзя.
A>Это я и имел в виду. Менять нельзя. Скомпилировать такое — без проблем. A>А вот если бы "abc" находилось в стеке, то проблем не было бы.
Проблемы появились бы при выходе из функции. Ты бы вернул указатель на временный объект.
Значительно уменьшить количество проблем позволит std::string.
Здравствуйте, Аноним, Вы писали:
А>Пётр Седов, с чем не согласен?
1.
const char szAsked = "abc";
szAsked — это указатель или массив? Если szAsked — указатель, то надо вписать звёздочку:
const char* szAsked = "abc";
Если szAsked — массив, то надо вписать квадратные скобки:
const char szAsked[] = "abc";
2.
void GetStr(std::string& str)
{
str = abc;
}
Что такое abc? Если имелся в виду строковый литерал, то надо вписать двойные кавычки:
void GetStr(std::string& str)
{
str = "abc";
}
3.
void GetStr(char* szBuf, int nBufSize)
{
strcpy(szBuf, szAsked, nBufSize);
}
Стандартная C-шная функция strcpy принимает два параметра:
char* strcpy(char* pDest, const char* pSource);
4.
void GetStr(char** pszBuf)
{
if (pszBuf) {
// manual deallocation is required
*pszBuf = new char[sizeof(szAsked)/sizeof(szAsked[0])];
strcpy(*szBuf, szAsked, sizeof(szAsked)/sizeof(szAsked[0]));
}
}
Во-первых, если szAsked — указатель, то sizeof(szAsked) = размер указателя в байтах (на 32-битной платформе скорее всего 4), а не размер массива char-ов.
Во-вторых, опять же, strcpy принимает два параметра.
В-третьих, что такое szBuf? (Скорее всего опечатка, имелось в виду pszBuf.)
По-моему, Ваш совет сбивает с толку новичка. Если бы я был новичком в C++, то я бы не хотел получить такой совет. За это и оценка «Не согласен» (минус).
A>>>>работает не так, как ожидалось (что "abc" находится в стеке). S>>>Не работает, ибо менять данные строкового литерала нельзя.
A>>Это я и имел в виду. Менять нельзя. Скомпилировать такое — без проблем. A>>А вот если бы "abc" находилось в стеке, то проблем не было бы. S>Проблемы появились бы при выходе из функции. Ты бы вернул указатель на временный объект. S>Значительно уменьшить количество проблем позволит std::string.
Это естественно, но я же никого не заставляю использовать временный объект.
Собственно мой пост был ответом на это:
ПС>>...При выходе из функции func строковый литерал "abc" не исчезает, так как размещён статически, и переменная p продолжает указывать на него. ... AD>объясните, пожалуйста, почему? Вроде же в стеке должно было размещаться и при выходе из блока уничтожаться?
Т.е., если бы "abc" было в стеке, то в коде выше не было бы проблем, но "abc" уже исчезал бы в отличие от действительности.
Т.е. всё потому, что в стеке не располагается.
Re[5]: Пётр Седов, с чем не согласен?
От:
Аноним
Дата:
24.08.07 06:35
Оценка:
Здравствуйте, Пётр Седов, Вы писали:
[]
ПС>По-моему, Ваш совет сбивает с толку новичка. Если бы я был новичком в C++, то я бы не хотел получить такой совет. За это и оценка «Не согласен» (минус).
Да, согласен, описок много.
Спасибо за подробный ответ.
Здравствуйте, Пётр Седов, Вы писали:
ПС>Здравствуйте, Аноним, Вы писали: А>>А как-нибуть еще можно задачу эту по другому решить? (только не возвращая указатель) ПС>В C++ работать с текстом можно с помощью стандартных классов std::string и std::ostringstream, например: ПС>
ПС>#include <assert.h>
ПС>#include <string> // нет ".h"
ПС>#include <sstream>
ПС>using namespace std; // чтобы не писать std::string, std::ostringstream
ПС>// строка возвращается через результат функции
ПС>string MakeCowsText(int NumCows)
ПС>{
ПС> assert(NumCows >= 0); // пред-условие
ПС> switch (NumCows)
ПС> {
ПС> case 0: return"no cows"; // строковый литерал неявно преобразуется в std::string
ПС> case 1: return"1 cow";
ПС> default:
ПС> {
ПС> ostringstream s;
ПС> s << NumCows << " cows"; // используется так же как std::cout
ПС> return s.str(); // метод ostringstream::str возвращает накопленный текст как std::string
ПС> }
ПС> }
ПС>}
ПС>// строка возвращается через out-параметр (pText)
ПС>void MakeCowsText2(string* pText, int NumCows)
ПС>{
ПС> assert(NumCows >= 0);
ПС> switch (NumCows)
ПС> {
ПС> case 0: *pText = "no cows"; break;
ПС> case 1: *pText = "1 cow"; break;
ПС> default:
ПС> {
ПС> ostringstream s;
ПС> s << NumCows << " cows";
ПС> *pText = s.str();
ПС> }
ПС> }
ПС>}
ПС>int main()
ПС>{
ПС> assert(MakeCowsText(/*NumCows:*/0) == "no cows");
ПС> assert(MakeCowsText(/*NumCows:*/1) == "1 cow");
ПС> assert(MakeCowsText(/*NumCows:*/2) == "2 cows");
ПС> string Text;
ПС> MakeCowsText2(&Text, /*NumCows:*/0);
ПС> assert(Text == "no cows");
ПС> MakeCowsText2(&Text, /*NumCows:*/1);
ПС> assert(Text == "1 cow");
ПС> MakeCowsText2(&Text, /*NumCows:*/2);
ПС> assert(Text == "2 cows");
ПС> return 0;
ПС>}
ПС>
Вот после таких советов люди и начинают использовать std::string где ни попадя, совершенно не задумываясь о тяжелом механизме аллокации памяти, лежащем в основе использования динамических строк STL. Для многопоточных приложений, это может стать серьезной проблемой, так как реаллокации при создании — копировании строк естественно требуют межпоточной синхронизации и не факт, что в используемом STL она будет грамотно реализована.
Здравствуйте, Аноним, Вы писали: А>Вот после таких советов люди и начинают использовать std::string где ни попадя, совершенно не задумываясь о тяжелом механизме аллокации памяти, лежащем в основе использования динамических строк STL. Для многопоточных приложений, это может стать серьезной проблемой, так как реаллокации при создании — копировании строк естественно требуют межпоточной синхронизации и не факт, что в используемом STL она будет грамотно реализована.
Полностью согласен. Я вообще не использую STL и iostreams в серьёзном коде. Но если человек — начинающий или casual-ный программист, то лучше ему использовать стандартные библиотеки, так как про них очень много написано (книги, статьи, форумы).
Также, если человек раньше программировал на Delphi (или на любом другом высоко-уровневом языке со встроенным строковым типом) и переходит на C++, то ему будет привычно использовать какой-нибудь C++-ный строковый класс. Основной плюс std::string — стандартность, он доступен в любом современном компиляторе C++.