LINUX.ORG.RU

Передача в функцию int *, если она хочет void *

 ,


0

1

Есть функция, которая особым образом выделяет память. Сколько - говорит пользователь. Как он будет использовать блок - его проблемы. Сигнатура:

int funcname(void ** pointer, int size);
Вот, например, мне нужен блок под int:
int * block;
if (funcname(&block, sizeof(int)) == -1)
   return -1;
А компилятор мне такой по рукам: а-та-та, не тот тип суешь. Делаю так:
int * block;
void * conv;
if (funcname(&conv, sizeof(int)) == -1)
   return -1;
block = conv;
Чувствую, что пахнет быдлокодом. Как это решить не переставляя void * как прямой результат функции (у меня там код ошибки во всех функциях)?

★★

Последнее исправление: cetjs2 (всего исправлений: 1)

Каст для чего придумали? А главное, я надеюсь, funcname не твоя? А то за такое УБИВАТ.

mix_mix ★★★★★
()

Как это решить не переставляя void * как прямой результат функции (у меня там код ошибки во всех функциях)?

ИМХО в данном конкретном случае быдлокод в виде глобальной переменной с кодом ошибки намного лучше.

emulek
()

Отличный способ делать какие-то действия с переменными разных типов. Судя по всему, эта штука внутри себя malloc вызывает, так что можешь или как аноним посоветовал делать, или свои финты с void* использовать, только в этом случае все равно придется писать block = (int*)conv, иначе ошибка будет.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от mix_mix

А главное, я надеюсь, funcname не твоя? А то за такое УБИВАТ.

как я понял, его эта функция, он не хочет возвращать void*, а хочет int. Хотя я не понимаю, какие там могут быть ошибки кроме «не смогла»?

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

Очень даже имеет. Допустим, твоя функция делает какую-нибудь хрень с числами: скажем, выдает длинный массив unsigned ..., где ... определяется размером size (скажем, при size=1 будет unsigned char, при size=4 — uint32_t и т.д.). Тогда, передав туда адрес указателя нужного типа, ты получишь в него массив.

Но таки симпатичней было бы возвращать этот массив и проверять на !NULL результат.

Eddy_Em ☆☆☆☆☆
()

может обойтись без памяти?

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

Не имеет. Почитайте уже стандарт и FAQ. ;)

В void * можно передать что угодно (а так же взять от туда), хоть struct ************* {}, а вот адресс адресса пустого — дырка от бублика.

UPD: http://c-faq.com/ansi/voidparith.html

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

Проблема уже тут: void ** — указатель на указатель на void не имеет смысла.

имеет. Но этот смысл может быть разным в некоторых платформах (в которых есть разные указатели). В x86 в void** нет никакого криминала, и такой код будет отлично работать на уютном локалхосте. Я гарантирую это!

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

void ** — указатель на указатель на void не имеет смысла.

Имеет и повсеместно используется. По ссылке идёт лишь речь о том, что на некоторых машинах (Eclipse MV, HP 3000) из далеких 80х размер char* и void* может быть разным.

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

Это никак не запрещает взять адрес переменной, в которой лежит указатель на что-то.

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

arithmetic on void *'s is disallowed (though some compilers allow it as an extension)

никакой арифметики тут нет. При чём тут вообще арифметика?

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

При том, что взяитие адресса — это частный случай арифметики.

Оно просто не имеет смысла, ты не можешь сделать v++, если v это void *, т.к. это подразумевает, что компилятор знает размер того, на что ссылается ссылка.

А void ** не имеет смысла в двойне. Т.к. void *** == void ** == void *

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

FYI: если тебе понадобился explicit cast, ты делаешь что-то неправильно.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 2)
Ответ на: комментарий от beastie
int main() {
	int x = 5;
	void *p = &x;
	void **pp = &p;
}
gcc -O0 -S test.c
	movl	$5, -4(%rbp)
	movq	%rdx, -16(%rbp)
	movq	%rcx, -24(%rbp)

Где ты здесь арифметику увидел?

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

Да что ты говоришь!

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

void calc(void **ret, size_t sz){
	int i;
	char *X = calloc(15, sz), *ptr = X;
	for(i = 0; i < 15; i++, ptr += sz)
		*ptr = i;
	*ret = (void*) X;
}

int main(){
	int i;
	uint8_t *u8;
	uint16_t *u16;
	uint32_t *u32;
	uint64_t *u64;
	calc((void**)&u8, 1);
	calc((void**)&u16, 2);
	calc((void**)&u32, 4);
	calc((void**)&u64, 8);
	printf("data:\n  u8\t  u16\t  u32\t  u64\n");
	for(i = 0; i < 15; i++)
		printf("%u\t%u\t%u\t%zd\n", *u8++, *u16++, *u32++, *u64++);
	return 0;
}

прекрасно работает:

gcc 1.c -Wall -Werror -Wextra -W -o test && ./test
data:
  u8	  u16	  u32	  u64
0	0	0	0
1	1	1	1
2	2	2	2
3	3	3	3
4	4	4	4
5	5	5	5
6	6	6	6
7	7	7	7
8	8	8	8
9	9	9	9
10	10	10	10
11	11	11	11
12	12	12	12
13	13	13	13
14	14	14	14

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от beastie

Фигушки!!!

Если у тебя аргумент void*, то ты не вернешь через него значение, как в моем примере ниже.

Eddy_Em ☆☆☆☆☆
()
Ответ на: Да что ты говоришь! от Eddy_Em

У меня примерно так реализовано отображение разных целых в терминал на микроконтроллере, чтобы от типа не зависело.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от beastie

А void ** не имеет смысла в двойне.

int funcname(void **pointer, int size)
{
        *pointer = malloc(size);
        return *pointer ? 0 : -1;
}

Ну ок, убираем void ** из параметров, получаем этот ужас:

int funcname(void *pointer, int size)
{
        *(void **)pointer = malloc(size);
        return *(void **)pointer ? 0 : -1;
}

Хотя да, void * в параметрах позволяет убрать каст при вызове, а внутри функции можно сделать так:

int funcname(void *pointer, int size)
{
        void **p = pointer;
        *p = malloc(size);
        return *p ? 0 : -1;
}
gentoo_root ★★★★★
()
Последнее исправление: gentoo_root (всего исправлений: 1)
Ответ на: комментарий от beastie

При том, что взяитие адресса — это частный случай арифметики. Оно просто не имеет смысла, ты не можешь сделать v++, если v это void *, т.к. это подразумевает, что компилятор знает размер того, на что ссылается ссылка.

ты что-то не то курил сегодня. Возьми массив в N элементов. Можно адрес взять? Можно. А можно сделать v++? Нельзя, если ты не знаешь N.

Можешь рассматривать void* как массив из нескольких char'ов. Точное количество этих char'ов неизвестно.

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

Как-то я лихо асм-листинг урезал.

leaq	-16(%rbp), %rcx ;; сохраняем адрес p в rcx
leaq	-4(%rbp), %rdx  ;; сохраняем адрес x в rdx
movl	$5, -4(%rbp)    ;; помещаем пятёрку в x
movq	%rdx, -16(%rbp) ;; помещаем rdx (=адрес x) в p
movq	%rcx, -24(%rbp) ;; помещаем rcx (=адрес p) в pp

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

При том, что взяитие адресса — это частный случай арифметики.

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

Оно просто не имеет смысла, ты не можешь сделать v++, если v это void *, т.к. это подразумевает, что компилятор знает размер того, на что ссылается ссылка.

Но ведь компилятор совершенно точно знает размер значения, на которое ссылается void**. Это sizeof(void*). Он равен sizeof(int*), sizeof(char*) и sizeof(uint32_t*), потому что размер всех указателей (на данные, по крайней мере, в отличие от указателей на функции) должен быть одинаковый.

Поправка. Стандарт в самом деле не гарантирует, что все указатели будут одинакового размера, но основной мой тезис остаётся в силе: указатель на void, в отличие от самого void, должен иметь вполне конкретный размер.

proud_anon ★★★★★
()
Последнее исправление: proud_anon (всего исправлений: 2)
int funcname(void *pointer, int size) {
    void *result = malloc(size);
    if (!result) return -1;
    *((void**)pointer) = result;
    return 0;
}

int *block;
if (funcname(&block, sizeof(int)) == -1) return -1;
Legioner ★★★★★
()
Ответ на: комментарий от emulek

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

всегда рад помочь :)

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

А void ** не имеет смысла в двойне. Т.к. void *** == void ** == void *

Ага, а если я массив указателей опишу? А если это будут нетипизированные указатели? Вот прямо так:

void *untyped_pointers_vector[10];

Напомню, a[ b ] есть то же самое, что *(a+b), причём всегда. Если бы void** был тем же самым, что и void* (над которым, как совершенно верно подмечено, запрещена арифметика — но лишь потому, что sizeof(void) не определён, чего никак нельзя сказать о sizeof(void*)), то к элементам вышеописанного массива было бы невозможно обращаться, то есть ваще никак.

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

УБИВАТ!

Нужно передавать указатель на указатель, чтобы по заголовку функции было понятно, что вы не буфер передаёте, а будете принимать указатель.

В качестве примера man: posix_memalign, pthread_join.

Sorcerer ★★★★★
()
Последнее исправление: Sorcerer (всего исправлений: 1)
Ответ на: комментарий от Legioner

Я знал, что вы так ответите, потому и написал: УБИВАТ!

Sorcerer ★★★★★
()
Ответ на: Да что ты говоришь! от Eddy_Em
pinkbyte@lgentoo4 ~ $ gcc 1.c 
pinkbyte@lgentoo4 ~ $ ./a.out 
data:
  u8      u16     u32     u64
0       0       0       0
1       256     16777216        72057594037927936
2       512     33554432        144115188075855872
3       768     50331648        216172782113783808
4       1024    67108864        288230376151711744
5       1280    83886080        360287970189639680
6       1536    100663296       432345564227567616
7       1792    117440512       504403158265495552
8       2048    134217728       576460752303423488
9       2304    150994944       648518346341351424
10      2560    167772160       720575940379279360
11      2816    184549376       792633534417207296
12      3072    201326592       864691128455135232
13      3328    218103808       936748722493063168
14      3584    234881024       1008806316530991104

Ты ничего с переполнением^W старшинством бит не попутал? :-)

pinkbyte@lgentoo4 ~ $ uname -m
s390x

Те же яйца, вид сбоку:

pinkbyte@timberdoodle ~ $ ./a.out 
data:
  u8      u16     u32     u64
0       0       0       0
1       256     16777216        16777216
2       512     33554432        33554432
3       768     50331648        50331648
4       1024    67108864        67108864
5       1280    83886080        83886080
6       1536    100663296       100663296
7       1792    117440512       117440512
8       2048    134217728       134217728
9       2304    150994944       150994944
10      2560    167772160       167772160
11      2816    184549376       184549376
12      3072    201326592       201326592
13      3328    218103808       218103808
14      3584    234881024       234881024
pinkbyte@timberdoodle ~ $ uname -m
ppc64
Pinkbyte ★★★★★
()
Последнее исправление: Pinkbyte (всего исправлений: 3)
Ответ на: комментарий от Pinkbyte

У тебя конечность другая что ли?

Ну так тоже проблем нет: ставь другой начальный адрес (начни не с 0, а с 3):

	char *X = calloc(15, sz), *ptr = X + 3;
	for(i = 0; i < 15; i++, ptr += sz)
		*ptr = i;

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

Легче стало, но не особо:

pinkbyte@lgentoo4 ~ $ ./a.out 
data:
  u8      u16     u32     u64
0       0       0       0
0       0       1       4294967296
0       1       2       8589934592
0       2       3       12884901888
1       3       4       17179869184
2       4       5       21474836480
3       5       6       25769803776
4       6       7       30064771072
5       7       8       34359738368
6       8       9       38654705664
7       9       10      42949672960
8       10      11      47244640256
9       11      12      51539607552
10      12      13      55834574848
11      13      14      60129542144

Может потому что на 3 надо сдвигать если байт 4, а если их 8(u64) - то надо сдвигать на 7? :-)

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

Понятное дело: для big-endian с произвольными типами надо переделать функцию так:

	char *X = calloc(15, sz), *ptr = X + sz - 1;
	for(i = 0; i < 15; i++, ptr += sz)
		*ptr = i;

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Pinkbyte

Так это на эмуляторах или у тебя реально есть такие архитектуры?

Потому как я живьем ничего кроме остроконечных интелоподобных и тупоконечного ARM'а (STM32, кубитрак и малинка) не видел!

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

И вообще, эта фиговина была лишь наглядным доказательством того, что void** имеет смысл!

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

Eddy_Em ☆☆☆☆☆
()

Передача в функцию int *, если она хочет void *

Именно поэтому индустрия до сих пор в жопе.

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

Так это на эмуляторах или у тебя реально есть такие архитектуры?

s390x - это виртуальная машина на реальном мэйнфрэйме, ppc64 - это сам сервак

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

если что, куча бытовой электроники и игровых консолей используют разные вариации powerpc. + старые маки.

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

АРМы все тупоконечные (big-endian), в отличие от интелей.

куча бытовой электроники и игровых консолей используют разные вариации powerpc. + старые маки.

Не знал.

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