На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> В буфере появляется лишний символ
    Здравствуйте!
    В программе перевода в двоичное число использую 2 функции:
    ExpandedWrap disabled
      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
       
      template <class T> void TypeToBin(T source, char* buffer, int size)
      {
        buffer += size - 1;
        for (int i = size - 1; i >= 0; i--)
        {
          *buffer-- = (source & 1) + '0'; // '0' - 48 '1' - 49  printf("%p\n", buffer);
          source >>= 1;
        }
      }
       
      template <class T> char* TypeToBin2(T source)
      {
        int const size = 8 * sizeof(T);
        char *p = (char*) malloc(size + 1);
        p[size] = '\0';
        p += size - 1;
        for (int i = size - 1; i >= 0; i--)
        {
          *p-- = (source & 1) + '0'; // '0' - 48 '1' - 49  printf("%p\n", buffer);
          source >>= 1;
        }
        return p;
      }
       
      int main()
      {
        char a = 'a';
        //int a = 1;
        int const size = 8 * sizeof(a);
        char buffer[size + 1];
        buffer[size] = '\0';
        TypeToBin(a, buffer, size);
        printf("a = %s  strlen = %d\n", buffer, (int) strlen(buffer));
        printf("a = %s\n", TypeToBin2(a));
        printf("\n");
        system("pause");
        return EXIT_SUCCESS;
      }
    Первая функция TypeToBin выводит строку "a = 01100001 strlen = 8", а у второй функции TypeToBin2 вывод a = ¤01100001 содержит в начале какой-то символ. Подскажите, пожалуйста, какую ошибку я допустил в этой функции. Проект прикрепил. В архиве файлы проектов дли VС++, CodeBlocks, QtCreator. Причем программы, созданные VC++ и QtCreator, выводят этот дополнительный символ в консоль, а CodeBlocks - нет.
    Прикреплённый файлПрикреплённый файлcppLearning.zip (3,01 Кбайт, скачиваний: 23)
      Ну так у тебя p постинкрементится, чтобы в начале следующей итерации указывать на очередной свободный символ, но когда цикл завершается, это свободное место не используется, и там остаётся мусор. Замени, например, на прединкремент, чтобы позиция свободного символа вычислялась на текущей итерации, а не предыдущей.
      ExpandedWrap disabled
          p += size;
          for (int i = size - 1; i >= 0; i--)
          {
            *--p = (source & 1) + '0'; // '0' - 48 '1' - 49  printf("%p\n", buffer);
            source >>= 1;
          }
        Спасибо большое за ответ. Так получилось. Но возникли следующие вопросы:
        1. Почему в первой функции такого не происходит? Из-за фиксированного размера буфера?
        2. В строке *--p = (source & 1) + '0' или в строке p[size] = '\0'; появляется предупреждение VC++ "Разыменование пустого указателя, но все работает нормально. Что нужно сделать, чтобы это предупреждение исчезло?
        Сообщение отредактировано: tumanovalex -
          Разобрался по своему глупому 1 вопросу - потому что не возвращаю указатель. Если его возвращать, то результат тот же самый. А вот по вопросу № 2 не разобрался.
            Формально malloc() может вернуть NULL. Подозреваю, что ему не нравится тот факт, что нет на это поверки

            Добавлено
            P.S. Вторая функция в целом плохо спроектирована. Во-первых, динамический буфер от malloc() должен освобождаться. Помимо того, что я нигде не вижу free(), а увидеть я его мог бы только на вызывающей стороно, т.е. в данном случае в main(), так ещё и вызываемому коду неоткуда взять параметр для free(), т.к. ей приходит не тот же указатель, что вернула malloc(), а насколько-то там увеличенный.
            Сообщение отредактировано: Qraizer -
              Переделал функции:
              ExpandedWrap disabled
                template <class T> void TypeToBin1(T source, char* buffer, int size)
                {
                  buffer += size;
                  for (int i = size - 1; i >= 0; i--)
                  {
                    *--buffer = (source & 1) + '0'; // '0' - 48 '1' - 49  printf("%p\n", buffer);
                    source >>= 1;
                  }
                }
                 
                template <class T> void TypeToBin2(T source, char* buffer, int size)
                {
                  for (int i = size - 1; i >= 0; i--)
                  {
                    buffer[i] = (source & 1) + '0';
                    source >>= 1;
                  }
                }
              Функции работают нормально. Но хотелось бы сделать так, чтобы в функцию передавать только source, размер типа и буфер создавать в функции и иметь возможность содержимое буфера передавать вызывающей стороне, что-то типа такого:
              ExpandedWrap disabled
                template <class T> char* TypeToBin3(T source)
                {
                  int const size = 8 * sizeof(T);
                  char buffer[size + 1];
                  buffer[size] = '\0';
                  for (int i = size - 1; i >= 0; i--)
                  {
                    buffer[i] = (source & 1) + '0';
                    source >>= 1;
                  }
                  return buffer;
                }
              В таком виде printf("TypeToBin3 a = %s\n", TypeToBin3(a)) не возвращает правильное содержимое буфера. Подскажите, пожалуйста, как правильно вернуть содержимое буфера, созданного в функции (если это вообще возможно).

              Добавлено
              Я имел ввиду буфер, который не создается в динамической памяти

              Добавлено
              Цитата Qraizer @
              Формально malloc() может вернуть NULL. Подозреваю, что ему не нравится тот факт, что нет на это поверки

              Добавлено
              P.S. Вторая функция в целом плохо спроектирована. Во-первых, динамический буфер от malloc() должен освобождаться. Помимо того, что я нигде не вижу free(), а увидеть я его мог бы только на вызывающей стороно, т.е. в данном случае в main(), так ещё и вызываемому коду неоткуда взять параметр для free(), т.к. ей приходит не тот же указатель, что вернула malloc(), а насколько-то там увеличенный.

              А как правильно это делается? Надо передавать буфер и параметры, созданные вызывающей стороной? Чтобы она их и освободила?
                Цитата tumanovalex @
                Функции работают нормально. Но хотелось бы сделать так, чтобы в функцию передавать только source, размер типа и буфер создавать в функции и иметь возможность содержимое буфера передавать вызывающей стороне, что-то типа такого:
                ...
                В таком виде printf("TypeToBin3 a = %s\n", TypeToBin3(a)) не возвращает правильное содержимое буфера. Подскажите, пожалуйста, как правильно вернуть содержимое буфера, созданного в функции (если это вообще возможно)
                Естественно неправильно. Тут buffer[] является локальным автоматическим объектом, чьё время жизни заканчивается на return, т.ч. возвращаемый указатель указывает в никуда.
                На предмет того, чтобы сделать всё правильно, вариантов уйма, и все в той или иной степени подойдут или нет в зависимости от предпочтений. Основная идея – у буфера должен быть владелец.
                1. Внешний владелец – вызывающий код. Он его создаёт, он его освобождает, когда посчитает нужным, он указывает, куда помещать результат. Так работает TypeToBin1(). Но так тебе неудобно.
                2. Внутренний владелец – вызываемый код. Он создаёт буфер нужного размера, заполняет его результатом, но удалить его не может, т.к. он нужен вызывающему. Значит он должен передать владение ему, и далее вызывающий будет решать, когда его удалять. Так работает TypeToBin2(), но неправильно.
                3. Владельцем является сам буфер, и он сам знает, когда уничтожиться. Тут не обойтись без того, чтобы спроектировать архитектуру такого буфера и реализовать её. Обычно, раз у тебя C++, разумнее всего сделать сие классом.
                Вылечить TypeToBin2() несложно, нужно лишь возвращать результат malloc(), а не какой-то другой, и тогда вызывающий код легко сделает free(), когда ему будет удобно. Это означает, что TypeToBin2() придётся переместить результат к началу буфера, чтобы впереди не было мусора. Но если уж говорить за третий вариант, то у нас уже есть готовые владельцы, например std::vector<> или std::string, так что своего ничего писать не обязательно. Например:
                ExpandedWrap disabled
                  template <class T> std::string TypeToBin3(T source)
                  {
                    int const size = 8 * sizeof(T);
                    std::string p(size, ' ');
                   
                    for (int i = size - 1; i >= 0; i--)
                    {
                      p[i] = (source & 1) + '0';
                      source >>= 1;
                    }
                    return p;
                  }
                std::string сам владеет своим буфером, умеет им управлять и знает, когда его нужно освобождать: когда он сам разрушается. Примерно то же будет с std::vector<char>. Вообще, я давно забыл, когда ловил баги утечки памяти, просто никогда не использую ни malloc()/free(), ни new/delete. Классы, владеющие своими буферами, всё делают за меня и не ошибаются.
                  Спасибо большое за подробные объяснения и код!
                    Дополнил код считыванием и установкой битов:
                    ExpandedWrap disabled
                      #include <string>
                       
                      template <class T> std::string TypeToBin(T source)
                      {
                        int const size = 8 * sizeof(T);
                        std::string str(size, ' ');
                       
                        for (int i = size - 1; i >= 0; i--)
                        {
                          str[i] = (source & 1) + '0';
                          source >>= 1;
                        }
                        return str;
                      }
                       
                      template <class T> int GetBit(T source, int nBitNumber)
                      {
                        int mask = 1 << nBitNumber;
                        source = mask & source;
                        if(source)
                          return 1;
                        else
                          return 0;
                      }
                       
                      template <class T> T SetBit(T source, int nBit, int nBitValue)
                      {
                        int mask = 1 << nBit;
                        if (nBitValue == 0)
                        {
                          mask = ~mask;
                          source = source & mask;
                        }
                        else
                        {
                          source = source | mask;
                        }
                        return source;
                      }
                       
                      int main()
                      {
                        //char a = 'a';
                       //int a = 257;
                        short a = -257;
                        int nBit = 15;
                        int nBitValue = 0;
                        printf("GetBit%d = %d\n", nBit, GetBit(a, nBit));
                        printf("TypeToBin a  = %s\n", TypeToBin(a).c_str());
                        short res = SetBit(a, nBit, nBitValue);
                        printf("res = %d\n", res);
                        printf("GetBit%d = %d\n", nBit, GetBit(res, nBit));
                        printf("TypeToBin a2 = %s\n", TypeToBin(res).c_str());
                        system("pause");
                        return EXIT_SUCCESS;
                    Буду благодарен за любые замечания и предложения. Возник вопрос: можно ли сделать так, чтобы переменная res в main, которой присваивается результат SetBit, зависела от типа первого параметра этой функции? А то при изменении типа анализируемой переменной каждый раз придется изменять и тип переменной res. Проект прикрепил.
                    Прикреплённый файлПрикреплённый файлcppLearning.zip (3,56 Кбайт, скачиваний: 26)
                      Цитата tumanovalex @
                      Возник вопрос: можно ли сделать так, чтобы переменная res в main, которой присваивается результат SetBit, зависела от типа первого параметра этой функции?

                      Можно. Вот так
                      ExpandedWrap disabled
                        int main()
                        {
                          //char a = 'a';
                         //int a = 257;
                          short res, a = -257;
                          int nBit = 15;
                          int nBitValue = 0;
                          printf("GetBit%d = %d\n", nBit, GetBit(a, nBit));
                          printf("TypeToBin a  = %s\n", TypeToBin(a).c_str());
                          res = SetBit(a, nBit, nBitValue);
                          printf("res = %d\n", res);
                          printf("GetBit%d = %d\n", nBit, GetBit(res, nBit));
                          printf("TypeToBin a2 = %s\n", TypeToBin(res).c_str());
                          system("pause");
                          return EXIT_SUCCESS;
                        }
                        Спасибо! Как я сам не сообразил!
                          Цитата tumanovalex @
                          Буду благодарен за любые замечания и предложения
                          Работает – и ладно. Я б не так писал. Например, SetBit() используется для установки бита в указанное значение, однако мы привыкли, что установка бита – это конкретно в 1, а если в 0, то это сброс бита. Поэтому интуитивнее видеть не одну функцию, а две setBit() и clearBit() и без указания значения, и так понятно, для какой какое. Или вот GetBit() формально возвращает значение бита, поэтому 0 или 1 понятно. Но мы привыкли, что бит либо взведён либо сброшен, поэтому вместо получения значения и сравнения с чем-то там, просто спрашиваем, взведён ли, и в ответ получаем bool. Но это на любителя.
                          Но вот что не на любителя... попробуй передать отрицательный номер бита или с большим значением, например, 57. По Стандарту это неопределённое поведение, так что результаты могут удивить. Или вот смешивание C и C++. Зачем printf(), если есть std::cout? А недавно нам Python-овый format() завезли, так вааще лафа.
                          Цитата tumanovalex @
                          Возник вопрос: можно ли сделать так, чтобы переменная res в main, которой присваивается результат SetBit, зависела от типа первого параметра этой функции?
                          std::any. Некое подобие динамической типизации. Не совсем, но очень похоже: в него можно положить всё, и он помнит, какой тип в него положен, однако при чтении нужно самому знать, что там. Нельзя положить, скажем int, а вытащить строку, бросит эксепшн. Ниже примерный код, основанный на decltype. Тоже очень удобная вещь, но статическая, в динамике тип не определяет.
                          ExpandedWrap disabled
                            int main()
                            {
                              char b = 'a';
                              int  c = 257;
                              short a = -257;
                              int nBit1 = 15;
                              int nBit2 = 0;
                              int nBitValue1 = 0;
                              int nBitValue2 = 1;
                             
                              printf("GetBit%d = %d\n", nBit1, GetBit(a, nBit1));
                              printf("TypeToBin a  = %s\n", TypeToBin(a).c_str());
                             
                              std::any res = SetBit(a, nBit1, nBitValue1);
                             
                              printf("res = %d\n", std::any_cast<decltype(a)>(res));
                              printf("GetBit%d = %d\n", nBit1, GetBit(std::any_cast<decltype(a)>(res), nBit1));
                              printf("TypeToBin a2 = %s\n", TypeToBin(std::any_cast<decltype(a)>(res)).c_str());
                             
                              printf("GetBit%d = %d\n", nBit2, GetBit(b, nBit2));
                              printf("TypeToBin b  = %s\n", TypeToBin(b).c_str());
                              res = SetBit(b, nBit2, nBitValue1);
                              printf("res = %d\n", std::any_cast<decltype(b)>(res));
                              printf("GetBit%d = %d\n", nBit2, GetBit(std::any_cast<decltype(b)>(res), nBit2));
                              printf("TypeToBin b2 = %s\n", TypeToBin(std::any_cast<decltype(b)>(res)).c_str());
                             
                              printf("GetBit%d = %d\n", nBit1, GetBit(c, nBit1));
                              printf("TypeToBin c  = %s\n", TypeToBin(c).c_str());
                              res = SetBit(c, nBit1, nBitValue2);
                              printf("res = %d\n", std::any_cast<decltype(c)>(res));
                              printf("GetBit%d = %d\n", nBit1, GetBit(std::any_cast<decltype(c)>(res), nBit1));
                              printf("TypeToBin c2 = %s\n", TypeToBin(std::any_cast<decltype(c)>(res)).c_str());
                             
                              system("pause");
                              return EXIT_SUCCESS;
                            }
                          Посмотри, мож получится применить.
                          Сообщение отредактировано: Qraizer -
                            Цитата tumanovalex @
                            можно ли сделать так, чтобы переменная res в main, которой присваивается результат SetBit, зависела от типа первого параметра этой функции?

                            Есть вариант, как если вместо std::any использовать auto. Вот переписанный вариант от Qraizer:

                            ExpandedWrap disabled
                              int main()
                              {
                                  char b = 'a';
                                  int  c = 257;
                                  short a = -257;
                                  int nBit1 = 15;
                                  int nBit2 = 0;
                                  int nBitValue1 = 0;
                                  int nBitValue2 = 1;
                               
                                  printf("GetBit%d = %d\n", nBit1, GetBit(a, nBit1));
                                  printf("TypeToBin a  = %s\n", TypeToBin(a).c_str());
                               
                                  auto res1 = SetBit(a, nBit1, nBitValue1);
                               
                                  printf("res = %d\n", res1);
                                  printf("GetBit%d = %d\n", nBit1, GetBit(res1, nBit1));
                                  printf("TypeToBin a2 = %s\n", TypeToBin(res1).c_str());
                                  printf("GetBit%d = %d\n", nBit2, GetBit(b, nBit2));
                                  printf("TypeToBin b  = %s\n", TypeToBin(b).c_str());
                               
                                  auto res2 = SetBit(b, nBit2, nBitValue1);
                               
                                  printf("res = %d\n", res2);
                                  printf("GetBit%d = %d\n", nBit2, GetBit(res2, nBit2));
                                  printf("TypeToBin b2 = %s\n", TypeToBin(res2).c_str());
                                  printf("GetBit%d = %d\n", nBit1, GetBit(c, nBit1));
                                  printf("TypeToBin c  = %s\n", TypeToBin(c).c_str());
                               
                                  auto res3 = SetBit(c, nBit1, nBitValue2);
                               
                                  printf("res = %d\n", res3);
                                  printf("GetBit%d = %d\n", nBit1, GetBit(res3, nBit1));
                                  printf("TypeToBin c2 = %s\n", TypeToBin(res3).c_str());
                               
                                  system("pause");
                                  return EXIT_SUCCESS;
                              }

                            Преимущество: auto - явно даёт более "легкий" код по сравнению с std::any.
                            Недостаток: если переменную, объявленную как auto, "переиспользовать", то можно ожидать сюрпризы, т.к. она получает тип только в момент объявления, а std::any помнит тип и в процессе переинициализации. Поэтому в своем примере я вынужден был объявлять три разных переменных, и переинициализацию не использовать.

                            Ну и вдогоночку замечания, почему так работу с битами лучше не делать:

                            1) При последовательном вызове SetBit, GetBit, TypeOfBin очень легко можно перепутать имена переменных, и очень часто это не приведёт к ошибке времени компиляции. А при выполнении будут ошибочные данные. Для этого и придумали инкапсуляцию от ООП. И она часто помогает в таких случаях. Оформляй это классом, а функции оформляй методами.
                            2) Установка, чтение и печать битов более-менее подойдет только для POD-типов. А вот представь, что ты в качестве источника используешь указатель, к примеру, на 64кб массив char. Хана твоей логике. Для того, чтобы это не случилось - тебе нужно написать метод побайтного доступа к той переменной, с которой будет работать твой класс. Подчеркиваю, только метод доступа - содержимое самой этой переменной дублировать не следует.
                            3) Индексы установки/чтения битов в методах нужно сразу проверять на выходы за допустимые пределы, иначе будет лабуда!
                              ADD:

                              tumanovalex, если в качестве теста/обучения - нет проблем, экспериментируй. Но если хочется чего-то более предметного, что можно включать в свои проекты - то лучше не изобретать велосипед, а воспользоваться уже готовыми решениями из STL. Вот небольшой примерчик (ИСПРАВЛЕНО):

                              ExpandedWrap disabled
                                #include <iostream>
                                #include <bitset>
                                #include <array>
                                 
                                int main() {
                                    // Создаем массив char размером 16 и инициализируем его
                                    std::array<unsigned char, 16> byteArray = {0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x01};
                                 
                                    // Создаем bitset, используя массив
                                    std::bitset<128> bits;
                                    for (size_t i = 0; i < byteArray.size(); ++i) {
                                        bits <<= 8;
                                        bits |= byteArray[i];
                                    }
                                 
                                    // Устанавливаем 3-й и предпредпоследний биты
                                    bits.set(2);
                                    bits.set(125);
                                 
                                    // Выводим результат
                                    std::cout << bits << std::endl;
                                 
                                    return 0;
                              Сообщение отредактировано: Majestio -
                                Спасибо, Qraiser и Majestio, за объяснения и код!
                                1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0498 ]   [ 19 queries used ]   [ Generated: 16.06.25, 19:06 GMT ]