Почему должен, указатель на массив символов нуждаться в strcpy для присвоения символов его массиву и присвоению двойных кавычек не будет работать?

Я работал на 3 компании до сих пор и только что недавно начал писать "offical" NUnit тесты, и я должен сказать, что я - большой поклонник. Существует 2 точки, которые я заметил однако за последние несколько недель:

  1. Это зависит от приложения - существуют определенные приложения/части приложения, которое может быть очень легко к модульному тесту. Функция, которая делает что-то в основном тот же каждый раз и приводит к легко распознаваемому результату. И существуют разделы, которые не делают этого: блок кода, который печатает кристаллические отчеты или изменяет изображение - здесь глазное яблоко, на самом деле необходим и вероятно лучше/быстрее. Эти области могут отговорить людей пробовать к тестам записи. Они затем только начинают думать это, если определенные области не могут быть протестированной единицей, почему беспокойство?

  2. гранулярность теста - много DEVs/QAs уже имеют в распоряжении своего рода автоматизацию, чтобы 'протестировать' часть функциональности и запутаться относительно того, как это отличается от модульного теста. В зависимости от их теста это не могло бы быть. Важная вещь здесь, которая занимает время для понимания, состоит в том, что хороший модульный тест полностью детализирован. Это тестирует минимальную часть логики, которая имеет смысл, который делает это повторяемым, автоматическим, и надо надеяться всегда допустимым. Это взяло меня немного, чтобы действительно ценить, как полезный то есть, особенно когда Вы имеете огромную кодовую базу и любите выполнять регрессионные тесты после внесения изменения, чтобы видеть если Вы просто foobared целое приложение.

10
задан Omar 16 October 2009 в 12:35
поделиться

9 ответов

Потому что char * не является строкой. Это просто указатель на какой-то символ с условием, что могут быть другие символы, а после последнего идет '\ 0' .

Строковый литерал в C (и, следовательно, в C ++), такой как «abc» , представляет собой просто массив символов, а компилятор молча добавляет '\ 0' . Когда вы назначаете массив указателю, массив молча преобразует указатель в первый элемент. В результате

at = "tw";

означает, что указателю на назначен адрес первого символа в строковом литерале «tw» . Тем самым он потеряет свою старую ценность. Поскольку это был адрес динамически выделяемого массива символов, вы теряете этот массив.

Когда вы позже назначаете символ в массиве , на который теперь указывает , вы присваиваете новое значение некоторому символу в строковом литерале. Это вызывает неопределенное поведение и немедленное зависание или сбой программы, вероятно, лучшее, что может случиться с вами, когда вы это сделаете. (На многих платформах вы выполняете запись в постоянную память.)

Позже вы передаете в в delete [] не delete , поскольку вы назвали новый [] , а не новый ). При этом вы передаете ему адрес строкового литерала вместо выделенного массива символов. Это, конечно, испортит диспетчер кучи. (Библиотека времени выполнения VC улавливает это в режиме отладки.)

std :: strcpy , с другой стороны, копирует строковый символ за символом из одного массива в другой массив. Никакие указатели не меняются, копируются только куски памяти. Указатель на целевой массив после этого все еще указывает на целевой массив, только данные в этом массиве изменились.

Позвольте мне добавить следующее: Как новичок в C ++, вы должны использовать std :: string , а не строки C. Это делает всю грязную работу за вас и имеет разумную семантику.

12
ответ дан 3 December 2019 в 13:37
поделиться

Вы ошиблись в двух вещах: заставляете указатель указывать на что-то другое (это то, что делает присваивание) и копируете некоторые данные в место, указанное указателем.

at = "tw";

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

char *at = new char [3];
strcpy(at,"t");

этот код выделяет память для трех символов и заставляет в указывать на эту часть памяти (строка 1), а затем копирует некоторые данные в память, указанную ] в .

И помните, что память, выделенная с помощью new [] , должна быть освобождена с помощью delete [] , а не delete

Я вам советую чтобы узнать больше об указателях. Это обсуждение охватывает это.

0
ответ дан 3 December 2019 в 13:37
поделиться

Когда вы делаете

char *at = ...;

at = "hello";

, вы в основном перезаписываете значение указателя (т. Е. Адрес памяти, выделенной для вас new [] ) с адресом статическая постоянная строка. Это означает, что когда вы позже удаляете из этой памяти, вы передаете delete указатель, который ранее не возвращался new .

Это плохо для

В C и C ++ назначения указателям обычно ничего не делают с памятью, на которую указывает указатель, они изменяют сам указатель. Это может сбивать с толку, если вы привыкли к языку, в котором строки больше относятся к «гражданам первого сорта».

Кроме того, вы должны использовать delete [] , если вы использовали new [] .

14
ответ дан 3 December 2019 в 13:37
поделиться

Необходимо понять 3 вещи:

1) char * at; - это просто переменная-указатель.
Переменная-указатель просто означает, что она содержит адрес памяти.

2) new char [3] возвращает начальный адрес памяти, выделенной в куче.

3) «hello» возвращает адрес строкового литерала.

char *at = new char [3];
//at now contains the address of the memory allocated on the heap


at = "hello";
//at now contains the address of the static string. 
// (and by the way you just created a 3 byte memory leak)


delete[] at; 
//WOOPS!!!! you can't do that because you aren't deleting 
// the original 3 chars anymore which were allocated on the heap!
//Since at contains the string literal's memory address you're 
// trying to delete the string literal.

Примечание об изменении постоянной памяти для чтения:

Также вы никогда не должны изменять строковый литерал. Т.е. этого никогда не следует делать:

char *at = "hello";
at[2] = '\0'; 

Память для строковых литералов должна быть доступна только для чтения, и если вы ее измените, результаты не будут определены языком C ++.

Поскольку вы используете C ++:

Поскольку вы используете C ++, рассмотрите возможность использования вместо него типа std :: string .

#include <string>

using namespace std;

int main(int argc, char **argv)
{
  string s = "hello";
  s += " world!";

  //s now contains "hello world!"

  s = "goodbye!";

  //Everything is still valid, and s contains "goodbye!"


  //No need to cleanup s. 

  return 0;
}
9
ответ дан 3 December 2019 в 13:37
поделиться

Не забывайте использовать

delete []

всякий раз, когда вы выделяете что-то с помощью [].

5
ответ дан 3 December 2019 в 13:37
поделиться

Указатель содержит адрес. Оператор = для указателя изменяет удерживаемый адрес.

at = "tw";

Указывает на массив "tw" (массив, созданный компилятором для хранения символов tw), он больше не указывает на массив, созданный с помощью new. создается в файле.

at[2] = '\0';

Добавляет NULL в конец массива компилятора.

4
ответ дан 3 December 2019 в 13:37
поделиться

В первом примере вы меняете значение, во втором вы меняете значение того, на что указывает. Присвоение символа char * строке с двойными кавычками назначает его статическому указателю на константу.

В частности, в первом примере at now указывает на другое место в памяти.

0
ответ дан 3 December 2019 в 13:37
поделиться

В вашем первом примере вы выделяете некоторую память и указываете на нее с помощью переменной «at». Когда вы выполняете

at = "tw"

, вы фактически перенаправляете символ * на постоянную строку символов. Это вызывает утечку памяти. Когда вы продолжаете удалять «at», вы пытаетесь удалить стековую память.

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

0
ответ дан 3 December 2019 в 13:37
поделиться

В первом примере вы вызвали утечку памяти.

Ваша переменная в равна указатель на адрес памяти, а не на саму строку. Когда вы назначаете указателю адрес «tw» , вы теряете исходный адрес, который вы получили с помощью new . at теперь указывает на адрес, который вы не выделили с помощью new , поэтому вы не можете удалить его.

Если вы думаете об указателях как о целых числах, это вероятно будет иметь больше смысла. Я назначил произвольные числа в качестве адресов для обсуждения.

char *at = new char[3];    // 0x1000
at = "tw";                 // 0x2000
at[2] = '\0';              // set char at 0x2002 to 0
delete at;                 // delete 0x2000 (whoops, didn't allocate that!)
0
ответ дан 3 December 2019 в 13:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: