0

Как всегда программирую под Android NDK.

Задача

Есть строка размером 32 байта. Нужно скопировать две подстроки по 8 байт с определенной позиции, а затем получить код каждого символа.

Написал код:

const char* str = "0a88d2e044ed675309eb79c6aa988ce2";

char* test1(){
    char k[17];

    strncpy(k, &str[3], 8);
    strncpy(k + 8, &str[8], 8);

    k[16] = 0;
    return k;
}

char* test2(){
    char* k = (char*) malloc(17 * sizeof(char));
    memset(k, 0, 17);

    memcpy(k, str + 3, 8);
    memcpy(k + 8, str + 8, 8);

    return k;
}

При вызове функций получаю одинаковый результат: 8d2e044e44ed6753.

Перебираю результат и получаю код символа:

c = test1 = test2
8 = 0     =  56
d = 17    =  100
2 = 0     =  50
e = 0     =  101
0 = 0     =  48
4 = 0     =  52
4 = 0     =  52
e = 0     =  101
4 = 0     =  52
4 = 17    =  52
e = 0     =  101
d = 0     =  100
6 = 0     =  54
7 = 17    =  55
5 = 0     =  53
3 = 0     =  51

test1 использует strncpy на экране — одно, а код — другой (видимо, кодировка своя какая-то).

Решил использовать test2, появились вопросы:

  1. Обнуляю выделенную память в начале через memset. Как ее обнулить после расширения памяти realloc-ом? Или, например, выделил памяти 30 байт, использовал 10, как поставить 0 в конце строки, чтобы не получить весь хлам до первого нуля?

  2. Функция test2 вернула результат, я что-то сделал. Как освободить выделенную внутри test2 память? Если освободить память так (см. ниже), то при следующем вызове функция вернет всякий хлам.

    char* test2() {
        char* k = (char*) malloc(17 * sizeof(char));
        memset(k, 0, 17);
        memcpy(k, str + 3, 8);
        memcpy(k + 8, str + 8, 8);
        const char* nk = k;
        free(k);
        return nk;
    }
  3. Как получить размер выделенной памяти?

    char* k = (char*) malloc(17 * sizeof(char));

    Результат sizeof(k) говорит о том, что выделено 8 байт. Но, если я правильно понимаю, в результате должно быть 17 байт.

eanmos
  • 6,651
Ivan
  • 556
  • 2
  • 14
  • 4 - sizeof не получает размер выделеных данных. Она показывает размер переменной в байтах. У вас ссылочный тип, размером 8 байт - т.е. 64-битная ссылка. Или возможно 32-seg:32-ofs. В случае [4] размер 17*sizeof(char) – nick_n_a Jun 13 '18 at 07:38
  • В test2 возвращается мертвый указатель free(k); return nk; освобождать память необходимо после работы с указателем ,т.е. где-то в main, т.е. указатель который вернула функция необходимо освобождать. –  Jun 13 '18 at 07:48
  • @LLENN как быть если результат функции возвращается в java? – Ivan Jun 13 '18 at 08:00
  • а JNI тут причём? Вы основ языка не понимаете, с этим у вас и проблемы. Если ява кидает вам const char*, зачем его пытаться чистить? – test123 Jun 13 '18 at 08:02
  • @test123 const char* объявлен в jni(си).получаю подстроку и возвращаю ее в java/ – Ivan Jun 13 '18 at 08:03
  • const char* объявлен в jni<< Настоятельно рекомендую ознакомиться с основами. Вы не понимаете что ваши шаманства над константными указателями - это нерабочий код.

    – test123 Jun 13 '18 at 08:06
  • @test123 а где мне еще секретный код объявить?В java что ли?Смысл тогда jni использовать? – Ivan Jun 13 '18 at 08:07
  • @Ivan, ваш вопрос по чистому си. На него я ответил по каждому из пунктов. Если вам нужны вопросы по jni (который, к вашему сведению, умеет и в c++), задавайте другой вопрос, в котором подробно раскройте что вы сделали, чего хотите, и что не получилось. – test123 Jun 13 '18 at 08:09
  • @test123 20 вчера задовал день и тишина я его удалил.Походу когда про jni слышат не отвечают вообще.Из-за использования c++ либы весят в 20 раз больше. – Ivan Jun 13 '18 at 08:11
  • @test123 Да и вопрос же задал как Android NDK - значит JNI. – Ivan Jun 13 '18 at 08:14
  • @Ivan, из вопроса совсем не очевидно, что и куда вы возвращаете. – eanmos Jun 13 '18 at 08:17
  • 20 вчера задовал день и тишина я его удалил<< А зачем удалили? Люди и по 3 дня ждут, и по неделе. Если вопрос понятен и логичен, на него, рано или поздно, ответят. >>Да и вопрос же задал как Android NDK - значит JNI<< Почему? А я думаю значит апельсин. Почему сразу jni, почему не Rust? Я пишу в адроиде на чистых сях, или вы хотите убедить меня что это всё неправда, и там есть только java?

    – test123 Jun 13 '18 at 08:17
  • Функция test1 возвращает мертвый указатель - указатель на локальную переменную. Потому у вас "коды символов" для первого массива и получились бессмысленные. – AnT stands with Russia Jun 19 '18 at 14:47

1 Answers1

2

1 Нельзя узнать размер объекта по указателю. Ваша ошибка в том, что вы не храните этот размер самостоятельно. Используйте, например, такую логику:

typedef struct MyData {
    int   size;
    char *data;
} MyData;

MyData *CreateData(int size){
    MyData *tmp = (MyData*)malloc(sizeof(MyData));

    if(tmp==NULL) // Что то пошло не так...
        exit(-1);

    tmp->data = (char*)malloc(size);

    if(tmp->data==NULL) // Что то пошло не так...
        exit(-1);

    tmp->size = size;
    return tmp;
}

void ReallocData(MyData *data, int size){
    char *tmp = (char*)realloc(data->data, size);

    if(tmp==NULL) // Что то пошло не так...
        exit(-1);

    data->data = tmp;
    data->size = size;
}

void DeleteData(MyData *data){
    free(data->data);
    free(data);
}

Соответственно, никаких проблем не будет с определением текущего размера. По поводу memset, аналогично, зная размеры выделенной памяти всё становится понятным.

2 Вы неверно сделали логику функции. вот пример основанный на логики первого пункта ответа:

MyData *test2() {
    MyData *tmp = CreateData(17);
    memset(tmp->data, 0, 17);
    memcpy(tmp->data, str + 3, 8);
    memcpy(tmp->data + 8, str + 8, 8);
    return tmp;
}

MyData *data = test2(); // получаем данные
... // работаем с данными

DeleteData(data); // чистим за собой

3 Вы удаляете данные, которые хотите вернуть. Это неверное решение. Данные нужно удалять когда они уже не нужны.

4 Ваша фраза ошибочна:

sizeof(k) говорит выделили 8 байт

sizeof от указателя возвращает размер указателя. А объект у вас char, его sizeof всегда 1 байт. Первый пункт объясняет как "измерять" данные, которые вы выделили - Никак. Только запоминать самому.

test123
  • 1,084
  • есть различия между MyData test2 и MyData test2?Как освобождать память если test2 вызывать из java и туда же вернуть результат?DeleteData(data) поле return уже не вызовешь же. – Ivan Jun 13 '18 at 08:02
  • @Ivan, Поясните, вы намерены отдавать яве char* указатель? – test123 Jun 13 '18 at 08:03
  • char строку хотел.Просто если функция возвратит char то он будет пуст. – Ivan Jun 13 '18 at 08:04
  • В этом си не поймешь когда что и как. – Ivan Jun 13 '18 at 08:06
  • 1
    @Ivan: В этом си не поймешь когда что и как. наоборот, в нем все чисто и понятно, в отличии от java c# и тому подобных языков где нет ручного управления памятью, вы не знаете когда объект есть, а когда его на самом деле, а в си, вы сами их создаете выделяя память под них, и уничтожаете только тогда когда в этом есть необходимость. –  Jun 13 '18 at 08:46
  • У вас в realloc ошибка. – 0andriy Jun 17 '18 at 15:26
  • @0andriy, подскажите в чём именно? Судя описанию http://en.cppreference.com/w/c/memory/realloc и нескольким проверкам в mingw https://onlinegdb.com/Hy5zmRVW7 всё работает как планировалось, или есть какие то подводные камни? – test123 Jun 18 '18 at 06:38
  • Вы перетираете предыдущий результат новым. Представьте теперь, что в какой-то момент произошло чудо, и вам явился NULL... Дальше рассказывать? (Понятно, что такой случай скорее всего редок, но тем не менее) – 0andriy Jun 18 '18 at 21:35
  • @LLENN, посмотрите на Rust, там всё понятно со временем жизни объектов. – 0andriy Jun 18 '18 at 21:37
  • @0andriy, >>Вы перетираете предыдущий результат новым<< Если вы хотите предложить самостоятельную реализацию функции realloc - это уже не связанная с вопросом автора проблема). Если я не заблуждаюсь, подобная ошибка может произойти из-за проблем с ОЗУ, сильной фрагментации, или вообще из-за нехватки памяти, думаете, в такой ситуации можно ещё что то сделать? – test123 Jun 19 '18 at 07:05
  • Да, если ваша программа основной потребитель памяти, например, или ситуация с нехваткой памяти временная (другой процесс что-то делает по этому поводу). То, что вы пишете, равносильно «давайте вообще не будем проверять результат вызова *alloc(), так как сделать уже ничего нельзя». Но по сути вы же проверяете, что вернул malloc(). – 0andriy Jun 19 '18 at 10:01
  • @0andriy, Большое спасибо за информацию, буду иметь ввиду) – test123 Jun 19 '18 at 11:18
  • @0andriy, исправил код – test123 Jun 19 '18 at 11:23
  • @test123, кстати, не стоит приводить результат функций *alloc к какому-либо типу. – eanmos Jun 19 '18 at 12:22
  • @eanmos, >>не стоит приводить<< Да, это привычка от плюсов. В данном вопросе оно, действительно, лишнее. – test123 Jun 19 '18 at 13:12
  • @test123, пожалуйста. Это (шаблон вызова realloc()) классическая ошибка в программировании на C. Ну код, вы, кстати, поправили неверно. Нужна временная переменная. – 0andriy Jun 19 '18 at 15:08
  • @test123, что-то типа temp = realloc(); \ if (temp == NULL) ... else data = temp. – eanmos Jun 19 '18 at 15:41
  • @0xdb, я не очень понял ваше действие. Обяснитесь? – 0andriy Jun 19 '18 at 22:39
  • Я (с ошибкой, правда) поправил вам в версии 3. Однако её вернули к версии 2. – 0andriy Jun 19 '18 at 22:41
  • @0andriy Вы изменили код в ответе и я его вернул. Подобные правки обычно отклоняются. – 0xdb Jun 19 '18 at 22:44
  • @0xdb, Почему? (Ладно, допустим в этой версии была ошибка) – 0andriy Jun 19 '18 at 22:45
  • @0andriy Код должен менять автор. Даже если он ошибочный, предоставте автору исправить его. Вы можете или дать комментарий, или дать свой ответ, или проголосовать минусом. – 0xdb Jun 19 '18 at 22:52
  • 0xdb, Есть ли где-нибудь устав ruSO или что-то подобное, где описаны чёткие и понятные критерии, например, для чего используется кнопка "править" у категории "ответов"? – 0andriy Jun 19 '18 at 22:55
  • @0andriy Есть конечно, в справке тут, и на мете много дискусий с меткой предлагаемые-правки. PS Не забывайте @ перед именем, а то не будет оповешения. – 0xdb Jun 19 '18 at 23:31