2013-09-02 4 views
7

Код:тетср пустоты указатель на объединение

union foo 
{ 
    char c; 
    int i; 
}; 

void func(void * src) 
{ 
    union foo dest; 
    memcpy(&dest, src, sizeof(union foo)); //here 
} 

Если я называю func() так:

int main() 
{ 
    char c; 
    int i; 
    func(&c); 
    func(&i); 
    return 0; 
} 

В вызове func(&c), размер c меньше sizeof(union foo), которые могут быть опасно, правда?

Есть ли линия с memcpy правильно? А если нет, то как это исправить?

Что я хочу, это безопасный звонок memcpy, который скопировал указатель void * на номер union.


Немного предыстории: это извлекается из очень сложной функции, а также подпись func() включая параметр void * находится вне моего контроля. Конечно, пример ничего не полезен, потому что я удалил весь код, который не имеет отношения к тому, чтобы предоставить пример с минимальным кодом.

+4

этот код не имеет смысла. что ты хочешь делать? –

+2

Memcpy НЕ ДОЛЖЕН. Неопределенное поведение не ограничивается только * письмом * в областях памяти, которые у вас нет. * Доступ к * им ** вообще ** - неопределенное поведение. То, что он может не сбой (и на некоторых платформах он * будет *) не имеет значения. – WhozCraig

+0

@KarolyHorvath Вы имели в виду пример кода ничего полезного? Если это так, это потому, что я только извлек часть, с которой я сомневаюсь. Я добавил немного фона, я не знаю, достаточно ли этого. –

ответ

3

Правильно ли линия с memcpy? А если нет, то как это исправить?

вы должны пройти размер памяти, на который указывает пустой указатель, так что вы можете знать src имеет столько размер, так что вы просто должны скопировать столько данных ...

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

+0

@MvG о, спасибо ... после почти двух лет я понимаю это. –

+0

«* ... так что вы можете знать, что у dest есть такой размер, поэтому вам просто нужно скопировать большую часть данных ... *": Это опечатка? Вы имеете в виду 'src' как' dest', не так ли? Согласно вашему 1-му предложению, это: «* ... передать размер назначения ... *» следует читать: «* ...пропустите размер источника ... * ", не так ли? – alk

+0

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

6

В вызове func(&c), размер c меньше sizeof(union foo), что может быть опасно, правда?

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

Но позвольте мне быть более конкретным. Согласно стандарту C, запись dest.c, но чтение dest.i будет всегда выход неопределенного поведения. Но большинство компиляторов на большинстве платформ также будут иметь четкое поведение для этих случаев. Так часто пишет dest.c, но чтение dest.i имеет смысл, несмотря на то, что говорит стандарт. В этом случае, однако, чтение от dest.i по-прежнему будет зависеть от неизвестных окружающих переменных, поэтому оно не определено не только с точки зрения стандартов, но и в очень практическом смысле.

Существует также редкий сценарий, который вы должны учитывать: c может быть расположен в самом конце выделенных страниц памяти. (Это относится к страницам памяти, выделенным из операционной системы и, в конечном итоге, к устройству управления памятью (MMU), а не к блочному распределению пространства пользователя, сделанному malloc и друзьям.) В этом случае чтение более одного байта может привести к доступ к немаркированной памяти и, следовательно, вызывают серьезную ошибку, скорее всего, сбой программы. Учитывая расположение вашего c в качестве автоматической переменной в главном, это кажется маловероятным, но я полагаю, что этот фрагмент кода является лишь примером.

Является ли линия с memcpy правильной? А если нет, то как это исправить?

В зависимости от того, что вы хотите сделать. Как бы то ни было, код не имеет особого смысла, поэтому я не знаю, какое правильное разумное приложение вы могли бы иметь в виду.Возможно, вам необходимо передать sizeof объект src на номер func.

+0

По модулю отображения памяти, вы имеете в виду модуль управления памятью (MMU)? – Martin

+0

@ Мартин: Да, вот что я имел в виду. Будет редактировать, чтобы исправить это. – MvG

1

memcpy в порядке. Пропустив адрес наименьшего члена профсоюза, вы закончите мусор в большем члене. Способ избежать бит мусора состоит в том, чтобы по умолчанию сделать все вызовы func - что я предполагаю, что вы выполняете управление - используйте только указатели на более крупный элемент - этого можно добиться, установив более крупный элемент на меньший: i = c, а затем позвоните по телефону func(&i).

+0

['memcpy()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/memcpy.html) не очень хорошо, потому что то, что передается для копирования из _isn't_ a union. –

+0

@ElchononEdelson: mem = memory - не требует контент, на который указывает, что-либо ... – slashmais

+1

Конечно, если это так, то передайте указатель на однобайтовый объект и скажите 'memcpy()' вытащить из этого указателя четыре байта, это Undefined. –

0

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

union foo 
{ 
    char c; 
    int i; 
}; 

void func(void * src, const char * type) 
{ 
    union foo dest; 

    if(strcmp(type, "char") == 0){ 
     memcpy(&dest, src, 1); 
    }else if(...){ 

    } 
} 
+0

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

+0

Я согласен с вами, я просто хочу привести пример, сравнение строк не очень хорошо. – MYMNeo

1

func сам в порядке.

Проблема заключается в том, действительно ли вызывающий должен убедиться, что память, на которую ссылаются при вызове func(), составляет не менее sizeof(union foo).

Если последнее всегда так, все в порядке. Это не случай для двух вызовов func() в примере OP.

Если память, на которую ссылаются при вызове func(), меньше sizeof(union foo), то memcpy() провоцирует неопределенное поведение.

+0

На самом деле, нет, все в порядке. Он предполагает информацию о читаемости следующих 3 байтов, которые вызывающий не гарантирует. –

+0

Разве я не написал это? @ChrisStratton (И почему вы ссылаетесь на «* три *» байта?) – alk

Смежные вопросы