Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > [C++] "char* s = {'\0'}"


Автор: JackYF 16.8.2007, 18:24
Итак, мини-квест.

Имеем код:

Код

#include <cstdio>
int main(int argc, char* argv[])
{
  char* s = {'\0'};

  printf("%p\n", s);

  return 0;
}


Смотрим и пытаемся предугадать, что сделает этот код.

Потом запускаем свой любимый компилятор, загоняем в него код, компилируем, запускаем, материмся про себя, и пытаемся объяснить увиденное.
Я вот не смог.

Автор: Annihilator 16.8.2007, 18:51
Если компилятор обнаруживает строковый литерал, он сохраняет его в таблице строк программы и генерирует указатель на нужную строку.
При выполнении этой проги символы, образующие строковый литерал, сохраняются в таблице строк, а переменной s присваивается указатель на соответствующую строку в этой таблице

Автор: zkv 16.8.2007, 19:08
Цитата(Annihilator @  16.8.2007,  18:51 Найти цитируемый пост)
Если компилятор обнаруживает строковый литерал

а где тут строковый литерал? Я вижу только {'\0'}, что эквивалентно {0}, этот пример сработает также:
Код

#include <cstdio>
int main(int argc, char* argv[])
{
  int i = {'\0'};
  printf("%p\n", i);
  return 0;
}

Автор: jonie 16.8.2007, 19:12
Цитата

Потом запускаем свой любимый компилятор, загоняем в него код, компилируем, запускаем, материмся про себя, и пытаемся объяснить увиденное.
Я вот не смог.
а что такого-то? инициализация массива обычная. ведь 
Код

char* p;

тоже что и
Код

char p[1];

так что в чем проблемы-то?
----------
"тоже" всмысле раскрывания выражений компилятором ессна)

Автор: JackYF 16.8.2007, 19:13
Annihilator, первый пункт выполнен на ура. Теперь выполняй второй smile (тот, который с участием компилятора).

Добавлено через 2 минуты и 13 секунд
Цитата(jonie @  16.8.2007,  19:12 Найти цитируемый пост)
а что такого-то? инициализация массива обычная

да, да. Я вот так тоже думал. Я не зря написал пункт про компиляцию и просмотр результата.

Добавлено через 8 минут и 56 секунд
Цитата(zkv @  16.8.2007,  19:08 Найти цитируемый пост)
а где тут строковый литерал? Я вижу только {'\0'}, что эквивалентно {0}

верно. Как выясняется, строкового литерала здесь нету.

Вопрос дальше: почему {0} эквивалентно 0 в данном случае? Учитывая, что в фигурные скобки берутся элементы списка инициализации массивов?

Ведь в случае char s[] = {'\0'}; происходит ожидаемая инициализация массива из одного элемента.

Автор: Annihilator 16.8.2007, 19:56
если напишем вот так
Код

printf("%s\n", s);

то выдаст NULL, получается мы инициализируем указатель NULL`ом

Автор: jonie 16.8.2007, 20:04
Цитата

В языке С константа 0, когда она распознается как указатель,
        преобразуется компилятором в нулевой указатель. То есть, если во
        время инициализации, присваивания или сравнения с одной стороны
        стоит переменная или выражение, имеющее тип указателя, компилятор
        решает, что константа 0 с другой стороны должна превратиться в
        нулевой указатель и генерирует нулевой указатель нужного типа.
потомуШта....
-----------------------
кстати  нулевой указатель вовсе не обязательно представляется двоичным нулем

Автор: MAKCim 16.8.2007, 20:15
Цитата(JackYF @  16.8.2007,  19:13 Найти цитируемый пост)
Вопрос дальше: почему {0} эквивалентно 0 в данном случае? Учитывая, что в фигурные скобки берутся элементы списка инициализации массивов?

смотри
Код

int main() {
    int a = {0};
    return 0;
}

так что {0} = 0  smile

Добавлено через 1 минуту и 22 секунды
Цитата(jonie @  16.8.2007,  20:04 Найти цитируемый пост)
кстати  нулевой указатель вовсе не обязательно представляется двоичным нулем

не вижу смысла в обратном

Автор: bsa 16.8.2007, 21:05
Цитата(jonie @ 16.8.2007,  20:04)
кстати  нулевой указатель вовсе не обязательно представляется двоичным нулем

Боюсь, если какой-то умник сделает его не равным двоичному нулю, то куча прог перестанет работать, так как значение NULL станет истиной в булевых операциях.

Автор: MAKCim 16.8.2007, 21:23
bsa, 
я всегда явно проверяю
Код

if (a == NULL);

 smile 

Автор: bsa 16.8.2007, 21:47
А вот сразу оптимизацией занимаюсь.  smile 

Автор: nickless 16.8.2007, 21:52
Мдя, а я попробовал на gcc... smile 
Я последния раз это слово видел, когда на дельфи программировал smile 

Автор: Xenon 16.8.2007, 22:04
Макросы нерулез и кака smile

Автор: jonie 16.8.2007, 22:09
Цитата(bsa @ 16.8.2007,  21:05)
Цитата(jonie @ 16.8.2007,  20:04)
кстати  нулевой указатель вовсе не обязательно представляется двоичным нулем

Боюсь, если какой-то умник сделает его не равным двоичному нулю, то куча прог перестанет работать, так как значение NULL станет истиной в булевых операциях.

вы не поняли смысла моей цитаты.
нулевой указатель не обязательно нуль.
Цитата

так как значение NULL станет истиной в булевых операциях.
еслиб вы знали как компилятор рассматривает операцию !указатель то такого бы не сказали.
Цитата

if(p)        эквивалентно        if(p != 0)
и это случай, когда происходит сравнение, так что компилятор поймет,
        что неявный ноль - это нулевой указатель и будет использовать
        правильное значение. Здесь нет никакого подвоха, компиляторы работают
    именно так и генерируют    в обоих    случаях    идентичный код.    Внутреннее
    представление указателя    _не_ имеет значения.

Оператор логического отрицания ! может быть описан так:
!expr   на самом деле    эквивалентно    expr?0:1
....
язык гарантирует,
        что 0, встреченный там, где по контексту подразумевается указатель,
        будет заменен компилятором на нулевой указатель.


Цитата

1.11:    Почему так много путаницы связано с нулевыми указателями? Почему
    так часто возникают вопросы?

О:      Программисты на С традиционно хотят знать больше, чем это необходимо
        для программирования, о внутреннем представлении кода.  Тот факт,
        что внутреннее представление нулевых указателей для большинства
        машин совпадает с их представлением в исходном тексте, т.е. нулем,
        способствует появлению неверных обобщений. Использование макроса
        (NULL) предполагает, что значение может впоследствии измениться, или
        иметь другое значение для какого-нибудь компьютера.  Конструкция
        "if(p == 0)" может быть истолкована неверно, как преобразование перед
        сравнением p к целому типу, а не 0 к типу указателя.  Наконец, часто
        не замечают, что термин "null" употребляется в разных смыслах
        (перечисленных выше).

    Хороший    способ устранить путаницу - вообразить,    что язык С имеет
        ключевое слово (возможно, nil, как в Паскале), которое обозначает
        нулевой указатель. Компилятор либо пребразует "nil" в нулевой
        указатель нужного типа, либо сообщает об ошибке, когда этого сделать
    нельзя.    На самом деле, ключевое    слово для нулевого указателя в С
        - это не "nil" а "0". Это ключевое слово работает всегда, за
        исключением случая, когда компилятор воспринимает в неподходящем
    контексте "0" без указания типа    как целое число, равное    нулю, вместо
        того, чтобы сообщить об ошибке. Программа может не работать, если
        предполагалось, что "0" без явного указания типа - это нулевой
        указатель.

в общем читайте FAQ-и : http://faqs.org.ru/progr/c_cpp/cfaqrus.htm

Автор: MAKCim 16.8.2007, 22:24
Цитата(jonie @  16.8.2007,  22:09 Найти цитируемый пост)
и это случай, когда происходит сравнение, так что компилятор поймет,
        что неявный ноль - это нулевой указатель и будет использовать
        правильное значение.

ты же сам написал
if (p) ~ if (p != 0)
если NULL != (void*)0, то if (p) = true
а это неправильно с учетом семантики NULL

Добавлено через 9 минут и 15 секунд
jonie, 
с ключевым словом понятно
это очевидно
но если, опять таки, предположить, что NULL != (void*)0
все операции вида if (p), if (!p) идут лесом

Автор: jonie 16.8.2007, 22:40
я имею в виду "бинарный нуль" во времен выполнения).. и только. т.е.
if(p) {наш код} не факт что будет компилироваться в подобное
Код

test eax,eax
jNz наш код

о чем я и говорил
Цитата

кстати  нулевой указатель вовсе не обязательно представляется двоичным нулем
за сим оффтоп стоит закрыть имхо.

Автор: MAKCim 16.8.2007, 22:49
Цитата(jonie @  16.8.2007,  22:40 Найти цитируемый пост)
if(p) {наш код} не факт что будет компилироваться в подобное

такой код не будет генерироваться никогда
если if (p) ~ if (p != 0)
и компилятор тут ничего не сможет сделать
ему неизвестно значение p, поэтому он будет действовать по шаблону

Автор: jonie 16.8.2007, 23:03
сори опечатался) там jnz конечно же.
 и там не код, а псевдокод опять же.
т.е. eax - это наше значение p, сравниваем с нулем (вот где тот нуль о чем я говорю)....
но этого нуля там может и не быть - это неизветно никому, кроме компилятора. Он вполне может компилировать код if(p) {наш код} в нечто такое
Код

cmp eax,121212h
jnz наш код

вот это 121212h и есть то внутренее представляние нулевого указателя, которое нам недоступно.

Автор: Fazil6 16.8.2007, 23:14
Цитата(JackYF @  16.8.2007,  19:13 Найти цитируемый пост)
Вопрос дальше: почему {0} эквивалентно 0 в данном случае? Учитывая, что в фигурные скобки берутся элементы списка инициализации массивов?

Попробуй добавить элементы в этот "списка инициализации массивов".... или убери скобки и увидишь что получится.
Вообще-то фигурные скобки здесь по барабану, и следовательно никакого списка инициализации массива здесь нет, просто тупо приведением типа из символа \0 получается NULL. 

Автор: Rockie 17.8.2007, 10:48
Цитата(jonie @  16.8.2007,  22:09 Найти цитируемый пост)
в общем читайте FAQ-и

Чтоб далеко не ходить - с того же сайта 
Цитата

>> 018. Зачем нужен NULL при живом 0
> Q: Зачем нужен NULL?

A: (Arkady Belousov) - 16.10.96

     Формально стандарты утверждают, что NULL идентичен 0 и для обоих
гарантируется корректное преобразование к типу указателя. Но разница есть
для случая функций с переменным числом аргументов (например, printf) - не
зная типа параметров компилятор не может преобразовать 0 к типу указателя
(а на писюках NULL может быть равным 0L).




Автор: UnrealMan 17.8.2007, 11:01
Цитата(JackYF @  16.8.2007,  19:13 Найти цитируемый пост)
Вопрос дальше: почему {0} эквивалентно 0 в данном случае? Учитывая, что в фигурные скобки берутся элементы списка инициализации массивов?

Ведь в случае char s[] = {'\0'}; происходит ожидаемая инициализация массива из одного элемента.

Не, массивы тут не при чём:

Цитата(ISO/IEC 14­882:2003(E) §8.5 Initializers - 13)
If T is a scalar type, then a declaration of the form
    T x = { a };
is equivalent to
    T x = a;

Что же касается массивов

Код
char s[] = {'\0'}; 

то это уже инициализация агрегатов (см. §8.5.1 Aggregates).

Цитата(MAKCim @  16.8.2007,  20:15 Найти цитируемый пост)
так что {0} = 0  

Неправда smile


Автор: MAKCim 17.8.2007, 11:05
Цитата(UnrealMan @  17.8.2007,  11:01 Найти цитируемый пост)
Неправда 

неправда в общем случае
в моем случае правда  smile 
а за цитату из стандарта +

Автор: UnrealMan 17.8.2007, 11:07
Цитата
Формально стандарты утверждают, что NULL идентичен 0 

В стандарте C++ есть понятия null pointer value и null pointer constant (которая может быть преобразована к null pointer value):

Цитата(ISO/IEC 14­882:2003(E) §4.10 Pointer conversions)
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type.

Таким образом, любая константа интегрального типа, равная нулю, будь то 0, '\0', false или 1+5-2*3 может быть использована как нулевой указатель.

А про NULL сказано следующее:

Цитата(ISO/IEC 14­882:2003(E) §18.1 Types)
The macro NULL is an implementation-defined C + + null pointer constant in this International Standard. Possible definitions include 0 and 0L, but not (void*)0.

Автор: JackYF 17.8.2007, 15:07
UnrealMan, да, теперь яснее. Век живи, век учись. Держи +.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)