LINUX.ORG.RU

[C] конкатенация строк


0

2

Есть код:

char* some = "/home/someuser/somefile";
хочется сделать его по универсальнее, как к началу этой строки прилепить getenv(«HOME»), который бы подставлял нужный путь /home/someuser.


Делаешь буфер побольше (в идеале - максимальная допустимая длина $HOME, если таковая имеется + длина somefile + 1) и strncat ||sprintf.

unikoid ★★★ ()
char path[4096];
const char *home = getenv("HOME");
const char *file = "/somefile";
assert(home != NULL);
assert(strlen(home) + strlen(file) < sizeof path);
strcpy(path, home);
strcat(path, file);
arsi ★★★★★ ()
Ответ на: комментарий от arsi

зы: для с99:

const char *home = getenv("HOME");
const char *file = "/somefile";
assert(home != NULL);
char path[strlen(home) + strlen(file) + 1];
strcpy(path, home);
strcat(path, file);
arsi ★★★★★ ()
Ответ на: комментарий от arsi

Не рекомендовал бы использовать assert().

Во-первых, переменная $HOME вполне может быть не установлена (env и простой unset) и этот случай нужно просто обрабатывать, выдавая сообщение на stderr и делая exit(EXIT_FAILURE). Вдобавок, assert() ещё и вызывает abort() из-за чего будет формироваться core файл (если он не запрещен).

Во-вторых, assert() выполняется только в дебажных сборках, а в production (при -DNDEBUG) программа либо упадёт (на strlen(NULL)), либо будет работать не корректно.

И вообще использовать getenv() я бы поостерёгся — всё-таки переменные окружения полностью под контролем пользователя. Я бы скорее взял uid пользователя и по нему определил home directory.

php-coder ★★★★★ ()

За strcpy/strcat надо убивать. Безопасная работа со строками предполагает, что их длина всегда известна, а раз так, то ничего не мешает воспользоваться более эффективным memcpy.

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

> И вообще использовать getenv() я бы поостерёгся — всё-таки переменные окружения полностью под контролем пользователя. Я бы скорее взял uid пользователя и по нему определил home directory.

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

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

что, без меня никак не разберётесь, кто там у вас быдлокодер? ;)

arsi ★★★★★ ()

Как-то так:

#include <stdio.h>
#include <limits.h>

#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>

static const char *const rel_path = ".conf/conffile";

int main()
{
        struct passwd *pwd;
        char path[PATH_MAX + 1];

        pwd = getpwuid(getuid());
        snprintf(path, PATH_MAX + 1, "%s/%s", pwd->pw_dir, rel_path);

        printf("Result: \"%s\"\n", path);

        return 0;
}
FIXME: Нет проверки возвращаемых значений, путь может быть длиннее PATH_MAX.

Deleted ()
Ответ на: комментарий от Deleted
static const char *const rel_path = ".conf/conffile";

Зачем в данном случае создавать лишний указатель? Лучше (и короче) будет так:

static const char rel_path[] = ".conf/conffile";
Deleted ()
;; самый правильный Си

CL-USER> (merge-pathnames "somefile" (user-homedir-pathname))

#P"/home/someuser/somefile"
anonymous ()
Ответ на: комментарий от Deleted

Зачем в данном случае создавать лишний указатель? Лучше (и короче) будет так:

Указатель будет в обоих случаях. И код тоже будет сгенерирован одинаковый. Мне просто привычнее писать const t *const =).

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

> И код тоже будет сгенерирован одинаковый.

кстати, нет.

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

Неправда. С точки зрения языка C это совершенно разные конструкции. В моем случае никакого указателя не объявляется. И код соответственно будет разный.

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

Шило на мыло:


$ diff -u test*.s
--- test1.s	2011-01-10 03:29:02.934554311 +0500
+++ test2.s	2011-01-10 03:29:20.006447219 +0500
@@ -1,10 +1,8 @@
 	.file	"test.c"
 	.section	.rodata.str1.1,"aMS",@progbits,1
 .LC0:
-	.string	".conf/conffile"
-.LC1:
 	.string	"%s/%s"
-.LC2:
+.LC1:
 	.string	"Result: \"%s\"\n"
 	.text
 	.p2align 4,,15
@@ -23,13 +21,13 @@
 	call	getpwuid
 	movq	32(%rax), %rcx
 	movq	%rsp, %rdi
-	movl	$.LC0, %r8d
-	movl	$.LC1, %edx
+	movl	$rel_path, %r8d
+	movl	$.LC0, %edx
 	movl	$4097, %esi
 	xorl	%eax, %eax
 	call	snprintf
 	movq	%rsp, %rsi
-	movl	$.LC2, %edi
+	movl	$.LC1, %edi
 	xorl	%eax, %eax
 	call	printf
 	xorl	%eax, %eax
@@ -41,5 +39,10 @@
 	.cfi_endproc
 .LFE14:
 	.size	main, .-main
+	.section	.rodata
+	.type	rel_path, @object
+	.size	rel_path, 15
+rel_path:
+	.string	".conf/conffile"
 	.ident	"GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"
 	.section	.note.GNU-stack,"",@progbits

В моем случае никакого указателя не объявляется.

А чем же тогда в твоём случае является rel_path?

Deleted ()
Ответ на: комментарий от Deleted
static const char *const rel_path = ".conf/conffile";
f(rel_path);

— косвенная адресация массива.

static const char rel_path[] = ".conf/conffile";
f(rel_path);

— прямая.

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

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

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

но только для static… как в примере, да.

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