LINUX.ORG.RU

Как красиво присвоить строковое поле?

 


1

2

Здравствуйте

Пусть есть структура foo с полем char s[8]

Чтобы присвоить полю значение надо написать аж 2(!) строки )

memset(foo.s, 0, sizeof(foo.s));
memcpy(foo.s, mystring, strlen(mystring));

Может есть способ поэлегантнее?

★★★★★
Ответ на: комментарий от former_anonymous

Потому-что ранее в поле могло быть другое значение, длиннее mystring. И без memset они перемешаются

makoven ★★★★★
() автор топика
Последнее исправление: makoven (всего исправлений: 2)
Ответ на: комментарий от omnomnomnus

А точно, sprintf же добавляет нолик в конец. Спасибо

makoven ★★★★★
() автор топика

Кажется в треде появился царь. Ждём срача на 10 страниц о том, как правильно скопировать строку.

Должен признать, мне понравился трюк с strncat, используемый вместо strncpy, который нолик в конце не ставит.

omnomnomnus
()
Ответ на: комментарий от makoven

Потому-что ранее в поле могло быть другое значение, длиннее mystring. И без memset они перемешаются

ЩИТО? Копировать нужно вместе с нулём. И ничего не перемешается. И за размерами следить, например используя strncpy.

former_anonymous ★★★
()

а вариант

memcpy(foo.s, mystring, strlen(mystring)  +  1  );
не рассматривали? Или mystring это не строка с нулем на конце?

anonymous
()
Ответ на: комментарий от former_anonymous

Да, все верно, что-то я затупил)

makoven ★★★★★
() автор топика
Ответ на: комментарий от aiqu6Ait

про xxprintf

семейство функций xxprintf xxscanf половину времени и ресурсов тратят на разбор форматной строки - а оно нужно чтобы 8 байтов переслать?

anonymous
()

Чтобы присвоить полю значение надо написать аж 2(!) строки )

В данном случае достаточно одной строки, последней. Но стоит сделать проверку на выход за границы s[].

andreyu ★★★★★
()
strcpy(foo.s, mystring);

Только сначала надо проверить на переполнение буфера. Т.е. иначе стоило бы так:

size_t mystring_size = strlen(mystring) + 1;
if (mystring_size > sizeof(foo.s) {/*error*/}
else memcpy(foo.s, mystring, mystring_size);
mystring — это же не константная строка?

Deleted
()
Последнее исправление: romeo250501 (всего исправлений: 1)
Ответ на: комментарий от omnomnomnus

Функции семейства printf принимают на вход форматы, а не строки.

Deleted
()
*(uint64_t *)(foo.s)=*(uint64_t *)mystring & ~0xfful;

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

MKuznetsov ★★★★★
()
Ответ на: комментарий от makoven

В экстремалой паранойей в источнике может не быть 8 байт

MKuznetsov ★★★★★
()

Эпичный топик по конкатенации строк на С уже был, теперь очевидно будет новый про копирование.

П.С. memcpy, только копировать надо сразу с нулем, а длину хранить

anonymous
()
Ответ на: комментарий от anonymous

Забудь все советы которые тут были и используй strlcpy.

Пиши непортируемый код на С, будь говнокодером.

anonymous
()
Ответ на: комментарий от anonymous

П.С. memcpy, только копировать надо сразу с нулем, а длину хранить
char s[8]

А в данном случае можно смело использовать константу 8 вместо длины для memcpy, будет гарантированно быстрее чем хранить длину или использовать strlen.

anonymous
()
Ответ на: комментарий от anonymous

можно смело использовать константу 8

В том числе за счет того, что gcc, например, выкинет вызов memcpy вообще.

П.С. сорри за поток сообщений, не могу редактировать.

anonymous
()

Конкретно то, что ты написал, делает strncpy.

kawaii_neko ★★★★
()
Ответ на: комментарий от anonymous

нормальных реализациях libc

Поймешь разницу между «нормальной» и «стандартной» - приходи. А то знаем таких говнокодеров: я писал как надо, это не мои проблемы, что мой говнокод не собирается на msvc, кастомном uclibc и т.д.

А говнокод - этот все что тут было предложено кроме strlcpy, ну и ты еще в куче (:

У меня - правильный, стандартный, компактный и самый быстрый вариант, утрись.

anonymous
()

шел 2016 год, в C еще не знают как присваивать «строковые поля»

umren ★★★★★
()

strlcpy(foo.s, mystring, sizeof(foo.s)); /* если доступен strlcpy */

strncpy(foo.s, mystring, sizeof(foo.s)); /* есть везде, но есть 2 нюанса: не добавляет '\0', если длинна строки больше поля, и всегда проходит всё поле, забивая остаток нулями */

snprintf(foo.s, sizeof(foo.s), «%s», mystring); /* медленно, строка формата разбирается в рантайме, несоптимизировать */

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

anonymous
()
Ответ на: комментарий от Stil
~$ cat ./test.c
#include <stdint.h>
#include <string.h>

typedef struct {
    char s[8];
}
foo_t;

int main(int argc, char **argv) {
    foo_t f1, f2;
    
    *(uint64_t *)(f1.s)=*(uint64_t *)f2.s & ~0xfful;
    
    memcpy(f1.s, f2.s, 8);
    
    return 0;
}
~$ gcc -g ./test.c
~$ objdump -S ./a.out 

./a.out:     формат файла elf64-x86-64

...

int main(int argc, char **argv) {
 770:	55                   	push   %rbp
 771:	48 89 e5             	mov    %rsp,%rbp
 774:	48 83 ec 30          	sub    $0x30,%rsp
 778:	89 7d dc             	mov    %edi,-0x24(%rbp)
 77b:	48 89 75 d0          	mov    %rsi,-0x30(%rbp)
 77f:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
 786:	00 00 
 788:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
 78c:	31 c0                	xor    %eax,%eax
    foo_t f1, f2;
    
    *(uint64_t *)(f1.s)=*(uint64_t *)f2.s & ~0xfful;
 78e:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
 792:	48 8d 55 f0          	lea    -0x10(%rbp),%rdx
 796:	48 8b 12             	mov    (%rdx),%rdx
 799:	b2 00                	mov    $0x0,%dl
 79b:	48 89 10             	mov    %rdx,(%rax)
    
    memcpy(f1.s, f2.s, 8);
 79e:	48 8b 45 f0          	mov    -0x10(%rbp),%rax
 7a2:	48 89 45 e0          	mov    %rax,-0x20(%rbp)
    
    return 0;
 7a6:	b8 00 00 00 00       	mov    $0x0,%eax
}

Причем и проще читается, и нет ограничений.

anonymous
()
Ответ на: комментарий от anonymous

8

Да. в реальном коде лучше не писать «магический числа», а использовать sizeof и константы.

anonymous
()
Ответ на: комментарий от anonymous
#include <stdint.h>
#include <string.h>

typedef struct {
    char s[8];
}
foo_t;

void via_cast(foo_t *f1, foo_t *f2)
{
    *(uint64_t *)(f1->s)=*(uint64_t *)f2->s & ~0xfful;
}

void via_memcpy(foo_t *f1, foo_t *f2)
{
    memcpy(f1->s, f2->s, 8);
}

int main(int argc, char **argv) {
    foo_t f1, f2;

    via_cast(&f1, &f2);
    via_memcpy(&f1, &f2);
    
    return 0;
}

gcc -S -O0 -o- test.c на -O0 даёт:

via_cast:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movq    %rdi, -8(%rbp)
        movq    %rsi, -16(%rbp)
        movq    -8(%rbp), %rax
        movq    -16(%rbp), %rdx
        movq    (%rdx), %rdx
        movb    $0, %dl
        movq    %rdx, (%rax)
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
via_memcpy:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    $16, %rsp
        movq    %rdi, -8(%rbp)
        movq    %rsi, -16(%rbp)
        movq    -16(%rbp), %rcx
        movq    -8(%rbp), %rax
        movl    $8, %edx
        movq    %rcx, %rsi
        movq    %rax, %rdi
        call    memcpy
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
Для -O2 надо что-нибудь посложнее придумать, хотя с фиксированными 8 байт, оно скорее всего всё равно заинлайнится...

Stil ★★★★★
()
Ответ на: комментарий от Stil

Во-первых это искусственный пример, т.к. gcc использует builtin-реализацию для memcpy по дефолту. Во-вторых gcc5 и gcc6 у меня и с -O0 ее используют. А вот четверка, да, оставила вызов memcpy.

anonymous
()
#define STRNCPY(dst, src) do { strncpy((dst), (src), sizeof(dst)); dst[sizeof(dst)-1]=0;} while (0)
#define STRNCAT(dst, src) do { strncat((dst), (src), sizeof(dst) - strlen(dst) - 1); dst[sizeof(dst)-1]=0;} while (0)
vromanov ★★★
()

Как только не извратятся, лишь бы продолжать рассказывать какой c++ плохой.

anonymous
()
Ответ на: комментарий от vromanov

#define STRNCPY(dst, src) do { strncpy((dst), (src), sizeof(dst)); dst[sizeof(dst)-1]=0;} while (0)

1. Размножение побочных эффектов т.к. макросы в С убогие
2. sizeof может конкретно так подставить при «неправильных» аргументах, т.к. типы не проверяются
3. Если dst будет иметь размерность 0 (и такое бывает), то будет порча памяти
4. Сразу видно костыль для кривого strncpy, который большую часть вызовов будет бесполезно писать 0 в память
5. Результат никак не проверяется - было скопировано все что надо или зачем-то только огрызок.

anonymous
()
Ответ на: комментарий от anonymous

Как только не извратятся, лишь бы продолжать рассказывать какой c++ плохой.

И в С++ бывает необходимость оптимизировать работу со строками. Но там это именно оптимизация. И даже больше, например, в libstdc++ строки сделаны так, что при длине до 15 (ЕМНИП) символов они вообще не используют динамическую память, и копирование такой строки будет немногим медленнее чем приведенный выше memcpy для 8 байт.

anonymous
()
Ответ на: комментарий от Stil

Размер mystring не известен. Во-вторых, такой код просто убогий и не поддерживаемый. Что, если автор захочет поменять размер буфера?

Deleted
()
Ответ на: комментарий от anonymous

Слова с++ плохой / сишка плохая слышны в этом треде только от бомбящих крестовичков. Ответ на вопрос, как скопировать nul-terminated в fixed width уже был дан, а с какого-то хрена еще что-то придумывать — как раз удел неудачников, которые все время пытаются изобрести «нормальные» строки. Вот только эти «нормальные» строки — либо костыли поверх культяпок, либо тянут за собой рантайм и язык, в котором 5-7 лет опыта это все еще сизонед джун, уровень чуть выше днища. Потому что ни низкий уровень не осилили, ни высокий, все плюхаются со своими мегалибами да ультрафрейворками.

arturpub ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.