2015-12-30 5 views
2

У меня есть следующая функция, которая изначально выполняет некоторую проверку параметров функции.C++ const char * to char *

char *doSomething(const char* first, const char* second) { 

    if((first == nullptr || *first == '\0') && (second == nullptr || *second == '\0')) { 
     return nullptr; 
    } else if (first == nullptr || *first == '\0') { 
     return (char *) second; 
    } else if (second == nullptr || *second == '\0') { 
     return (char *) first; 
    } 
    //doSomething 
} 

ли отливку параметр функции возвращает новый char*, который указывает на другую область памяти? Я не хочу, чтобы кто-то использовал эту функцию для управления значением, на которое указывают постоянные параметры. Я хотел бы, чтобы новый char* был возвращен с тем же значением, что и один из параметров, если он равен nullptr или пуст.

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

+1

И затем я называю 'doSomething (« привет »,« мир! ») [0] = 'X';' и стрела! SIGSEGV. (ответ «нет», вам придется «strncpy()» эти строки. – YSC

+0

Если вы не хотите, чтобы кто-то, использующий эту функцию, манипулировал значением, которое константные параметры указывают на return by 'const char *', или return _by value_. Функция выполняет неявное преобразование 'const_cast'. – Ziezi

+0

Вы хотите, чтобы вы вернули изменчивую копию строки? В противном случае, почему бы вам просто не возвратить' const char * '? – rici

ответ

5

Нет, он не делает никакого нового объекта, он просто отбрасывает const в область памяти, которую вы объявили неизменной. Какой обычно приводит к ужасному неопределенному поведению, но если они исходят из указателя const, вы будете в порядке (EDIT - благодаря @anatolyg).

+0

@anatolyg Спасибо , вы правы. Соответственно, отредактированный ответ –

+0

Я бы утвердил, что не только * обычно *, но и всегда * 'const_cast' просто отлично, он никогда не приводит к UB. * Запись на объект * (не чтение!) через результирующий указатель может привести к UB при определенных обстоятельствах. Один из них (единственный?) состоит в том, что объект находится в постоянной памяти, как литерал строки символов в современных реализациях, но это довольно редко: более распространенный случай является то, что rameter объявлен const, чтобы указать намерение функции не изменять его, но эта функция вызывает другую, которая не объявляла свой параметр param, чтобы он был отличен. –

+0

@ PeterA.Schneider Конечно, но в общем случае вы не можете сказать, будет ли или нет, что теперь не -const' будет записано или нет. UB - UB независимо от памяти только для чтения или иначе. –

2

Данные не копируются автоматически.

Вы также должны вернуть const char*. Поведение при отбрасывании const и попытка изменить исходную строку символов через этот указатель не определены, если исходные данные были первоначально const: например, строковый литерал.

Сделайте глубокую копию вне функции, если вам нужен указатель char*.

еще лучше, бен все это и передать std::string либо const указатель, если nullptr все еще разрешено или const ссылки, если это не так.

+0

Здесь тоже должно быть nitpicking: поведение при отбрасывании const всегда * un * undefined. Это всегда приводит к действительному указателю на исходный объект. «Действительный» означает, что, например, это значение адреса не будет ловушку, если положить его в регистр. Поскольку он указывает на исходный объект, этот объект всегда можно читать без UB. Единственное, что есть UB, - это * запись * на такой объект, если он действительно находится в памяти только для чтения (например, строковый литерал в современных реализациях). Чтение из такого литерала с помощью неконстантного указателя прекрасно. –

+0

@ PeterA.Schneider: Очень верно, я исправил. – Bathsheba

0

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

Если это плохо, вы можете вернуть const char *, чтобы вызывающий абонент не мог его изменить или вообще не возвращать.

Булева переменная может сделать ваш код более понятным и избежать дублирования кода.

0

Значение этого значения не копируется. Если вы хотите скопировать его, вам придется пройти по значению, передача указателем или ссылкой позволит пользователю манипулировать возвращаемым значением (если это не const).

Я не понимаю, почему вы беспокоитесь о том, что пользователь манипулирует параметрами, поскольку они эффективно постоянны.

И что касается нулевых проверок, использование bool не требуется. Нулевые чеки дешевы в использовании (с точки зрения производительности).

2

Стандарт 2011 говорит (5.2.11/3):

Результат указателя const_cast относится к исходному объекту.

Это довольно просто и отвечает на ваш первоначальный вопрос.

Он также говорит (5.2.11/7):

[Примечание: В зависимости от типа объекта, операция записи через указатель, или указатель-значение для элемента данных в результате чего из const_cast, который отбрасывает спецификатор const, может привести к неопределенному поведению (7.1.6.1). -end note]

Это означает, что иногда можно даже писать объект через новый указатель. Примеры, когда это не нормально, - это указатели на объекты, которые находятся в постоянной памяти. Это укусило людей с помощью указателей на строковые литералы и часто случается с константами во встроенных системах. Сам бросок является, насколько я вижу, никогда не определяемым поведением.

Что касается Вашего вопроса обзора кода:

  • Как другие говорили, все средства вернуть константный символ.
  • Выполняет ли вызывающий объект что-либо в результате? Если нет, верните bool, указывающий на успех или неудачу, или int для нескольких режимов отказа.
  • Если вызывающий абонент использует возвращаемый указатель char, что вы возвращаете для указания успеха? Три возможных случая ошибок используют сразу доступные значения возврата. Рассмотрите возможность возврата кода ошибки и передачи фактического результата указателя char в «параметр out» (указатель на указатель char).
0

Я хотел бы использовать один логический флаг, чтобы избежать код дубликата и сделать ее менее подверженной:

bool secondIsEmpty = second == nullptr || *second == '\0'; 
if(first == nullptr || *first == '\0') 
    return secondIsEmpty ? nullptr : second; 
else 
    if(secondIsEmpty) 
     return first; 

Для char *, вы должны изменить тип возврата к const char * и удалить C бросание из кода (вы должны использовать const_cast <> в любом случае). Если вам нужно вернуть изменчивую строку из этой функции, что делает ее более сложной. Вы либо должны принимать изменяемые строки в качестве параметров, либо создавать буфер где-то. В этом случае вы, вероятно, должны вернуть умный указатель на строку, выделенную new или даже лучше использовать std::string и вернуть его по значению.

0

(Этот ответ в основном о побочном вопросе о том, как сделать код лучше)

Как люди здесь отметили, вы, вероятно, хотите вернуться const char*:

const char *doSomething(const char* first, const char* second) 
{ 
    ... 
} 

Это позволит получить избавиться от слепков: идея

const char *doSomething(const char* first, const char* second) 
{ 
    if (...) 
    { 
     return nullptr; 
    } 
    else if (...) 
    { 
     return second; 
    } 
    else if (...) 
    { 
     return first; 
    } 
} 

вашего кода находится примерно:

Найти и вернуть непустую строку, с предпочтением second one; если их нет, верните nullptr.

Это может быть легко представлена ​​следующим образом:

const char *FindNonEmpty(const char* first, const char* second) 
{ 
    if (second && *second) 
     return second; 
    else if (first && *first) 
     return first; 
    else 
     return nullptr; 
} 

Я использовал указатели и байты здесь в логическом контексте.Это вопрос вкуса; вы, вероятно, захотите использовать явные сравнения; однако некоторые люди утверждают, что использование указателя как булева более читаемо, чем сравнение его с nullptr.