Что самый безопасный путь состоит в том, чтобы раздать строки в C?

У меня есть программа в C использование Соляриса с ОЧЕНЬ древней совместимостью, которой это кажется. Много примеров, даже здесь на Так, не работают, а также много кода, который я написал на Mac OS X.

Таким образом, при использовании очень строгого C, что самый безопасный путь состоит в том, чтобы передать строки?

Я в настоящее время использую символьные указатели повсеместно, из-за того, что я думал, была простота. Таким образом, у меня есть функции, которые возвращают символ*, я передаю символ* им и т.д.

Я уже вижу странное поведение, как символ* я передал имение его права значения, когда я ввожу функцию и затем значение, заканчивающееся загадочно, ИЛИ повредил/перезаписал после чего-то простого как один printf () или malloc к некоторому другому указателю.

Один подход к функциям, которые я уверен, является неправильным, мог быть:

char *myfunction(char *somestr) {    
  char localstr[MAX_STRLENGTH] = strcpy(localstr, somestr);
  free(somestr);
  /* ... some work ... */
  char *returnstr = strdup(localstr);
  return returnstr;
}

Это кажется... неаккуратным. Кто-либо может указать на меня в правильном направлении на простом требовании?

Обновление

Один пример функции, где я в замешательстве для того, что происходит. Не уверенный, если это достаточно для понимания этого, но здесь идет:'

char *get_fullpath(char *command, char *paths) {
  printf("paths inside function %s\n", paths); // Prints value of paths just fine

  char *fullpath = malloc(MAX_STRLENGTH*sizeof(char*));

  printf("paths after malloc %s\n", paths); // paths is all of a sudden just blank
}
5
задан chucknelson 15 April 2010 в 00:13
поделиться

3 ответа

Хорошо написанный код C придерживается следующего соглашения:

  • Все функции возвращают код состояния типа int , где возвращаемое значение 0 указывает на успех, а -1 указывает на неудачу. В случае сбоя функция должна установить errno с соответствующим значением (например, EINVAL).
  • Значения, которые "сообщаются" функцией, должны сообщаться с использованием "выходных параметров". Другими словами, одним из параметров должен быть указатель на целевой объект.
  • Право собственности на указатели должно принадлежать вызывающей стороне; следовательно, функция не должна освобождать какие-либо из своих параметров, а должна только освобождать объекты, которые она сама выделяет с помощью malloc / calloc .
  • Строки должны передаваться либо как объекты const char * , либо как объекты char * , в зависимости от того, должна ли строка быть перезаписана. Если строка не подлежит изменению, следует использовать const char * .
  • Каждый раз, когда передается массив, который не является строкой с завершающим нулем, должен быть предоставлен параметр, указывающий количество элементов в массиве или емкость этого массива.
  • Когда изменяемая строка / буфер (т. Е. char * ) объект передается в функцию, и эта функция предназначена для перезаписи, добавления или иного изменения строки, необходимо предоставить параметр, указывающий емкость строки / буфера (чтобы разрешить для динамических размеров буфера и во избежание переполнения буфера).

Я должен указать, что в вашем примере кода вы возвращаете localstr , а не returnstr . Следовательно, вы возвращаете адрес объекта в кадре стека текущей функции. Кадр стека текущей функции исчезнет, ​​как только функция вернется. Сразу после этого вызов другой функции, скорее всего, изменит данные в этом месте, что приведет к обнаруженному вами повреждению. Возврат адреса локальной переменной приводит к «неопределенному поведению» и является неверным.

Изменить
На основании вашего обновленного кода (get_fullpath) ясно, что проблема не в вашей функции get_fullpath, а скорее в функции, которая ее вызывает. Скорее всего, переменная paths предоставляется функцией, которая возвращает адрес локальной переменной. Следовательно, когда вы создаете локальную переменную в get_fullpath, она использует то же самое место в стеке, которое ранее занимали пути. Поскольку «paths» является псевдонимом «fullpaths», он в основном перезаписывается адресом буфера, который вы установили с ошибкой, который является пустым.

Редактировать 2
Я создал страницу Соглашения о кодировании C на моем веб-сайте с более подробными рекомендациями, пояснениями и примерами для написания кода C, если вам интересно . Кроме того, утверждение о том, что вместо returnstr возвращается localstr, больше не соответствует действительности, поскольку вопрос был отредактирован в последний раз.

12
ответ дан 18 December 2019 в 10:43
поделиться

Ваш пример "update" завершен? Я бы не думал, что он будет компилироваться: он требует возврата значения, но вы никогда ничего не возвращаете. Вы никогда не делаете ничего will fullpath, но, возможно, это намеренно, возможно, вы просто хотите сказать, что когда вы делаете malloc, другие вещи ломаются.

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

Обновление: чтобы действительно ответить на вопрос

Работа со строками - хорошо известная проблема в C. Если вы создаете массив фиксированного размера для хранения строки, вам придется беспокоиться о том, что длинная строка переполнит выделенное пространство. Это означает постоянную проверку размера строки при копировании, использование strncpy и strncat вместо обычных strcpy и strcat, или другие подобные приемы. Вы можете пропустить это и просто сказать: "Ну, никто никогда не будет иметь имя длиннее 60 символов" или что-то в этом роде, но всегда есть опасность, что кто-то это сделает. Даже при вводе чего-то, что должно иметь известный размер, например, номера социального страхования или ISBN, кто-то может ошибиться и дважды нажать на клавишу, или злоумышленник может намеренно ввести что-то длинное. И т.д. Конечно, в основном это проблема при вводе данных или чтении файлов. Как только у вас есть строка в поле какого-то известного размера, то для любых копий или других манипуляций вы знаете размер.

Альтернативой является использование динамически распределяемых буферов, где вы можете сделать их настолько большими, насколько это необходимо. Это звучит как хорошее решение, когда вы слышите его впервые, но на практике это гигантская боль в C, потому что выделение буферов и их освобождение, когда они вам больше не нужны, доставляет много хлопот. Другой постер здесь сказал, что функция, которая выделяет буфер, должна быть той же самой, которая его освобождает. Хорошее эмпирическое правило, я в целом согласен, но... Что если подпрограмма хочет вернуть строку? Она выделяет буфер, возвращает его и ... как она может освободить его? Никак, потому что смысл в том, что она хочет вернуть его вызывающей программе. Вызывающая сторона не может выделить буфер, потому что не знает его размера. Также невозможны такие, казалось бы, простые вещи, как:

if (strcmp(getMeSomeString(),stringIWantToCompareItTo)==0) etc

. Если функция getMeSomeString выделит строку, конечно, она может вернуть ее, чтобы мы могли сравнить, но теперь мы потеряли хэндл и никогда не сможем его освободить. В итоге приходится писать неудобный код типа

char* someString=getMeSomeString();
int f=strcmp(someString,stringIWantToCompareItTo);
free(someString);
if (f==0)
etc

Так что ладно, это работает, но читабельность только что резко упала.

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

0
ответ дан 18 December 2019 в 10:43
поделиться

Вы не можете вернуть указатель на массив, который размещен локально внутри функции. Как только функция вернется, этот массив будет уничтожен.

Кроме того, когда вы помещаете

char localstr[MAX_STRLENGTH] = strcpy(localstr, somestr);

, происходит то, что strcpy () копирует байты в массив localstr [], но тогда происходит ненужное присваивание. Вы, вероятно, могли бы получить желаемый эффект в виде двух строк, таким образом ..

char localstr[MAX_STRLENGTH];
strcpy(localstr, somestr);

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

Если вы хотите, чтобы функция изменяла строку, общее соглашение выглядит примерно так

// use a prototype like this to use the same buffer for both input and output
int modifyMyString(char buffer[], int bufferSize) {
    // .. operate you find in buffer[],
    //    leaving the result in buffer[]
    //    and be sure not to exceed buffer length
    // depending how it went, return EXIT_FAILURE or maybe
    return EXIT_SUCCESS;

// or separate input and outputs
int workOnString(char inBuffer[], int inBufSize, char outBuffer[], int outBufSize) {
    // (notice, you could replace inBuffer with const char *)
    // leave result int outBuffer[], return pass fail status
    return EXIT_SUCCESS;

Отсутствие встраивания malloc () или free () внутрь также поможет избежать утечек памяти.

4
ответ дан 18 December 2019 в 10:43
поделиться
Другие вопросы по тегам:

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