Они по существу одинаковы, но using
обеспечивает alias templates
, что весьма полезно. Один хороший пример, который я мог найти, следующий:
namespace std {
template<typename T> using add_const_t = typename add_const<T>::type;
}
Итак, мы можем использовать std::add_const_t<T>
вместо typename std::add_const<T>::type
Разница в том, что
char *s = "Hello world";
поместит «Hello world»
в части памяти, доступные только для чтения , и сделает ] s
указатель, который делает любую операцию записи в эту память незаконной.
При выполнении:
char s[] = "Hello world";
помещает буквальную строку в постоянную память и копирует строку во вновь выделенную память в стеке. Таким образом,
s[0] = 'J';
становится законным.
В свете комментариев здесь должно быть очевидно, что: char * s = "hello"; Это плохая идея, и ее следует использовать в очень узком контексте.
Это может быть хорошей возможностью указать, что «постоянная корректность» - это «хорошо». Всегда и везде, где Вы можете, используйте ключевое слово "const" для защиты своего кода от "расслабленных" вызывающих абонентов или программистов, которые обычно наиболее "расслаблены", когда в игру вступают указатели.
Достаточно мелодрамы, вот чего можно достичь при добавлении указателей в "const". (Примечание: объявления указателей следует читать справа налево.) Вот 3 различных способа защитить себя при игре с указателями:
const DBJ* p means "p points to a DBJ that is const"
- то есть объект DBJ нельзя изменить с помощью p.
DBJ* const p means "p is a const pointer to a DBJ"
- то есть вы можете изменить объект DBJ с помощью p, но вы не может изменить сам указатель p.
const DBJ* const p means "p is a const pointer to a const DBJ"
- то есть вы не можете изменить сам указатель p, а также не можете изменить объект DBJ с помощью p.
Ошибки, связанные с попыткой мутации констант, обнаруживаются во время компиляции.
(Предполагается, что вы используете компилятор C ++, конечно?)
- DBJ
В случае:
char *x = "fred";
x - это lvalue - ему можно присвоить. Но в случае:
char x[] = "fred";
x не является l-значением, это r-значение - вы не можете присвоить ему значение.
char s[] = "hello";
объявляет s
как массив char
, который достаточно длинный для хранения инициализатора (5 + 1 char
s ) и инициализирует массив, копируя элементы данного строкового литерала в массив.
char *s = "hello";
объявляет s
как указатель на один или несколько (в данном случае более) char
s и указывает его прямо на фиксированное (доступное только для чтения) место, содержащее литерал "hello"
.
Учитывая объявления
char *s0 = "hello world";
char s1[] = "hello world";
, предположим следующую гипотетическую карту памяти:
0x01 0x02 0x03 0x04 0x00008000: 'h' 'e' 'l' 'l' 0x00008004: 'o' ' ' 'w' 'o' 0x00008008: 'r' 'l' 'd' 0x00 ... s0: 0x00010000: 0x00 0x00 0x80 0x00 s1: 0x00010004: 'h' 'e' 'l' 'l' 0x00010008: 'o' ' ' 'w' 'o' 0x0001000C: 'r' 'l' 'd' 0x00
Строковый литерал "hello world"
представляет собой массив из 12 элементов char
( const char
в C ++) со статической продолжительностью хранения, что означает, что память для нее выделяется при запуске программы и остается выделенной до ее завершения. Попытка изменить содержимое строкового литерала вызывает неопределенное поведение.
Строка
char *s0 = "hello world";
определяет s0
как указатель на char
с длительностью автоматического сохранения (что означает, что переменная s0
существует только для области, в которой она объявлен) и копирует в него адрес строкового литерала ( 0x00008000
в этом примере). Обратите внимание: поскольку s0
указывает на строковый литерал, его не следует использовать в качестве аргумента какой-либо функции, которая попытается его изменить (например, strtok ()
, strcat ()
, strcpy ()
и т. Д.).
Строка
char s1[] = "hello world";
определяет s1
как массив из 12 элементов char
(длина берется из строкового литерала) с автоматической продолжительностью хранения и копирует содержимое литерала в массив. Как видно из карты памяти, у нас есть две копии строки «hello world»
; разница в том, что вы можете изменить строку, содержащуюся в s1
.
s0
и s1
взаимозаменяемы в большинстве контекстов; вот исключения:
sizeof s0 == sizeof (char*)
sizeof s1 == 12
type of &s0 == char **
type of &s1 == char (*)[12] // pointer to a 12-element array of char
Вы можете переназначить переменную s0
, чтобы она указывала на другой строковый литерал или на другую переменную. Вы не можете переназначить переменную s1
, чтобы она указывала на другой массив.
Это объявление:
char s[] = "hello";
Создает один объект - массив char
размера 6, называемый s
, инициализированный с помощью значения 'h', 'e', 'l', 'l', 'o', '\ 0'
. Где этот массив размещен в памяти и как долго он живет, зависит от того, где появляется объявление. Если объявление находится внутри функции, оно будет жить до конца блока, в котором оно объявлено, и почти наверняка будет размещено в стеке; если он находится вне функции, он , вероятно, будет сохранен в «сегменте инициализированных данных», который загружается из исполняемого файла в записываемую память при запуске программы.
С другой стороны, это объявление :
char *s ="hello";
Создает два объекта:
символов
, содержащий значения 'h', 'e', 'l', 'l', 'o', '\ 0'
, который не имеет имени и имеет статический срок хранения (что означает, что он живет в течение всего срока службы программы); и s
, которая инициализируется местоположением первого символа в этом безымянном массиве, доступном только для чтения. Безымянный доступ только для чтения. массив обычно находится в «текстовом» сегменте программы, что означает, что он загружается с диска в постоянную память вместе с самим кодом. Местоположение переменной-указателя s
в памяти зависит от того, где появляется объявление (как и в первом примере).
s
, которая инициализируется местоположением первого символа в этом безымянном массиве, доступном только для чтения. Безымянный доступ только для чтения. Обычно массив располагается в «текстовом» сегменте программы, что означает, что он загружается с диска в постоянную память вместе с самим кодом. Расположение переменной-указателя s
в памяти зависит от того, где появляется объявление (как и в первом примере).
s
, которая инициализируется местоположением первого символа в этом безымянном массиве, доступном только для чтения. Безымянный доступ только для чтения. Обычно массив располагается в «текстовом» сегменте программы, что означает, что он загружается с диска в постоянную память вместе с самим кодом. Расположение переменной-указателя s
в памяти зависит от того, где появляется объявление (как и в первом примере).
s
, который инициализируется местоположением первого символа в этом безымянном массиве, доступном только для чтения.
Безымянный массив, доступный только для чтения, обычно располагается в "текстовом" сегменте программы, Это означает, что он загружается с диска в постоянную память вместе с самим кодом. Местоположение переменной-указателя s
в памяти зависит от того, где появляется объявление (как и в первом примере).
s
, который инициализируется местоположением первого символа в этом безымянном массиве, доступном только для чтения.
Безымянный массив, доступный только для чтения, обычно располагается в "текстовом" сегменте программы, Это означает, что он загружается с диска в постоянную память вместе с самим кодом. Расположение переменной-указателя s
в памяти зависит от того, где появляется объявление (как и в первом примере).
Во-первых, в аргументах функций они полностью эквивалентны:
void foo(char *x);
void foo(char x[]); // exactly the same in all respects
В других контекстах char *
выделяет указатель, а char []
выделяет массив. Вы спросите, куда девается струна в первом случае? Компилятор тайно выделяет статический анонимный массив для хранения строкового литерала. Итак:
char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;
Обратите внимание, что вы никогда не должны пытаться изменить содержимое этого анонимного массива с помощью этого указателя; эффекты не определены (часто это означает сбой):
x[1] = 'O'; // BAD. DON'T DO THIS.
Использование синтаксиса массива напрямую выделяет его в новую память. Таким образом, модификация безопасна:
char x[] = "Foo";
x[1] = 'O'; // No problem.
Однако массив существует только до тех пор, пока он продолжает свою область видимости, поэтому, если вы делаете это в функции, не возвращайте и не пропускайте указатель на этот массив - вместо этого сделайте копию с помощью strdup ()
или аналогичный. Если массив размещен в глобальной области видимости, конечно, нет проблем.
char s[] = "Hello world";
Здесь s
- это массив символов, который при желании можно перезаписать.
char *s = "hello";
Строковый литерал используется для создания этих блоков символов где-нибудь в памяти. на который указывает этот указатель s
. Здесь мы можем переназначить объект, на который он указывает, изменив его, но пока он указывает на строковый литерал, блок символов, на который он указывает, изменить нельзя.