Страницы

Поиск по вопросам

Показаны сообщения с ярлыком строковый-литерал. Показать все сообщения
Показаны сообщения с ярлыком строковый-литерал. Показать все сообщения

воскресенье, 8 марта 2020 г.

Почему без ошибок выполняется этот код?

#c #указатели #calloc #строковый_литерал


char * buf = (char*)calloc(0,0);

    buf = "vygbubinon";

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


Выводится "vygbubinon". Хотя по идее памяти выделено недостаточно
    


Ответы

Ответ 1



Давайте последовательно рассмотрим предложения указанного вами фрагмента кода. В первом предложении char * buf = (char*)calloc(0,0); выделяется память нулевого размера. Согласно стандарту языка C (7.22.3 Memory management functions) If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object. То есть вы можете запрашивать выделение памяти нулевого размера, но результат будет зависеть от платформы, где компилируется и выполняется программа. Либо будет возвращено значение NULL, либо некоторый действительный адрес памяти, по которому однако вы не можете обращаться к объекту. Так что данное предложение корректно. Далее в следующем предложении независимо от того, каков был результат предыдущего предложения (либо NULL, либо некоторый действительный адрес) указатель buf переприсваивается, и получает адрес первого символа строкового литерала "vygbubinon". buf = "vygbubinon"; Строковые литералы имеют статическую память. Следовательно, указатель получает адрес первого символа данного строкового литерала, расположенного в статической области памяти. Память для строковых литералов резервируется компилятором на этапе компиляции. (Компиляторы обычно собирают все литералы, присутствующее в программе, в некоторый пул литералов.) Ничего некорректного в этом предложении нет. Единственная проблема, связанная с этим предложением состоит в том, что если в предыдущем предложении была выделена память, то вы уже не сможете ее удалить, так как адрес этой памяти за счет переприсвоения указателя был утерян. Произойдет утечка памяти, но никакой ошибки времени компиляции или времени выполнения не будет. Ну, и, наконец, в этом предложении выводится на консоль данный литерал. printf("%s\n", buf); Так что фрагмент кода совершенно корректный за исключением возможной утечки памяти, что представляет собой лишь логическую ошибку.

Ответ 2



памяти выделено недостаточно Нет, достаточно. Вот этот код: buf = "vygbubinon"; не копирует строку в память, на которую указывает buf. Он всего лишь присваивает указателю buf адрес строки "vygbubinon", лежащей где-то в сегменте данных. Если будет понятней, то на уровень ниже это может выглядеть как-то так: .data ; ... _some_static_string db "vygbubinon", 0 buf dd ? ; ... .code ; ... mov buf, offset _some_static_string Если же вам нужно скопировать строку в область памяти, адрес которой содержится в указателе, нужно использовать функции семейств strcpy(), memcpy() и другие.

Ответ 3



char *buf - всего-лишь переменная-указатель. По сути она является просто адресом некоторой ячейки памяти. В случае со строками - это адрес самого первого символа. Поэтому в первой строке вы присваиваете переменной buf адрес выделенной памяти, но никак не саму память. А в следующей строке присваиваете buf значение из области данных программы, т.е. ваше "vygbubinon". При этом следует учитывать, что если вы не освобождаете кусок памяти, выделенный в куче вызовом malloc (если вы выделяли не 0) и перезаписываете указатель на нее, то происходит утечка памяти

пятница, 10 января 2020 г.

Какой тип данных используется для хранения строки?

#cpp #строки #cpp11 #объявление #строковый_литерал


Вот пример кода:

auto size = "Hello, world";
std::cout << sizeof(size);


В консоль выводится 4. Не могу понять, какой тип данных использует компилятор для
хранения этой строки?
    


Ответы

Ответ 1



Строковые литералы в C++ имеют типы константных символьных массивов. Данный литерал "Hello, world" имеет тип const char[13] Используемый в качестве выражения инициализации он неявно преобразуется к указателю на свой первый элемент, который имеет тип const char * Соответственно переменная size имеет тип const char *. Вы можете убедиться в этом, запустив на выполнение следующий фрагмент кода auto size = "Hello, world"; std::cout << typeid(size).name() << std::endl; Если бы вы написали так decltype(auto) size = "Hello, world"; std::cout << sizeof(size) << std::endl; то переменная size была бы ссылкой на строковый литерал и имела тип const char ( & )[13], а оператор sizeof вернул бы значение 13.

Ответ 2



С абстрактно-наивной точки зрения в данном случае приемлемыми типами для левой части инициализации являются const char (&)[13], [const] char[13] и const char *. Поведение, соответственно, будет зависеть о того, как правила языка описывают дедукцию типа для спецификатора auto в такой декларации. Эти правила совпадают со старыми-добрыми классическими правилами дедукции типов шаблонных аргументов для шаблонных функций, которые говорят, что в данном случае должен дедуцироваться тип "указатель" (а не тип "массив" или тип "ссылка"). Т.е. произойти в данном случае должно то же самое, что и примере template void foo(T) { std::cout << sizeof(T) << std::endl; } int main() { foo("Hello, world"); // специализируется как `foo` } При желании, как в вашем случае, так и в примере с функцией выше, вы можете направить дедукцию в другом направлении auto &size = "Hello, world"; и получить size типа const char (&)[13] и sizeof, возвращающий 13. Заметьте, что ни в первом, ни во втором случае не происходит создания копии исходного строкового литерала, т.е. никакого дополнительного "хранения строки" при этом не возникает. "Хранящаяся строка" в этих случаях - это лишь исходный строковый литерал и хранится он так, как он хранился с начала времен: как статический неизменяемый объект.

Ответ 3



Указатель на char sizeof(char *)

понедельник, 6 января 2020 г.

Почему строковый литерал в С++ это l-value?

#cpp #строковый_литерал


Например строковый литерал: "Hello, world!" не может стоять слева от оператора присваивания.
Тогда, как он может являться леводопустимым выражением?

"WTF" = "Hello, world!"; // ошибка компиляции



  l-value:
  
  
  Выражения, непосредственно обозначающие объект, non-modifiable в случае const-квалификации.
Например, имя переменной, параметра функции и т.п.
  Выражения ссылочных типов. non-modifiable в случае const-квалификации. В частности,
результат вызова функций, возвращающих объекты по ссылке; выражения, состоящие из имен
ссылочных переменных; операции преобразования к ссылочному типу и т.д.
  Результат встроенной операции разыменования (*) — lvalue указуемого типа; non-modifiable
в случае const-квалификации.
  Результат встроенных префиксных операций ++, --.
  Имя функции.
  Имя массива. 
  Строковые литералы.
  
  
  r-value:
  
  
  Выражения, обозначающие временные объекты. В частности, результат вызова функций,
возвращающих объекты не по ссылке; результат встроенных операций +, -. > *, / и т.п.;
явное создание временной переменной int() или C(); преобразования не к ссылочным типам
и т.д.
  Результат встроенной операции взятия адреса (&) — rvalue типа указатель.
  Результат встроенных постфиксных операций ++, --.
  Литералы, за исключением строковых (например, числовые литералы).
  Константы перечислений.
  

    


Ответы

Ответ 1



Строковый литерал в С++ по всей строгости - это константа, и уже по одному этому участвовать в присвоении не может. Более того, в 2.14.5.12 сказано: The effect of attempting to modify a string literal is undefined. Далее, по сути это - массив, а массив опять же в присвоении ну никак не участвует. В 3.10.9 говорится: "If an expression can be used to modify the object to which it refers, the expression is called modifiable. A program that attempts to modify an object through a nonmodifiable lvalue or rvalue expression is ill-formed." Наш же lvalue явно немодифицируемый. Впрочем, само понятие lvalue имеет несколько иной смысл, чем приписываемый вами. An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. Тут главное не то, что исторически они могли быть слева в выражении присваивания, а то, что это объект в памяти, от которого можно взять адрес. А получить адрес строкового литерала вполне можно, и в этом смысле это lvalue. По-моему, так... (с) Пух

Ответ 2



lvalue не значит, что такое выражение всегда может стоять слева от оператора присваивания. К примеру, пусть у Вас есть const int meConst = 5;, meConst в выражении meConst = 6; является lvalue, но пример всё равно собрать не удастся, т.к. нельзя изменять const int. То же самое со строковым литералом: да — это lvalue, нет — его нельзя модифицировать. И небольшая, но релевантная цитата из C11[6.3.2.1]: A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a constqualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a constqualified type.

понедельник, 27 мая 2019 г.

Почему без ошибок выполняется этот код?

char * buf = (char*)calloc(0,0);
buf = "vygbubinon";
printf("%s
", buf);
Выводится "vygbubinon". Хотя по идее памяти выделено недостаточно


Ответ

Давайте последовательно рассмотрим предложения указанного вами фрагмента кода. В первом предложении
char * buf = (char*)calloc(0,0);
выделяется память нулевого размера. Согласно стандарту языка C (7.22.3 Memory management functions)
If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
То есть вы можете запрашивать выделение памяти нулевого размера, но результат будет зависеть от платформы, где компилируется и выполняется программа. Либо будет возвращено значение NULL, либо некоторый действительный адрес памяти, по которому однако вы не можете обращаться к объекту. Так что данное предложение корректно.
Далее в следующем предложении независимо от того, каков был результат предыдущего предложения (либо NULL, либо некоторый действительный адрес) указатель buf переприсваивается, и получает адрес первого символа строкового литерала "vygbubinon".
buf = "vygbubinon";
Строковые литералы имеют статическую память. Следовательно, указатель получает адрес первого символа данного строкового литерала, расположенного в статической области памяти. Память для строковых литералов резервируется компилятором на этапе компиляции. (Компиляторы обычно собирают все литералы, присутствующее в программе, в некоторый пул литералов.) Ничего некорректного в этом предложении нет. Единственная проблема, связанная с этим предложением состоит в том, что если в предыдущем предложении была выделена память, то вы уже не сможете ее удалить, так как адрес этой памяти за счет переприсвоения указателя был утерян. Произойдет утечка памяти, но никакой ошибки времени компиляции или времени выполнения не будет.
Ну, и, наконец, в этом предложении выводится на консоль данный литерал.
printf("%s
", buf);
Так что фрагмент кода совершенно корректный за исключением возможной утечки памяти, что представляет собой лишь логическую ошибку.

среда, 20 февраля 2019 г.

Какой тип данных используется для хранения строки?

Вот пример кода:
auto size = "Hello, world"; std::cout << sizeof(size);
В консоль выводится 4. Не могу понять, какой тип данных использует компилятор для хранения этой строки?


Ответ

Строковые литералы в C++ имеют типы константных символьных массивов. Данный литерал "Hello, world" имеет тип const char[13]
Используемый в качестве выражения инициализации он неявно преобразуется к указателю на свой первый элемент, который имеет тип const char *
Соответственно переменная size имеет тип const char *
Вы можете убедиться в этом, запустив на выполнение следующий фрагмент кода
auto size = "Hello, world"; std::cout << typeid(size).name() << std::endl;
Если бы вы написали так
decltype(auto) size = "Hello, world"; std::cout << sizeof(size) << std::endl;
то переменная size была бы ссылкой на строковый литерал и имела тип const char ( & )[13], а оператор sizeof вернул бы значение 13