Re[10]: memset(this, 0, sizeof(T));
От: smeeld  
Дата: 23.11.16 11:23
Оценка:
Здравствуйте, N. I., Вы писали:



NI>А что такое нулевое значение для класса "с виртуальной таблицей и прочими причиндалами"?


То же самое, что и для классов без причиндал, то есть, нулевое значение, заданных определением класса полей. Если конструкция {} приравнивает нулю эти поля классов без причиндал, почему это не распространяется на иные классы? Служебная инфа, вроде указателся на vtable или таблицы таковых (VTT), помещаемая компилятором в кусок памяти, занимаемую объектом, ортогональна его, заданным определением, полей.
Re[3]: memset(this, 0, sizeof(T));
От: Pavel Dvorkin Россия  
Дата: 23.11.16 11:35
Оценка: 2 (1)
Здравствуйте, Dair, Вы писали:

D>При обнулении получишь утечку в общем случае. Вполне c-like структура.


Если она уже инициализирована, то да. Но чаще делают для только что описанных, то есть неинициализированных структур. В MSDN такого кода полно.

https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms646829(v=vs.85).aspx#open_file

(правда, вместо memset здесь ZeroMemory, но это то же самое, только в профиль)
With best regards
Pavel Dvorkin
Re[11]: memset(this, 0, sizeof(T));
От: N. I.  
Дата: 23.11.16 14:11
Оценка: 18 (4)
smeeld:

NI>>А что такое нулевое значение для класса "с виртуальной таблицей и прочими причиндалами"?


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


Значение объекта и последовательность значений байт в его представлении — это не одно и то же.

S>Если конструкция {} приравнивает нулю эти поля классов без причиндал, почему это не распространяется на иные классы?


Во-первых, нуль нулю рознь. Даже у объекта POD типа "нулевое" значение может быть представлено ненулевыми байтами, а последовательность из нулевых байт может образовывать ненулевое значение. Например:

#include <cstring>
#include <iostream>

struct S
{
    int i, j;
    int S::*p;
};

#define PRINT_VALUE(expr) std::cout << #expr ": " << (expr) << std::endl;

int main()
{
    S s;
    s = S();
    std::cout << std::boolalpha;
    std::cout << "value-initialization" << std::endl;
    PRINT_VALUE(s.p == nullptr);
    PRINT_VALUE(s.p == &S::i);
    PRINT_VALUE(s.p == &S::j);

    std::memset(&s, 0, sizeof(s));
    std::cout << "bytewise zeroing" << std::endl;
    PRINT_VALUE(s.p == nullptr);
    PRINT_VALUE(s.p == &S::i);
    PRINT_VALUE(s.p == &S::j);
}

Выхлоп:

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
value-initialization
s.p == nullptr: true
s.p == &S::i: false
s.p == &S::j: false
bytewise zeroing
s.p == nullptr: false
s.p == &S::i: true
s.p == &S::j: false

http://coliru.stacked-crooked.com/a/e5cea54dc6d9dd14

int S::* — это скалярный тип (все скалярные типы являются POD типами), и в данном случае компилятор для представления нулевого указателя на член использует последовательность значений байт, отличную от последовательности нулей.

Во-вторых, зачем подобной языковой конструкции заполнять что-то нулевыми байтами или заниматься обнулением всех полей встроенных типов, если это не обеспечивает как минимум валидность значения инициализируемого объекта? Раз ты сам взялся определять способ конструирования валидного дефолтного значения через добавление соответствующего пользовательского конструктора — тебе и карты в руки.
Отредактировано 23.11.2016 15:02 N. I. . Предыдущая версия .
Re: memset(this, 0, sizeof(T));
От: CEMb  
Дата: 23.11.16 16:45
Оценка:
Здравствуйте, makdak, Вы писали:

M>Всем привет!

M>можно ли так делать в членах класса?
M>так то работает, сегодня..

Лучше взять в привычку делать метод очистки экземпляра класса, где аккуратно расписать обнуление всех его членов.
Потому что сегодня работает, а завтра ты так будешь делать везде

В новых плюсах очистку-инициализацию можно делать прямо в описании самого класса
class C {
    int m_i = 0;
    //...
}
но я всё равно обычно пишу отдельный метод Clear, по аналогии с, например std::string::clear
Re[12]: memset(this, 0, sizeof(T));
От: uzhas Ниоткуда  
Дата: 23.11.16 17:49
Оценка:
Здравствуйте, N. I., Вы писали:

NI>int S::* — это скалярный тип (все скалярные типы являются POD типами), и в данном случае компилятор для представления нулевого указателя на член использует последовательность значений байт, отличную от последовательности нулей.


интересный нюанс

я пытался найти в стандарте во что превращаются скалярные типы после memzero, но не смог найти =\
для каких standard-layout типов ясно определена семантика? то есть если прочитаю из того, что покрыл нулями на байтовом уровне
например, для bool верно, что false прочитаю?

зы. привет, Мастеркент! 4 года не писал тут
Re[12]: memset(this, 0, sizeof(T));
От: smeeld  
Дата: 23.11.16 19:03
Оценка:
Здравствуйте, N. I., Вы писали:

Хотели удивить фактом того, что переменная типа nullptr_t не обязательно равно значению 64-ёх разрядной переменной равной нулю, или что оператор == при сравнении переменных разных типов, тем более переменных с различающимся sizeof даст неожиданные результаты? Вы почти КЭП.
Re[3]: memset(this, 0, sizeof(T));
От: smeeld  
Дата: 23.11.16 19:07
Оценка:
Здравствуйте, Dair, Вы писали:


D>При обнулении получишь утечку в общем случае. Вполне c-like структура.


Это если мы инициализировали void* memory, а после, не освободив, приравняли memory нолю? Не кажется Вам, что эта ситуация ортогональна теме треда, и скорее относится к тому, каким чувакам стоит не лезть в Cя, а лучше писать на яве.
Re[13]: memset(this, 0, sizeof(T));
От: N. I.  
Дата: 23.11.16 20:12
Оценка:
uzhas:

U>я пытался найти в стандарте во что превращаются скалярные типы после memzero, но не смог найти =\


Если существует значение, которое представляется полученной последовательностью нулевых unsigned char-ов, то в объекте будет храниться это самое значение. Иначе объект, наверное, прекращает существование в соответствии с [basic.life].

U>для каких standard-layout типов ясно определена семантика?


Для unsigned char определённо

U>например, для bool верно, что false прочитаю?


Если std::memcpy-подобное копирование объекта, хранящего false, в массив unsigned char-ов образует нолики (вряд ли есть реализации, где это не так), то да. Если нолики получаются при копировании true, то будет true. Иначе формально там может получиться что угодно, насколько я вижу.

U>4 года не писал тут


Забанили за какой-то язвительный комментарий, решил отдохнуть
Отредактировано 23.11.2016 20:21 N. I. . Предыдущая версия .
Re[13]: memset(this, 0, sizeof(T));
От: N. I.  
Дата: 23.11.16 20:12
Оценка: +3 :)
smeeld:

S>Хотели удивить фактом того, что переменная типа nullptr_t не обязательно равно значению 64-ёх разрядной переменной равной нулю, или что оператор == при сравнении переменных разных типов, тем более переменных с различающимся sizeof даст неожиданные результаты?


С такими кадрами, чувствую, следующие 4 года начнутся очень скоро
Re[3]: memset(this, 0, sizeof(T));
От: _NN_  
Дата: 23.11.16 20:15
Оценка:
Здравствуйте, makdak, Вы писали:

M>>>можно ли так делать в членах класса?


J>>А зачем?


M>Чтобы обнулить всё в нём. Смотрю исходники, везде юзают:

M>
M> memset(pointer_to_obj_T, 0, sizeof(T));
M>

M>вот и думаю почему ни кто не делает это в члене класса, тем более если такая операция многократно используется, над одним и тем же объектом..
M>думаю, дай поинтересуюсь, может что новое узнаю

auto_value
Автор: Кодт
Дата: 16.01.03
?
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: memset(this, 0, sizeof(T));
От: _smit Россия  
Дата: 23.11.16 20:16
Оценка:
кроме того, везде в коде, где встречается memset надо делать проверку на ловушки, которые он в себе таит, см.: https://habrahabr.ru/post/272269/
Сопровождать (модифицировать) такой код будет тяжело. Про open-closed принцип (SOLID) можно забыть.
Re[14]: memset(this, 0, sizeof(T));
От: smeeld  
Дата: 23.11.16 21:12
Оценка: :)
Здравствуйте, N. I., Вы писали:

NI>С такими кадрами, чувствую, следующие 4 года начнутся очень скоро


Каков Ваш там выше пример, таков и ответ. Конструкции T::x и T::*x в С++ имеют особое значение и нечего за них цепляться. Значение &T::x вообще равно смещению поля в пределах объекта в байтах, а не значению виртуального адреса поля объекта.

Если же прогнать такой код
include <cstring>
#include <iostream>
struct S
{
    int i, j;
    int *p;
};
#define PRINT_VALUE(expr) std::cout << #expr ": " << (expr) << std::endl;

int main()
{


   S  s = S();
    std::cout << std::boolalpha;
    PRINT_VALUE(s.p == 0);
    PRINT_VALUE(s.p == &s.i);
    PRINT_VALUE(s.p == &s.j);

    std::memset(&s, 0, sizeof(s));
    std::cout << "bytewise zeroing" << std::endl;
    PRINT_VALUE(s.p == 0);
    PRINT_VALUE(s.p == &s.i);
    PRINT_VALUE(s.p == &s.j);
}


То выхлоп будет таким

value-initialization
s.p == 0: true
s.p == &s.i: false
s.p == &s.j: false
bytewise zeroing
s.p == 0: true
s.p == &s.i: false
s.p == &s.j: false


Что доказывает, что memset даёт те же зачения полям, что и другие способы обнуления.
Re[15]: memset(this, 0, sizeof(T));
От: _smit Россия  
Дата: 24.11.16 06:09
Оценка:
Здравствуйте, smeeld, Вы писали:

S>...Конструкции T::x и T::*x в С++ имеют особое значение и нечего за них цепляться. Значение &T::x вообще равно смещению поля в пределах объекта в байтах, а не значению виртуального адреса поля объекта.

...
S>Что доказывает, что memset даёт те же зачения полям, что и другие способы обнуления.

есть много примеров сайт эффектов memset. Да, он может "давать те же значения полям, что и другие способы обнуления". Но я пока не увидел его достоинств по сравнению с "классическим" (стандартным) методом инициализации структур.
Re[15]: memset(this, 0, sizeof(T));
От: night beast СССР  
Дата: 24.11.16 07:20
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Если же прогнать такой код

S>
S>struct S
S>{
S>    int i, j;
S>    int *p;
S>};
S>int main()
S>{
S>    PRINT_VALUE(s.p == &s.i);
S>    PRINT_VALUE(s.p == &s.j);
S>}
S>


чисто для справки, почему s.p сравнивается именно с &s.i и &s.j?
здесь скрыт какой-то сакральный смысл?
Re[16]: memset(this, 0, sizeof(T));
От: smeeld  
Дата: 24.11.16 07:35
Оценка:
Здравствуйте, night beast, Вы писали:

NB>чисто для справки, почему s.p сравнивается именно с &s.i и &s.j?

NB>здесь скрыт какой-то сакральный смысл?

Чисто для прикола и демонстрации отличия &s.i от &S::i
Re[17]: memset(this, 0, sizeof(T));
От: night beast СССР  
Дата: 24.11.16 08:17
Оценка:
Здравствуйте, smeeld, Вы писали:

NB>>чисто для справки, почему s.p сравнивается именно с &s.i и &s.j?

NB>>здесь скрыт какой-то сакральный смысл?

S>Чисто для прикола и демонстрации отличия &s.i от &S::i


ну для демонстрации логично было бы и &S::i где-нибудь использовать, не?
Re: memset(this, 0, sizeof(T));
От: smeeld  
Дата: 24.11.16 12:18
Оценка:
Здравствуйте, makdak, Вы писали:

Тут в одном треде LapteVV приводил ссылки по полезным C++-ым ресурсам, пробежался по ним, всковырнул сочиненьице одного из гуру, а там нашёл такой код.
Предлагаю уважаемой публике оценить этот код, там как раз этот самый memset преступно вовсю юзается, там ещё и memcpy, это вообще преступление.
#define  _CRT_SECURE_NO_WARNINGS  // Avoid warning for vsnprintf function in MS compiler
#include <memory.h>               // For memcpy and memset
#include <string.h>               // For strlen, strcmp, strchr
#include <stdlib.h>               // For exit in Error function
#include <stdarg.h>               // For va_list, va_start
#include <stdio.h>                // Needed for example only
//#include <varargs.h>            // Include varargs.h for va_list, va_start only if required by your system

#ifdef   USE_ASMLIB
#include "asmlib.h"               // Header file for asmlib library
#define  memcpy_  A_memcpy        // Use asmlib versions of string functions
#define  memset_  A_memset
#define  strlen_  A_strlen
#define  strcmp_  A_strcmp
#define  strstr_  A_strstr
#define  MEM_PADDING 15           // Padding of memory buffer
#else
#define  memcpy_  memcpy          // Use standard library versions of string functions
#define  memset_  memset
#define  strlen_  strlen
#define  strcmp_  strcmp
#define  strstr_  strstr
#define  MEM_PADDING 0
#endif

// Define pointer to zero-terminated string
typedef char const * PChar;

// Class definition for StringPoolS
class StringPoolS {
public:
   // Helper class StringElement. This class defines all the operators and 
   // functions we can apply to StringPoolSObject[i]:
   class StringElement {
   public:
      // Constructor:
      StringElement(StringPoolS & a, int i) : arr(a) {index = i;}; 
      // Get string length:
      int Len() {return strlen_(arr.Get(index));};
      // Format string using printf style formating:
      StringElement const & Printf(const char * format, ...);
      // Write string to file or stdout:
      int Write(FILE * f);
      // Search in string:
      int SearchForSubstring(PChar s);
      // Assign substring:
      StringElement const & SetToSubstring(PChar s, int start, int len);
      // Operator '=' assigns string:
      PChar operator = (PChar s) {arr.Set(index, s); return *this;};
      PChar operator = (StringElement const & s) {return *this = PChar(s);};
      // Operator '+=' concatenates strings:
      void operator += (const char * s) {arr.Concatenate(index, s);};
      // Automatic conversion to const char *
      operator PChar() const {return arr.Get(index);};
      // Operator '[]' gives access to a single character in a string:
      inline char & operator[] (int i);
   protected:
      StringPoolS & arr;               // Reference to the StringPoolS that created this object
      int index;                       // Index to string in StringPoolS
   };
   friend class StringElement;         // Friend class defining all operations we can do on StringPoolObject[i]

   // Members of class StringPoolS
   StringPoolS();                      // Constructor
   ~StringPoolS();                     // Destructor
   void Clear();                       // Erase all strings
   int GetNum() const {return Num;}    // Get number of strings
   StringElement operator[] (int i) {  // Operator '[]' gives access to a string in StringPoolS 
      return StringElement(*this, i);}
   void Set(int i, PChar s);           // Insert zero-terminated string. Used by StringElement 
   void Set(int i, PChar s, int Len);  // Insert non zero-terminated string. Used by StringElement 
   void Concatenate(int i, PChar s);   // Concatenate strings. Used by StringElement 
   void ReserveBuf(int newsize);       // Allocate memory for string space
   void ReserveNum(int newsize);       // Allocate memory for string indices

   // Define desired allocation sizes. You may change these values:
   enum DefineSizes {
      AllocateSpace1 = 4096,           // Minimum number of characters to allocate in string buffer 
      AllocateSpace2 = 1024,           // Minimum number of indices to allocate in offsets buffer
      FormatLength   = 1023,           // Maximum length of strings written with Printf
      MemPadding     = MEM_PADDING     // Memory buffer padding
   };
protected:
   char * Get(int i) const;            // Read string. Used only from StringElement 
   void Error(int message,int i) const;// Produce fatal error message.
private:
   char * Buffer;                      // Memory block containing strings
   char * OldBuffer;                   // Save old Buffer during copying of string
   unsigned int * Offsets;             // Memory block containing offsets to strings
   int BufferSize;                     // Size of buffer block
   int OffsetsSize;                    // Size of Offsets block
   int DataSize;                       // Used part of Buffer, including garbage
   int GarbageSize;                    // Total size of garbage in Buffer
   int Top;                            // Highest used Offset
   int Num;                            // Number of strings = highest used index in Offsets + 1
   char * Allocate(int i, int len);    // Make space for a new string
   StringPoolS(StringPoolS &){};       // Private copy constructor to prevent copying
   void operator = (StringPoolS &){};  // Private operator = to prevent copying
};

inline bool operator == (StringPoolS::StringElement const & a, StringPoolS::StringElement const & b) {
   return strcmp_(a, b) == 0;
}
inline bool operator != (StringPoolS::StringElement const & a, StringPoolS::StringElement const & b) {
   return strcmp_(a, b) != 0;
}
inline bool operator  < (StringPoolS::StringElement const & a, StringPoolS::StringElement const & b) {
   return strcmp_(a, b) < 0;
}
inline bool operator  > (StringPoolS::StringElement const & a, StringPoolS::StringElement const & b) {
   return strcmp_(a, b) > 0;
}


/******************************************************************************
 2. Function definition part. Put this in a .cpp file:
******************************************************************************/

// Default constructor
StringPoolS::StringPoolS() {
   // Set everything to zero
   memset_(this, 0, sizeof(*this));
}


// Destructor
StringPoolS::~StringPoolS() {
   // Free allocated memory
   if (Buffer)    delete[] Buffer;
   if (OldBuffer) delete[] OldBuffer;
   if (Offsets)   delete[] Offsets;
   // Set everything to zero
   memset_(this, 0, sizeof(*this));
}


// Erase all strings
void StringPoolS::Clear() {
   // Set all offsets to zero
   if (Offsets) memset_(Offsets, 0, Num * sizeof(*Offsets));
   // Indicate that Buffer is empty, but do not deallocate
   DataSize = GarbageSize = 0;
}


// Insert string. Used by StringElement 
void StringPoolS::Set(int i, PChar s) {
   if (i < 0) Error(1, i);
   if (i >= Num) {
      // i is higher than any previous index
      Num = i + 1;
      if (i >= OffsetsSize) {
         // Make Offsets buffer bigger
         ReserveNum(i + 1);
      }
   }
   // Length of new string
   int Len = 0;
   if (s) Len = strlen_(s);
   if (Len == 0) {
      // Erase string
      if (Offsets[i]) {
         GarbageSize += strlen_(Buffer + Offsets[i]) + 1;
         Offsets[i] = 0;
      }
      return;
   }
   // Make space for string
   char * p = Allocate(i, Len);
   // Insert string
   memcpy_(p, s, Len+1);
   // Release OldBuffer if any
   if (OldBuffer) {
      delete[] OldBuffer;  OldBuffer = 0;
   }
}


// Insert non zero-terminated string. Used by StringElement
void StringPoolS::Set(int i, PChar s, int Len) {
   if (i < 0) Error(1, i);
   if (i >= Num) {
      // i is higher than any previous index
      Num = i + 1;
      if (i >= OffsetsSize) {
         // Make Offsets buffer bigger
         ReserveNum(i + 1);
      }
   }
   // Length of new string
   if (Len <= 0) {
      // Erase string
      if (Offsets[i]) {
         GarbageSize += strlen_(Buffer + Offsets[i]) + 1;
         Offsets[i] = 0;
      }
      return;
   }
   // Make space for string
   char * p = Allocate(i, Len);
   // Insert string
   memcpy_(p, s, Len);
   // Zero-terminate
   p[Len] = 0;
   // Release OldBuffer if any
   if (OldBuffer) {
      delete[] OldBuffer;  OldBuffer = 0;
   }
}
Re[4]: memset(this, 0, sizeof(T));
От: SaZ  
Дата: 24.11.16 14:02
Оценка:
Здравствуйте, Ops, Вы писали:


Ops>Если говорить про ключевые слова, то да. А если логически разделять, не привязываясь к языку?


Ну это такой вопрос, который задаётся в контексте языка. В C# понятно — там это в первую очередь разделение по типу памяти. Но в контексте задачи про memset это явно про C++.
Re[2]: memset(this, 0, sizeof(T));
От: IID Россия  
Дата: 24.11.16 14:19
Оценка:
Здравствуйте, Dair, Вы писали:

D>1. Класс может завтра стать виртуальным и ты обнулишь таблицу виртуальных функций.


Не таблицу виртуальных функций, а лишь указатель на неё.
kalsarikännit
Re[5]: memset(this, 0, sizeof(T));
От: Ops Россия  
Дата: 24.11.16 14:19
Оценка: +1
Здравствуйте, SaZ, Вы писали:

SaZ>Ну это такой вопрос, который задаётся в контексте языка. В C# понятно — там это в первую очередь разделение по типу памяти. Но в контексте задачи про memset это явно про C++.


Подозреваю, в контексте ответа, имелись в виду C-like структуры. Чуятельный орган подсказывает.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.