LINUX.ORG.RU

Вопрос по синтаксису C.


0

0

Начал изучать управление процессами под Linux и наткнулся на следующую конструкцию:
int execv(const char *path, char *const atgv[]);

Объясните пожалуйста, в чем разница между "const char *" и "char *const". Сам я не очень понимаю. Спасибо.

anonymous

Вкратце: const char* p означает что можно передвинуть указатель, но нельзя изменить значение, на которое указывает указатель.

char* const p означает что изменить значение можно, но передвинуть указатель - нельзя. В c++ указатели типа type* const аналогичны "ссылкам" - references.

Соответственно, const char* const p - нельзя "передвинуть" и нельзя "изменить", а char* p - можно и "передвигать" и "менять".

Для более подробного объяснения, включая дискуссию о volatile и typedef, я бы посоветовал Вам почитать книжку Питера ван дер Линдена "Expert C programming - Deep C secrets". Там достаточно досконально описывается логика c-v modifiers.

omerm
()
Ответ на: комментарий от omerm

референс конечно реализуется через указатель, но разименовку к нему применить нельзя, и подвигать тоже

anonymous
()
Ответ на: комментарий от anonymous

> референс конечно реализуется через указатель, но разименовку к нему
> применить нельзя, и подвигать тоже

foo * const ты тоже не подвигаешь, а в остальном разница только в
наличии/отсутствии & и *. То есть разный синтаксис, но не семантика.

int19h ★★★★
()

Вообще, ответ написан в самом вопросе.

const char* - const стоит перед типом, значит это указатель на константу.

char* const name - const стоит перед именем переменной, значит это - константный указатель.

С++ очень логичен в этом плане.

devinull ★★
()
Ответ на: комментарий от int19h

основное отличие как раз в семантике!

1. ссылки не могут указывать в никуда и обязательно должны быть проинициализированы, хотя возможна ситуация dangling reference, как и dangling pointer.

2. сами ссылки не могут быть cv-qualified, только типы на которые они ссылаются.

AnToXa
()
Ответ на: комментарий от AnToXa

> 1. ссылки не могут указывать в никуда и обязательно должны быть
> проинициализированы, хотя возможна ситуация dangling reference, как и
> dangling pointer.

Угумс...

int& foo = *static_cast<int*>(0);

Хотя требование инициализации их, таки да, отличает. Но не
принципиально.

> 2. сами ссылки не могут быть cv-qualified, только типы на которые они
> ссылаются.

Я сравнивал не ссылки и указатели вообще, а ссылки и _константные_
указатели. Почувствуй разницу, что называется.

int19h ★★★★
()
Ответ на: комментарий от int19h

> int& foo = *static_cast<int*>(0);

вот это есть undefined behaviour по стандарту.

> Хотя требование инициализации их, таки да, отличает. Но не принципиально.

кому как.

> Я сравнивал не ссылки и указатели вообще, а ссылки и 
> _константные_ указатели. Почувствуй разницу, что 
> называется.
вам привести еще различий или лучше закончим обсуждение?

AnToXa
()
Ответ на: комментарий от AnToXa

> вот это есть undefined behaviour по стандарту.

И? Дереференсинг неинициализированного указателя - тоже undefined
behaviour.

> вам привести еще различий или лучше закончим обсуждение?

Как угодно. Меня это не напрягает, да и, глядишь, узнаю что-то новое =)

int19h ★★★★
()
Ответ на: комментарий от int19h

> И? Дереференсинг неинициализированного указателя - тоже undefined behaviour.

ага, только мы говорили об инициализации :)
вот в такой инициализации ссылки происходит Null pointer dereference, что и порождает UB, т.е. невозможно получить неинициализированную ссылку, кроме как через UB.

> Как угодно. Меня это не напрягает, да и, глядишь, узнаю что-то новое =)
надеюсь я тоже узнаю :))
вот навскидку, что вспомнилось.

0. ссылка вообще сама по себе "идеологически" не является "объектом", т.е. некоторой осязаемой сущностью, это по сути алиас.
нельзя сделать массив ссылок, положить ссылку в контейнер, template argument deduction выводит не ссылки, а сами типы всегда, хотя бы указатели :) (хотя можно явно указать что параметр - ссылка).
ссылки не обязательно имеют storage, это implementation-defined.

1. ссылка может "extend"(не очень нравится русский эквивалент, поэтому оставляю на англ, простите) время жизни temporary объекта.
например:
struct Foo {};
struct Bar : Foo {};

typedef Foo& ref;
Bar make() { /**/ };

ref r = make(); // теперь временный объект, который вернула функция make() is bound to reference, и живет пока ссылка не выходит из scope.

2. не бывает cv-qualified ссылок, ссылки всегда immutable, указатель я могу скастить и изменить объект и указатель, через ссылку - только объект.

3. инициализация ссылок происходит иначе, один из примеров: cref может быть привязана к rvalue, указатель - нет, т.к. у rvalue нету адреса.
void foo(int const& val) { /**/ }
foo(10); // создается temporary object int(10), который потом привязывается к ссылке, там в 8.5.2 довольно много на тему :)

AnToXa
()
Ответ на: комментарий от AnToXa

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

Массив - да, но вот насчет контейнеров... насколько я помню, можно,
например, сделать boost::tuple<int&>.

> template argument deduction выводит не ссылки, а сами типы всегда,
> хотя бы указатели :) (хотя можно явно указать что параметр - ссылка).

Это можно (условно) записать в неявный дереференсинг.

> ссылки не обязательно имеют storage, это implementation-defined.

Собственно, да. Вы главное следствие из этого забыли - на ссылку нельзя
получить другую ссылку или указатель =) впрочем, я тоже забыл. Каюсь.
Да, это, пожалуй, самое серьезное, принципиальное различие. Остальное
все же мелочь...

> 1. ссылка может "extend"(не очень нравится русский эквивалент,
> поэтому оставляю на англ, простите) время жизни temporary объекта.

Да, про это я тоже забыл. А зря, кстати - этот трюк в том же boost
нередко для оптимизации используют.

> 2. не бывает cv-qualified ссылок, ссылки всегда immutable, указатель
> я могу скастить и изменить объект и указатель, через ссылку - только
> объект.

А вот здесь поправлю: строго говоря, убрать const с чего-либо можно,
только если оно не было изначально объявлено как const, в противном
случае это будет undefined behaviour. Т.е.:

  int* p;
  int* const cp = 0;

  int foo(int* const& cp) {
    const_cast<int*&>(cp) = 0;
  }

  foo(p);  // well-defined
  foo(cp); // undefined

int19h ★★★★
()
Ответ на: комментарий от int19h

> Массив - да, но вот насчет контейнеров... насколько я помню, можно, например, сделать boost::tuple<int&>.

tuple - это вообще-то не контейнер, строго говоря :) но можно, только инициализировать надо.

> Это можно (условно) записать в неявный дереференсинг.
можно наверное, неявный дереференсинг следует из того, что ссылка - не объект, а алиас.

> Собственно, да. Вы главное следствие из этого забыли - на ссылку нельзя получить другую ссылку или указатель =) 
> впрочем, я тоже забыл. Каюсь.
> Да, это, пожалуй, самое серьезное, принципиальное различие. Остальное все же мелочь...
угу, забыл, виноват :)
это тоже следует из того, что ссылка - не объект
хотя с другой стороны у нее даже может быть linkage, чуть смазывает концепцию иногда :)

> Да, про это я тоже забыл. А зря, кстати - этот трюк в том же boost нередко для оптимизации используют.
а где там оптимизация? temporary все равно создается, это скорее для скрытия деталей используется имхо.
для scope guard например:
struct scope_guard_base {};
template<class Functor> struct scope_guard_impl { /* функтор вызывается в dtor и обычно генерится через boost::bind */};
typedef scope_guard_base const& scope_guard;
template<class Functor> scope_guard_impl<Functor> make_guard(Functor const& f);

и юзают: scope_guard g = make_guard(boost::bind(....));

> А вот здесь поправлю: строго говоря, убрать const с чего-либо можно, только если оно не было изначально объявлено как const

да, все так, но ведь могло быть и не объявлено, а просто указание, что функция не меняет указатель, тогда все well-defined, т.е.
void foo(char * const p) { const_cast<char*>(); } // вот за это руки оторвать по самые ноги :)
char *ptr = new char[10];
foo(ptr);

вот и поговорили :) жаль нету как на рсдн иконки :beer: :)

AnToXa
()
Ответ на: комментарий от AnToXa

> да, все так, но ведь могло быть и не объявлено, а просто указание,
> что функция не меняет указатель, тогда все well-defined, т.е. void foo
> (char * const p) { const_cast<char*>(); } // вот за это руки оторвать
> по самые ноги :) char *ptr = new char[10]; foo(ptr);

Если функция не меняет указатель, то тогда смысл его прописывать
константным (это все равно что const int брать в качестве аргумента).

Кстати, если я правильно понимаю, то вышеприведенное на самом деле
undefined по той же причине, что и раньше. Т.к. p объявлено как char
*const, и не важно, что это локальная переменная, и даже что это
параметр, убирать с него const все равно нельзя, раз он с ним объявлен.
Или ты имел в виду все-таки (const char* p)?

int19h ★★★★
()
Ответ на: комментарий от int19h

нет, я имел в виду то что написано.
с этими casting away constness я сам не доконца разбираюсь,
но вот счас почитал еще раз стандарт:

5.2.11 Const cast [expr.const.cast]
.....
7 [Note: Depending on the type of the object, 
a write operation through the pointer, 
lvalue or pointer to data member resulting from
a const_cast that casts away a const-qualifier
may produce undefined behavior (7.1.5.1). ]

7.1.5.1 The cv-qualifiers [dcl.type.cv]
.....
4 Except that any class member declared mutable (7.1.1)
can be modified, any attempt to modify a const object
during its lifetime (3.8) results in undefined behavior.

т.е. если я все правильно понимаю, то ты прав, это у нас действительно UB,
т.к. внутри функции у нас копия аргумента.
и для того что я хотел проиллюстрировать надо было писать char const *.
вот я и разобрался :)

AnToXa
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.