2014-12-04 3 views
-1

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

Я хочу, чтобы иметь возможность передавать беззнаковый тип ЛЮБОГО размера в качестве контейнера и указать ограничение на то, насколько велика ценность, которую он должен удерживать. Прототип функции заключается в следующем:

str_to_uint(void *tar, const char *str, const uint64_t lim) 

uint64_t *tar то, где целое число без знака будет сохранено, *str это строка числа и uint64_t lim является пределом размеру *tar будет в состоянии держать.

с sizeof(tar) является переменной, безопасно ли отличать *tar в uint64_t *, а затем содержать преобразованную переменную в этом? Я знаю, что он всегда будет меньше фактического типа, так как я проверяю его с помощью переменной lim.

такое возможно?

в основном это свелось бы к этому

  1. Я имею переменную без знака типа, где sizeof(variable) равно 1, 2, 3 или 4.
  2. я передать переменную в функцию посредством (void *)&variable.
  3. В этой функции я передал ее uint64_t * и записал в нее обнаруженную переменную. Я убеждаюсь, что обнаруженная переменная может быть записана в переменную, если она меньше или равна lim

это разрешено?

код:

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

static int str_to_uint(uint64_t *tar, const char *str, const uint64_t lim) { 
    char *eptr = NULL; 
    unsigned long long int temp = 0; 

    if (str == NULL) { 
     printf("str is a NULL pointer\n"); 
     return -1; 
    } 

    temp = strtoull(str, &eptr, 10); 
    if (temp == 0 && eptr == str) { 
     printf("strtoull() conv err, %s\n", str); 
     return -1; 
    } else if (temp > lim) { 
     printf("strtoull() value to big to contain specified limit, %s\n", str); 
     return -1; 
    } else { 
     *tar = temp; 
    } 

    return 0; 
} 

int main() { 
    int ret; 

    uint8_t a; 
    uint16_t b; 
    uint32_t c; 
    uint64_t d; 

    ret = str_to_uint((void *)&a, "22", UINT8_MAX); 
    if (ret != 0) { 
     exit(1); 
    } 

    ret = str_to_uint((void *)&b, "22", UINT16_MAX); 
    if (ret != 0) { 
     exit(1); 
    } 

    ret = str_to_uint((void *)&c, "22", UINT32_MAX); 
    if (ret != 0) { 
     exit(1); 
    } 

    ret = str_to_uint((void *)&d, "22", UINT64_MAX); 
    if (ret != 0) { 
     exit(1); 
    } 

    printf("a = %"PRIu8"\nb = %"PRIu16"\nc = %"PRIu32"\nd = %"PRIu64"\n", a, b, c, d); 

    exit(0); 

} 
+0

Это не будет вести себя так, как вы хотите, на маленькой конечной машине. –

+0

«безопасно ли выставлять * tar на uint64_t *, а затем содержать преобразованную переменную?» Нет, это не так. «tar» не гарантируется, что он правильно выровнен и вызовет неуравновешенную проблему с доступом. Если вы хотите получить значение, используйте ['intprt_t' и' uintptr_t'] (http://en.cppreference.com/w/c/types/integer), в противном случае указатели объектов могут быть только безопасно преобразованы 'void *' –

ответ

3

Нет, конечно, вы не можете сделать это.

Если я называю вашу функцию так:

uint8_t my_little_value; 
str_to_uint(&my_little_value, "4711", sizeof my_little_value); 

Тогда вы

uint64_t *user_value = tar; 
*user_value = ...; 

Boom, вы перезаписана кучу байтов вы не разрешается трогать. Конечно, вы знали это, так как я передал вам размер моей переменной, и вы говорите, что «убедитесь», но я не вижу, как вы намереваетесь это сделать, если ваш подход будет обрабатывать tar как uint64_t *.

Я не понимаю, почему вы не можете просто вернуть преобразованное число, например strtoul(). Это несет ответственность за несоответствие между местом хранения и потенциальной точностью для представления преобразованного числа на пользователя (или даже на компиляторе!), Где он принадлежит. Ваш предлагаемый API очень подвержен ошибкам и трудно понять.

+0

вообще Я сделал это, потому что есть большая вероятность, что я могу вызвать функцию с помощью '* str == NULL' и потому, что я не хочу, чтобы значение orignal было затронуто, если оно не может быть преобразовано. Конечно, я мог бы делать временные переменные за пределами функции, но на самом деле я ленив, поэтому не хотел этого делать –

+0

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

+0

вы не можете просто сказать «Я ленив», чтобы не делать что-то. Это может относиться к безопасности, безопасности ... И не использовать обратные ссылки для апострофов '' ' –

0

В этой строке кода:

*tar = temp; 

Вы пишете 8 байт данных в переменную, которая может быть меньше, чем 8 байт.Вам нужно обрабатывать каждый размер отдельно, например, для 1 байт:

*(uint8_t *) tar = temp; 
Смежные вопросы