Почему strncpy копирует больше чем надо
Перейти к содержимому

Почему strncpy копирует больше чем надо

  • автор:

strncpy в C/C++: разбираемся с примерами

�� Привет! В этой статье мы поговорим о функции strncpy . С помощью этой функции мы можем копировать Си-строки из одного места в другое. Отличие этой функции от strcpy заключается в том, что она копирует только первые n байтов. В начале мы посмотрим на несколько примеров использования этой функции. В конце вы найдете упражнения для закрепления материала.

Иллюстрация strncpy

Как скопировать Си-строку с помощью strncpy

Функция strncpy объявлена в заголовочном файле . В C++ вы можете использовать . Выглядит эта функция следующим образом:

char * strncpy ( char * destination, const char * source, size_t num );
  • Первый аргумент — это указатель на строку, в которую будет производиться копирование.
  • Второй аргумент — это строка, которую нам нужно скопировать.
  • Третий аргумент указывает максимальное количество байтов, которые нужно скопировать (символ \0 не влияет на этот лимит).
  • Функция возвращает указатель на скопированную строку destination (первый аргумент).

Функция будет копировать символы до тех пор, пока не встретит символ окончания строки \0 , или пока не скопирует num байтов:

#include #include int main()  char source[] = "string"; char destination[4]; char* result = strncpy(destination, source, 3); printf("%s, %s, %s", result, destination, source); return 0; >

Вывод этой программы:

str, str, string

Как видите, в результате мы получили «str» в result и destination . Также хочу заметить, что строка destination занимает не три, а четыре байта. strncpy добавляет символ окончания строки \0 в конце. Для вывода строк на экран, мы используем функцию printf .

Что произойдет, если num в функции strncpy больше длины исходной строки?

char src[] = "hello"; char dest[10]; strncpy(dest, src, 8); printf("%s", dest);

Программа выведет “hello” и затем 3 случайных символа.
Программа выведет “hello” и затем 3 пробела.
Программа выведет только “hello”.
Программа выведет ошибку.

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

Как реализовать strncpy самому

Давайте попробуем сами реализовать эту функцию, чтобы лучше ее понять:

#include char* my_strncpy(char* destination, const char* source, size_t num)  size_t i = 0; // начинаем копирование с начала while (source[i] != '\0' && i  num)  // копируем пока не упремся в \0 или // не скопируем нужно количество байт destination[i] = source[i]; // копируем байт i++; // двигаемся вперед > destination[i] = '\0'; // нужно не забыть пометить конец строки return destination; > int main()  char source[] = "string"; char destination[7]; char* result = my_strncpy(destination, source, 3); printf("%s, %s, %s", result, destination, source); return 0; > 
str, str, string

Как видите, наша версия функции работает не хуже стандартной!

  1. Использование strncpy :
    Напишите программу на C++, которая запрашивает у пользователя две строки и число n . Программа должна использовать функцию strncpy для копирования первых n символов из одной строки в другую и затем выводить результат. Ваша программа также должна выводить исходные строки пользователя.
  2. Создание своей версии strncpy :
    Используя предоставленный в статье пример, создайте свою версию функции strncpy . Проверьте ее работу, используя различные строки и значения num .
  3. Сравнение вашей версии strncpy с стандартной:
    Напишите программу, которая сравнивает работу вашей версии функции strncpy с работой стандартной функции. Ваша программа должна использовать обе функции и выводить результаты их работы на экран.

Читайте также

strlen в C/C++

Функция strlen используется в языке программирования C для подсчета количества символов в строке. В статье будут разобраны примеры использования этой функции. Также мы реализуем эту функцию сами, а в конце статьи есть упражнения для закрепления материала.

string в C++

В этом уроке вы узнаете, как работать со строками в C++. Также, если вы хотите узнать, какие функции есть у `std::string` и как ими пользоваться — то вам сюда.

string::size в C++

C++ предлагает разнообразные инструменты для работы со строками, и одна из самых важных функций — это string::size. В этой статье мы подробно рассмотрим эту функцию, поймем, как она измеряет строки, и исследуем ее практическое применение.

sprintf в C/C++

Функция sprintf — это близкий аналог функции printf. Основное отличие между ними заключается в том, что sprintf не выводит отформатированную строку на экран, а записывает ее по указанному адресу. Эта функция часто используется в C/C++ и рекомендуется к изучению.

Функция puts используется в языке программирования C для вывода строки на экран используя стандартный вывод. В статье будут разобраны примеры использования этой функции. Также мы реализуем эту функцию сами, а в конце статьи есть упражнения для закрепления материала.

snprintf в C/C++

Функция snprintf используется в языках C и C++ для форматирования строки без вывода ее на экран. В статье будут разобраны примеры использования этой функции. Также в конце статьи есть упражнения.

printf в C/C++

Функция printf — это швейцарский нож для вывода на консоль в C/C++. Из-за этого эта функция поддерживает огромное количество разных флагов и модификаторов для стилизации вывода как вам угодно. В этой статье мы разберемся на примерах, как же ей, все-таки, пользоваться.

string::append в C++

Язык программирования C++ предлагает множество способов работы со строками. Одна из самых распространенных операций — это добавление одной строки к другой. В этом руководстве мы подробно рассмотрим функцию string::append, ее особенности и преимущества.

strcmp в C/C++

Функция strcmp используется в языках программирования C для сравнения двух строк. В статье будут разобраны примеры использования этой функции. Также мы реализуем эту функцию сами, а в конце статьи есть упражнения для закрепления материала.

string::substr в C++

C++ предлагает различные методы работы со строками. Одной из таких функций является substr, которая позволяет извлекать определенные части строки. В этой статье мы рассмотрим функцию substr, узнаем, как она работает, и посмотрим на ее многочисленные применения.

string::find в C++

C++ предоставляет множество способов поиска в строках. Метод string::find — один из наиболее часто используемых методов для поиска подстрок. В этой статье вы узнаете, как эффективно использовать этот метод, поймёте его поведение на различных примерах и даже напишете простую собственную функцию поиска в строке.

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

Функция atoi в C++

Функция atoi позволяет получить число из строки. В данной статье вы узнаете, как использовать данную функцию, возможные проблемы, а также реализуете аналог данной функции сами. Закрепить изученный материал нам помогут упражнения в конце статьи.

Векторы в C++

В этом уроке вы узнаете, что такое вектор в C++, а также если вы хотите узнать, как правильно пользоваться и какие функции к им применять — то вам сюда.

vector::push_back в C++

Язык C++ предоставляет различные способы работы с данными. Функция push_back — это популярный метод добавления элементов в вектор. В этой статье мы подробно рассмотрим эту функцию, разберемся, как и когда её использовать, и обсудим некоторые интересные моменты, связанные с ней.

map::count в C++

C++ предлагает множество функций для работы с контейнерами. Одна из таких функций, count, позволяет узнать, существует ли ключ в std::map. В этой статье вы узнаете, когда она может вам пригодится.

vector::size в C++

В C++, контейнер std::vector представляет собой динамический массив, который может автоматически изменять свой размер. Часто возникает необходимость определить, сколько элементов на данный момент содержит vector. Встречайте функцию size! В этой статье мы рассмотрим, как работает функция size, покажем практические примеры и расскажем о других полезных функциях.

В данном уроке пойдет речь о функции pow в C++. Мы рассмотрим с примерами, как возвести число в степень, а также какие есть альтернативы у данной функции.

Функция itoa в C++

Функция itoa позволяет сконвертировать число в строку. itoa не является частью стандарта C++, поэтому мы разберем как использовать данную функцию когда она есть в вашем компиляторе, а потом реализуем данную функцию сами.

Функция find в C++
Разберемся в функции find из C++ и научимся использовать ее с такими контейнерами, как vector и set.
Стек (stack) в C++

В этой статье вы познакомитесь с структурой данных стек. Мы разберем что это за зверь и как им пользоваться в своей программе. Также мы изучим стек через массив.

Функция exp используется в языках C и C++ для вычисления экспоненты. В статье будут разобраны примеры использования этой функции. Также в конце статьи есть упражнения.

В этом уроке вы познакомитесь с функцией log в C++. Данная функция позволяет нам получить натуральный логарифм числа. Мы разберемся с тем как работает функция log в теории и закрепим изученное на практике.

Функция rand в C++

Язык C++ имеет несколько способов генерации случайных чисел. Функция rand — один из самых популярных способов получить случайное число. В этой статье мы научимся использовать эту функцию, генерировать случайные числа в диапазоне, а также реализуем свой простенький генератор случайных чисел.

Функция srand используется в языке программирования C++ для инициализации генератора случайных чисел. В статье будут разобраны примеры использования этой функции. В конце статьи есть упражнения для закрепления материала.

strncpy ошибка во время выполнения

компилятор только ворнингом предупреждает о том что функция древняя, и во время выполнения происходит ошибка
Unhandled exception at 0x0048f0f8 in APL.exe:0xC00000005:Access violation writing location 0x004ff8f4
и открывает strcpy.asm тыкая в строчку выделенную стрелкой

copy_tail_loop: mov al,byte ptr [esi] ; load byte from source add esi,1 => mov [edi],al ; store byte to dest add edi,1 test al,al ; EOS found? je short fill_tail_zero_bytes ; '\0' was already copied sub ebx,1 jnz copy_tail_loop

особого опыта общения со строками у меня нет, догадок почему это происходит и что с ним делать тоже нет, чтозанафик, как решать?

#1
20:37, 25 сен 2010

«qwertyuiop» — это константа

#2
20:51, 25 сен 2010

Блин. попытался ответить.
Вчитался в код. кокойужос. удалил свой ответ.
Не пиши сюда больше, пока не прочитаешь приличную книгу по С.

#3
21:32, 25 сен 2010

_vasa_
> «qwertyuiop» — это константа
спасибо за подсказку

kvakvs
> Не пиши сюда больше, пока не прочитаешь приличную книгу по С.
такого нет в правилах форума

#4
22:35, 25 сен 2010

unknown
твои ошибки:

_Text = (char*)malloc(strlen("qwertyuiop")+1);

создаешь лишнюю копию строки «qwertyuiop» в виде литерала — не ошибка, но плохой стиль и лишний расход памяти, если компилер не соптимизирует

_Text = "qwertyuiop";

ты думаешь, что инициализируешь строку, а на деле меняешь указатель _Text на указатель на строку «qwertyuiop», заданный при компиляции, ранее распределенная область памяти остается без указателя и ее не освободишь до закрытия программы — это наз-ся «утечка памяти».
строка с malloc получается лишней
или сразу присвой указателю строку или скопируй через strcpy, если надо именно копию данных для изменений

while(_Text[i]!= '\0')
while(_Text[i])

еще есть компактная запись

_Text = strncpy(_Text,_Text2,2)

ты в цикле меняешь и _Text (указатель на начало) и i (смещение)
что получится не хочу гадать
а что хочешь получить?

Вообще сишными char* «строками» пользоваться очень неудобно
Смотри std::string, cstring итд

ты копируешь по 2 символа. очевидно выходишь за пределы строки

#5
23:14, 25 сен 2010

функция malloc не заполняет выделенную память нулями там может оказаться мусор.
Надо явно в конец строки записать «\0».
_Text = «qwertyuiop\0»;

PS результат будет «11ertyuiop» действительно странный код 🙂

#6
23:25, 25 сен 2010

Aslan
Конкретно в оп-примере нет выхода за конец строки. Он копирует один раз, и в начало буффера, размера которого хватает для двух байт.

Но мне вот стало интересно.

char * pBuffer_1 = "qwerty"; char pBuffer_2[] = "qwerty";

Казалось бы, одинаковые вещи. Но нет.
Ошибка, насколько я понимаю, в том, что pBuffer_1 — это указатель на константные данные, компилятор плюсов разрешает код, который пытается записать что-то по этому указателю (особенности работы со строками), код при запуске падает. Нельзя писать в константу.

И я тут подумал, а если взять и обмануть? Типо такого: (пришлось упростить строку до символа из-за ссылки на обьект)

const char * pBuff = "q"; // указатель на вредный обьект const char & pBuff2 = *pBuff; // ссылка на обьект char & pBuff3 = const_cast(pBuff2); // убираем константу у ссылки pBuff3 = 'r'; // меняем значение символа . падаем . writing error

И теперь у меня вопрос:

char * pBuffer_1 = "qwerty";

msdn говорит, что «const_cast . blablabla. Depending on the type of the referenced object, a write operation through the resulting pointer, reference, or pointer to data member might produce undefined behavior.» ok
Но

"qwerty"

— это же обычный блок памяти. Ладно, не обычный, его создает компилятор при старте и прячет где-то в ресурсах. Но, блин, это же все равно блок памяти. Почему его нельзя изменить?

#7
0:06, 26 сен 2010

0xdeadc0de
> Почему его нельзя изменить?
Потому что он физически может лежать в страничке, защищенной от записи.

Почему strncpy копирует больше чем надо

#include
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);

ОПИСАНИЕ

Функция strcpy() копирует строку (включая завершающий нулевой байт ‘\0’), указанную в указателе src, в буфер, указанный в указателе dest. Строки не могут пересекаться, а строка в назначении dest должна быть достаточно большой, чтобы принять копию. Берегитесь переполнения буфера (См. ОШИБКИ)! Функция strncpy() сходна, за исключением того, что скопировано будет только первые n байт из src. Внимание: если в первых n байт из src не окажется нулевого байта, то строка в dest также не будет завершена нулевым байтом. Если длина src меньше n, strncpy() дописывает в dest нулевые байты, чтобы убедиться, что всего было записано n байт. Простейшей реализацией strncpy() может быть:

char * strncpy(char *dest, const char *src, size_t n)

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Функции strcpy() и strncpy() возвращают указатель на скопированную строку dest.

АТРИБУТЫ

Описание терминов данного раздела смотрите в attributes(7).

Интерфейс Атрибут Значение
strcpy(), strncpy() безвредность в нитях безвредно (MT-Safe)

СООТВЕТСТВИЕ СТАНДАРТАМ

POSIX.1-2001, POSIX.1-2008, C89, C99, SVr4, 4.3BSD.

ЗАМЕЧАНИЯ

Некоторые программисты считают strncpy() неэффективной и склонной к ошибкам. Если программист точно знает (в том числе с помощью проверок в коде), что размер dest больше src, то strcpy() может быть использована. Единственным правильным (и предполагаемым) использованием strncpy() является копирование C-строки в буфер фиксированной длины с одновременной проверкой того, что буфер не переполнен и неиспользованные байты в целевом буфере обнулены (для исключения возможной утечки информации в случае, если буфер будет записан на носитель или передан другому процессу через межпроцессовое общение). Если в первых n байт src не будет завершающего нулевого байта, strncpy() создаст ничем не ограниченную строку в dest. Если размер buf равен buflen, вы можете принудительно завершить строку с помощью следующего кода:

strncpy(buf, str, buflen - 1); if (buflen > 0) buf[buflen - 1]= '\0';

Конечно же, вышеприведенный пример не учитывает того, что если размер src больше buflen — 1 байт, при копировании в dest будет потеряна часть информации.

strlcpy()

В некоторых системах (BSD, Solaris и других) есть следующая функция:
size_t strlcpy(char *dest, const char *src, size_t size); Эта функция подобна strncpy(), но копирует не более size-1 байт в dest, всегда добавляет конечный байт null и не дополняет назначение (помимо этого) байтами null. В данной функции исправлены некоторые проблемы strcpy() и strncpy(), но вызывающий по-прежнему должен обрабатывать возможность потери данных, если значение size окажется слишком мало. Возвращаемое значение функции — длина src, что позволяет легко обнаружить усечение: если возвращаемое значение больше или равно size, то выполнялось усечение. Если потеря данных неприемлема, то вызывающий должен или проверять аргументы перед вызовом, или проверять возвращаемое значение. Функция strlcpy() отсутствует в glibc и не стандартизована в POSIX, но доступна в Linux из библиотеки libbsd.

ДЕФЕКТЫ

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

Почему strncpy копирует больше чем надо

Увеличил буфер до 32 К:
Microsoft Windows XP [Версия 5.1.2600]
(С) Корпорация Майкрософт, 1985-2001.

C:\Documents and Settings\. \Test\Release>test
strncpy: 36656 msec
my_strncpy: 36844 msec

C:\Documents and Settings\. \Test\Release>test
strncpy: 36703 msec
my_strncpy: 36875 msec

Re[6]: fast strncpy

От: sokel
Дата: 15.01.08 09:12
Оценка:

Здравствуйте, Vlad_SP, Вы писали:

V_S>Увеличил буфер до 32 К:
V_S>Microsoft Windows XP [Версия 5.1.2600]
V_S>(С) Корпорация Майкрософт, 1985-2001.

V_S>C:\Documents and Settings\. \Test\Release>test
V_S>strncpy: 36656 msec
V_S>my_strncpy: 36844 msec

V_S>C:\Documents and Settings\. \Test\Release>test
V_S>strncpy: 36703 msec
V_S>my_strncpy: 36875 msec

а можно код теста? каков размер копируемой строки относительно буфера?

Re[2]: fast strncpy

От: alexeiz
Дата: 15.01.08 09:21
Оценка:

Здравствуйте, MShura, Вы писали:

S>>Интересуют предложения по оптимизации.

MS>В свой время Крис Касперски оптимизировал memcpy за счет периодического чтения «вперед».
MS>За счет этого уменьшались простои самого внутреннего кэша процессора, который, кстати, разного размера для разных CPU.

MS>Он приводил очень убедительные результаты сравнения разных реализаций memcpy в свою пользу.

Эти оптимизации перестали работать, начиная с P4. Я сам пробовал. На P3 работало, на P4 — уже нет. Prefetcher в процессорах стал более умный и он уже сам понимает, как эффективно заполнить кэш.

Re[3]: fast strncpy

От: Сергей Мухин
Дата: 15.01.08 09:32
Оценка: 1 (1)

Здравствуйте, alexeiz, Вы писали:

S>>>Интересуют предложения по оптимизации.

MS>>В свой время Крис Касперски оптимизировал memcpy за счет периодического чтения «вперед».
MS>>За счет этого уменьшались простои самого внутреннего кэша процессора, который, кстати, разного размера для разных CPU.

MS>>Он приводил очень убедительные результаты сравнения разных реализаций memcpy в свою пользу.

A>Эти оптимизации перестали работать, начиная с P4. Я сам пробовал. На P3 работало, на P4 — уже нет. Prefetcher в процессорах стал более умный и он уже сам понимает, как эффективно заполнить кэш.


С уважением,
Сергей Мухин
Re[3]: fast strncpy

От: Sergey Chadov
Дата: 15.01.08 10:11
Оценка:

SC>>Здравствуйте, sokel, Вы писали:

S>1. MS-specific не надо. Да и функцию такого размера как haszero любой компилятор заинлайнит без вопросов.
Я имел ввиду inline на саму функцию. Конечно может привести к разбуханию кода, но тем не менее может дать приличный выигрыш в производительности, если функция вызывается достаточно часто с не очень длинными строками. Впрочем, тут больше зависит от контекста использования. чем от самой функции, как часто и бывает при агрессивной низкоуровневой оптимизации.

S>3. так лучше?:
значительно.

Re[3]: fast strncpy

От: Кодт
Дата: 15.01.08 10:30
Оценка:

Здравствуйте, djs_, Вы писали:

К>>Ты выравниваешь по sizeof(size_t), а не по sizeof(int). Но именно int является наиболее родным типом данных для процессора.

_>Извините, а разве size_t не есть просто целочисленный define или typedef соотв. типа для данной платформы?

Нет, это самостоятельный тип. Эквивалентный unsigned int или unsigned long.
Хотя некоторые криворукие компиляторы действительно определяют его как синоним.

_>Т.е. где-то он будет 4 байта, где-то 16, а где-то даже и 20 бит (2,5 байта)?

Ну уж полубайтами он точно не будет. Размер всегда меряется в байтах (хотя не все старшие биты этих байтов обязаны быть значащими).

_>Или вы что-то иное имете ввиду?

Я имею в виду, что int и size_t — это родные для компилятора типы. Да, они платформенно-зависимы.
int — тип, наиболее удобный для целочисленной арифметики; size_t и ptrdiff_t — достаточные для адресной.

Всё тот же реальный режим x86, где длина адреса — 20 бит, а длина регистра данных — 16.
Сделать 20- или 32-битную арифметику большого труда не составляет, но этот труд ненулевой. Даже если это копирование.
Поэтому использовать long вместо int без нужды — неоправданно.

Перекуём баги на фичи!
Re[4]: fast strncpy

От: sokel
Дата: 15.01.08 11:42
Оценка: -1

Здравствуйте, Кодт, Вы писали:

К>Я имею в виду, что int и size_t — это родные для компилятора типы. Да, они платформенно-зависимы.
К>int — тип, наиболее удобный для целочисленной арифметики; size_t и ptrdiff_t — достаточные для адресной.

Только вот я не встречал компилятора где sizeof(int) != 4. Та же реализация strncpy с выравниванием по int на 64-битной платформе занчительно уступает реализации с выравниванием по size_t.

Re[5]: fast strncpy

От: Sergey
Дата: 15.01.08 11:55
Оценка:

> К>Я имею в виду, что int и size_t — это родные для компилятора типы. Да, они платформенно-зависимы.
> К>int — тип, наиболее удобный для целочисленной арифметики; size_t и ptrdiff_t — достаточные для адресной.
>
> Только вот я не встречал компилятора где sizeof(int) != 4.

Не так давно таких компиляторов было полно

Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена «За заслуги перед Отечеством» является Геннадий Хазанов.
Re[5]: fast strncpy

От: MShura
Дата: 15.01.08 14:05
Оценка:

S>Только вот я не встречал компилятора где sizeof(int) != 4.

На многих сигнальных процессорах (например для TS201) sizeof(int) = 1

Re[5]: fast strncpy

От: Кодт
Дата: 15.01.08 14:25
Оценка:

Здравствуйте, sokel, Вы писали:

S>Только вот я не встречал компилятора где sizeof(int) != 4. Та же реализация strncpy с выравниванием по int на 64-битной платформе занчительно уступает реализации с выравниванием по size_t.

Вот упёрся же ты рогом в size_t.
Для IA64 с сегментной моделью sizeof(size_t) должен быть не менее 10. (8 байт — смещение, хранимое в регистрах общего назначения, и 2 байта — селектор сегмента).
Плоская модель — это всего лишь подарок микрософта благодарным прикладным программистам.

Форум-то называется «С++», а не «Intel Pentium»?

Перекуём баги на фичи!
Re[6]: fast strncpy

От: sokel
Дата: 15.01.08 15:33
Оценка: -1

Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, sokel, Вы писали:

S>>Только вот я не встречал компилятора где sizeof(int) != 4. Та же реализация strncpy с выравниванием по int на 64-битной платформе занчительно уступает реализации с выравниванием по size_t.

К>Вот упёрся же ты рогом в size_t.
К>Для IA64 с сегментной моделью sizeof(size_t) должен быть не менее 10. (8 байт — смещение, хранимое в регистрах общего назначения, и 2 байта — селектор сегмента).
К>Плоская модель — это всего лишь подарок микрософта благодарным прикладным программистам.

К>Форум-то называется «С++», а не «Intel Pentium»?

Не подарок микрософта, а механизм страничной трансляции. А сегментная модель памяти, это как раз для форума «Intel Pentium».

Re[7]: fast strncpy

От: Кодт
Дата: 15.01.08 16:04
Оценка:

Здравствуйте, sokel, Вы писали:

К>>Плоская модель — это всего лишь подарок микрософта благодарным прикладным программистам.
S>Не подарок микрософта, а механизм страничной трансляции. А сегментная модель памяти, это как раз для форума «Intel Pentium».

Ну раз уж спускаемся до этого уровня — то процессоры с сегментно-страничной памятью — это не только интелы (макинтоши, VAXы, что там ещё было).
А подарок микрософта состоит в том, что прикладных программистов освободили, во-первых, от секса с ручным управлением сегментами, и во-вторых, от недо-гарвардской архитектуры (когда стек, данные и код лежат в разных сегментах). Хорошо известной ещё под досом.

Вот не помню, какую модель предлагает OS/2 warp. Там, кажется, как раз рукоделия приветствовались.

Перекуём баги на фичи!
Re[8]: fast strncpy

От: sokel
Дата: 16.01.08 06:59
Оценка:

Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, sokel, Вы писали:

К>>>Плоская модель — это всего лишь подарок микрософта благодарным прикладным программистам.
S>>Не подарок микрософта, а механизм страничной трансляции. А сегментная модель памяти, это как раз для форума «Intel Pentium».

К>Ну раз уж спускаемся до этого уровня — то процессоры с сегментно-страничной памятью — это не только интелы (макинтоши, VAXы, что там ещё было).
К>А подарок микрософта состоит в том, что прикладных программистов освободили, во-первых, от секса с ручным управлением сегментами, и во-вторых, от недо-гарвардской архитектуры (когда стек, данные и код лежат в разных сегментах). Хорошо известной ещё под досом.

К>Вот не помню, какую модель предлагает OS/2 warp. Там, кажется, как раз рукоделия приветствовались.

Ладно, не суть. В общем, size_t я выбрал потому что этот тип определяет возможности адресации и, как правило, покрывает разрядность процессора, соответственно, оптимален для пословного копирования. А двухкомпонентная адресация и сегментная модель памяти, как я думал, просто наследие 286-х и 16-битной адресации, что упоминается, например, здесь или здесь.

Re: fast strncpy

От: se_sss
Дата: 16.01.08 20:32
Оценка:

Здравствуйте, sokel, Вы писали:

S>Предлагаю на растерзание функцию копирования строк, аналог strncpy, без недостатков оной. Не заполняет остаток нулями, всегда в конце вставляет 0. На входе принимает размер буфера назначения, на выходе дает число скопированных символов. Интересуют предложения по оптимизации.

Тогда уж можно попробовать поэкспериментировать с
такой штукойтакой штукой

Re[2]: fast strncpy

От: sokel
Дата: 17.01.08 09:45
Оценка:

Здравствуйте, se_sss, Вы писали:

_>Здравствуйте, sokel, Вы писали:

S>>Предлагаю на растерзание функцию копирования строк, аналог strncpy, без недостатков оной. Не заполняет остаток нулями, всегда в конце вставляет 0. На входе принимает размер буфера назначения, на выходе дает число скопированных символов. Интересуют предложения по оптимизации.

_>Тогда уж можно попробовать поэкспериментировать с
_>такой штукойтакой штукой

_>-)

Re: fast strncpy

От: Аноним
Дата: 18.01.08 08:39
Оценка:

Здравствуйте, sokel, Вы писали:

S>Предлагаю на растерзание функцию копирования строк, аналог strncpy, без недостатков оной. Не заполняет остаток нулями, всегда в конце вставляет 0. На входе принимает размер буфера назначения, на выходе дает число скопированных символов. Интересуют предложения по оптимизации.

Конструктивную критику принимаете? Может Вы где-нибудь уже ответили, но когда пишут «fast strncpy» приводят относительно чего fast? Никаких таблиц сравнения не заметил. В классических изложениях также перед кодом алгоритма дают его основную идею.

Re: Мой эксперимент (strncpy, my_strncpy, memcpy)

От: Critical Error ICQ: 123736611
Дата: 18.01.08 13:31
Оценка: 1 (1)

Я провел свой тест данного алгоритма, но решил посмотреть несколько в другую сторону. Я сравнил strncpy, my_strncpy и memcpy. И вот результат:

dst_sz, src_sz | strncpy | my_strncpy| memcpy | 256, 128 | 0.93 | 0.68 | 0.54 | 8192, 128 | 3.94 | 0.81 | 0.86 | 16384, 128 | 8.51 | 0.83 | 0.81 | 32768, 128 | 19.62 | 0.79 | 0.83 | 65536, 128 | 49.89 | 0.82 | 0.67 | 131072, 256 | 104.60 | 0.77 | 1.10 | 131072, 4096 | 101.42 | 3.22 | 1.46 | 131072, 8192 | 100.27 | 5.83 | 1.98 | 131072, 16384 | 110.20 | 10.99 | 3.68 | 131072, 32768 | 113.72 | 21.35 | 6.79 | 131072, 65536 | 119.81 | 42.26 | 16.67 |

MSVC8 — Intel Pentium D 2.8GHz.
dst_sz, src_sz — это размеры строки получателя и строки источника соответственно.
strncpy|my_strncpy|memcpy — время выполнения функции в микросекундах.

Первая часть таблицы иллюстрирует выгоду использования my_strncpy перед strcpy. Из таблицы следует, что strncpy явно проигрывает my_strncpy, но это мы выяснили и ранее

Вторая часть таблицы сравнивает my_strncpy с memcpy. Тут однозначно выигрывает memcpy. Это по сути сравнение null-terminated строк со строками с заданной длиной.

Вывод: в общем случае выгоднее всего использовать my_strncpy, но когда известна длина буфера получателя и длина строки источника лучше использовать memcpy. Если пишите какую либо программу, активно работающую со строками не забывайте о memcpy, она значительно быстрее любой реализации strncpy. Если хотите получить высокую скорость работы со строками, то не используйте null-terminated строки и функции для работы с ними.

void test_fast( size_t dst_sz, size_t src_sz ) < tick_count t0, t1; char* dst = new char[dst_sz]; char* src = new char[src_sz]; memset(src, '-', src_sz); src[src_sz-1] = 0; t0 = tick_count::now(); strncpy(dst, src, dst_sz); t1 = tick_count::now(); double tv1 = (t1-t0).seconds()*1000000.; t0 = tick_count::now(); my_strncpy(dst, src, dst_sz); t1 = tick_count::now(); double tv2 = (t1-t0).seconds()*1000000.; t0 = tick_count::now(); memcpy(dst, src, src_sz); t1 = tick_count::now(); double tv3 = (t1-t0).seconds()*1000000.; t0 = tick_count::now(); memcpy(dst, src, strlen(src)+1); t1 = tick_count::now(); double tv4 = (t1-t0).seconds()*1000000.; stringstream ost; ost ", " delete src; delete dst; > int main(int argc, char* argv[]) < test_fast(256, 128); test_fast(256*32, 128); test_fast(256*64, 128); test_fast(256*128, 128); test_fast(256*256, 128); cout return 0; >

Re: fast strncpy

От: Аноним
Дата: 19.01.08 11:22
Оценка:

Здравствуйте, sokel, Вы писали:

S>Предлагаю на растерзание функцию копирования строк, аналог strncpy, без недостатков оной. Не заполняет остаток нулями, всегда в конце вставляет 0. На входе принимает размер буфера назначения, на выходе дает число скопированных символов. Интересуют предложения по оптимизации.

Если речь идет о версиях компилятора от
Microsoft VC 8+, то в стандартных библиотеках появился целый набор функций с постфиксом «_s».
В частности, имеется функция strncpy_s, которая описанного выше недостатка не имеет.
Однако есть небольшая понкость — в Debug версии она все же заполняет буффер данными (0xfd).
Это значительно помогает искать многие трудновыявляемые баги. В релизе же работает как и положено — без лишних операций по заполнению.

Re: fast strncpy

От: remark http://www.1024cores.net/
Дата: 19.01.08 13:55
Оценка: +1

Здравствуйте, sokel, Вы писали:

S>Интересуют предложения по оптимизации.

Оптимизация таких вещей — это всегда платформенно-зависимая вещь.
Во-первых, тебе надо определиться, для чего ты хочешь применять эту функцию. Варианты: для копирования маленьких областей, для копирования очень больших областей, для всего подряд.
Например для копирования очень больших областей на x86 тебе надо применять non-temporal сохранения + non-temporal предвыборку в L1$ + барьер на запись. Смотри описание и гугли по инструкциям MOVNTQ, PREFETCHNTA, SFENCE.
Во-вторых, погляди, не можешь ли ты наложить какие-то дополнительные ограничения на входные данные.
Например ты можешь потребовать, что бы обе области были выровнены на 16 байт + размер памяти под области тоже кратен 16 байтам. Тогда ты можешь не обрабатывать отдельным образом начало и конец области, а сразу и до конца шпарить по 16 байт. Я думаю, это должно дать выигрыш для маленьких областей, т.к. функция будет маленькой и простой и без дополнительных ветвлений.
Если же ты хочешь функцию портируемую, для всех размеров областей и без дополнительных ограничений на входные данные, то тут и никакого простора для оптимизаций нет. Остаётся только оставить функцию как есть и надеяться на оптимизатор компилятора. Но тут ты скорее всего всегда будешь отставать от библиотечной memcpy() и от intrinsic’а компилятора.

Re: fast strncpy

От: netch80 http://netch80.dreamwidth.org/
Дата: 19.01.08 14:02
Оценка:

Здравствуйте, sokel, Вы писали:

S>Предлагаю на растерзание функцию копирования строк, аналог strncpy, без недостатков оной. Не заполняет остаток нулями, всегда в конце вставляет 0. На входе принимает размер буфера назначения, на выходе дает число скопированных символов. Интересуют предложения по оптимизации.

Прокомментирую не только по оптимизации:)

Во-первых, интерфейс strncpy() действительно немного неадекватен обычному использованию — дело в том, что эта функция была придумана в Unix именно для заполнения полей фиксированного размера так, чтобы они потом были пригодны для memcmp (а не с ограничением по размеру), поэтому там доливание нулями. Поэтому все тесты, в которых размер приёмника больше размера источника, заведомо неадекватны; сравнивать надо не с классической strncpy(), а с, лучше всего, strcpy_s() или strncpy_s() (ISO drafts, из платформ — последние MSVC). Если же смотреть на приведённые дальше сравнения скоростей, Ваша strncpy заметно проигрывает штатной memcpy(), а это показывает, что таки старания на Си не дают достаточного результата, и ассемблер тут таки прогрессивнее. (Разумеется, если надо так делать. Я не премину повторить, что NUL-terminated strings — зло, и от них надо избавляться при любой возможности.)

Во-вторых, если strlen(src)>=dst_size, я бы делал не так — буфер заполняется полностью, \0 в него не дописывается, а возвращается dst_size. (Кстати, оригинальная strncpy делает именно так.) Это позволяет немедленно проверять на переполнение; у Вас же не отличить переполнения от полного заполнения буфера. Если кому нужна в таком случае урезанная строка, он может и вручную дописать \0 в конец буфера.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *