Можно ли объединить строковый литерал и символьный литерал? - PullRequest
4 голосов
/ 11 марта 2009

Почему name плохо работает в следующем коде C ++?

string name =  "ab"+'c';

Как будет вести себя эквивалентный код в Java / C #?

Ответы [ 8 ]

13 голосов
/ 11 марта 2009

Попробуйте

std::string name = "ab" "c";

или

std::string name = std::string("ab") + c;

В C ++ «ab» - это не std::string, а скорее указатель на строку символов. Когда вы добавляете целое значение к указателю, вы получаете новый указатель, который указывает дальше по строке:

char *foo = "012345678910121416182022242628303234";
std::string name = foo + ' '; 

name устанавливается на «3234», поскольку целочисленное значение '' составляет 32, а 32 символа после начала foo - это четыре символа перед концом строки. Если бы строка была короче, вы пытались бы получить доступ к чему-либо на неопределенной территории памяти.

Решением этой проблемы является создание строки std: из массива символов. std: строки позволяют добавлять символы к ним, как ожидалось:

std::string foo = "012345678910121416182022242628303234";
std::string name = foo + ' '; 

name устанавливается на "012345678910121416182022242628303234"

8 голосов
/ 11 марта 2009

Проблема в том, что "ab" - это не C ++ std::string, а const char[3]. Таким образом, оператор +, который он ищет, это operator+ (const char[3], char). Этого не существует, поэтому компилятор пытается позволить массиву распадаться на указатель, поэтому он ищет operator+ (const char*, char). Это существует, поэтому компилятор выбирает это, но делает не то, что нужно. Добавление целочисленного значения (char) к указателю (const char *) является достаточно распространенной операцией, и, очевидно, это то, что делает этот оператор +. Ключом к пониманию этого является понимание того, что первый аргумент - это 1) массив и 2) указатель, когда массив не имеет смысла. Да, она также использовалась как строка в C, но это не строка. Это указатель (или иногда массив).

Существует operator+ (const std::string&, char), который объединяет, но компилятор даже не ищет его, потому что первый аргумент не является std :: string.

Поэтому решение состоит в том, чтобы вручную создать строку:

string name = std::string("ab")+'c';

Теперь компилятор может определить правильный оператор + для вызова.

5 голосов
/ 11 марта 2009

В C ++ компилятор ищет функцию с этим прототипом:

T operator+ (const char*, char);

Поскольку его нет, он не может понять, что такое T, и не может разрешить вызов operator<<, поэтому он прибегает к единственному оставленному решению: добавлению указателя. В ответе Джоша нет проблем с привязкой к строке, потому что для нее существует функция.

3 голосов
/ 11 марта 2009

Учитывая код C ++:

std::string name =  "ab"+'c';

Эквивалент в Java:

String name = "ab".substring('c');

Оба продвигают char к int. Конечно, в Java он проверяет диапазон и, следовательно, выдает исключение. В C ++ вы просто получаете неопределенное поведение (или что-то подобное).

2 голосов
/ 11 марта 2009

Java:

public class Main
{
    public static void main(String[] args)
    {
        System.out.println("AB" + 'c');
    }
}

Вывод:

ABc

Редактировать:

На самом деле компилятор жестко кодирует String ABc ...

Если вы делаете «AB» + argv [0] .charAt (0); чтобы заставить его использовать переменную, компилятор делает это (в основном):

StringBuilder b = new StringBuilder;
b.append("AB");
b.append(argv[0].charAt(0));
System.out.println(b.toString());
1 голос
/ 12 марта 2009

Ну, то, что я обычно делал бы в C ++, это

имя строки = строка ("ab") + 'c';

Помните, что литерал "ab" является , а не типа string. Вы надеялись, что между массивами символов и символами не работает знак «+», а затем надеялись, что компилятор каким-то образом заметит, что вы действительно хотите, чтобы результат был std :: string, и затем проанализировал свое выражение на правая часть для некоторой комбинации неявных преобразований, которые могут объединяться с оператором (ами) для получения результата этого типа. Для меня это довольно высокий заказ.

Независимо от того, это не имеет значения. Видите ли, в Си единственная разница между массивом и указателем заключается в том, как распределяется их память. Если у вас есть один, у вас по сути есть «массив / указатель». Таким образом, «+» - это оператор, определенный во всех массивах и указателях, который принимает другой аргумент любого целочисленного типа и выполняет математическую обработку указателей, возвращая указатель на то множество элементов, прошедших через эту точку. Кроме того, в C "char" на самом деле просто еще один тип целочисленного типа. Эти решения по дизайну C были полезны взломы , но, как часто случается с взломами, они сочетаются с интуитивно неожиданными результатами. Таким образом, все, что «ab» + «c» делает для вас, - это возвращать адрес на 99 байт раньше, где бы ни находился литерал «ab» в памяти.

Иногда вы можете положиться на неявные преобразования, но вы действительно должны быть готовы помочь вашему компилятору немного в другое время.

1 голос
/ 11 марта 2009

Компилятор C ++ автоматически не объединяет строковые литералы с символьными литералами. Но он объединит строковые литералы друг с другом. Синтаксис такой:

const char * cs = "ab" "c"; // append string-literals

Как уже упоминалось, string не является встроенным языком C ++. Но в стандартной библиотеке C ++ есть тип string. Вот несколько примеров использования:

#include <string>
const char * cs = "ab" "c";
std::string s1( cs );
std::string s2( "ab" "c" );
std::string s3 = "ab" "c";
1 голос
/ 11 марта 2009

Вы можете объединять строки и символы в C # - я думаю, это не так строго, как в C ++.

Это прекрасно работает в C #:

string test = "foo" + 'b';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...