Динамический массив
Есть класс, который имеет в своем составе члены стандартных типов int и char, а также члены-структуры мною созданные.
public: char service_type[6];
int1 source_addr_npi;
char source_addr[65];
int1 dest_addr_ton;
int1 dest_addr_npi;
char destination_addr[65];
int1 esm_class;
TLV source_port;
TLV source_addr_subunit;
};
Int1 - это ппросто переименованный unsigned char - мне так удобнее.
Здесь у меня с символьными строками все в порядке - имеют максимальный фмксированный размер.
Структура TLV:
short Tag;
short Length;
// Byte Value[2];
};
Так вот - суть проблемы: Мне необходимо измерять переменные типа DATA_SM в байтах, например так:
int b=sizeof(A);
НО, я не знаю как сделать так чтобы массив байт Value в TLV был нефиксированного размера и при этом измерение было правильным. ПО идее надо использовать указатель, но тогда sizeof вернет неверный размер. (Размер указателя 4 байта.)
Если использовать strcpy(), значение копируется, а вот размер массива остается прежним.
char val[]="valueeeee";
strspy(var1.Value, val);
Вероятно здесь я не понимаю до конца как работает память с переменными и что-то там связанное со стэком.
Подскажите пожалуйста кто может как поступить. ПРосто иначе придется сделать массив фиксированного размера)))
int b=sizeof(A);
КОнечно как вариант - сделать функцию, которая будем мерить размер переменной типа DATA_SM, складывая размеры полей и поля,хранщие размеры структурных полей. Но класс DATA_SM один из 20 с копейками, которые отличаются друг от друга названием полей и их количеством. Поэтому как-то слишком мануально получается.
short Tag;
short Length;
Byte* Value;
};
Хм... Я так полагаю, что определить его можно по массиву, на который ссылается этот указатель.
char val[]="valueeeee";
strspy(var1.Value, val);
var1.Length=sizeof(val);
If an unsized array is the last element of a structure, the sizeof operator returns the size of the structure without the array.
Скорее всего во время выполнения. Хотя надо уточнить...
И вы в конце концов определитесь что вам надо: размер структуры в терминах языка или же размер какого то пакета данных, представляемых структурой, допустим для сериализации? Храните размер буфера в структурах, в чем сложность то? Не хотите - храните вобще контейнеры. Например std:vector. Он вам предоставит и интерфейс массива и сам будет следить за размером и хранить его.
Найдите такой пример и приведите его, многие тайны откроются вам.
short Tag;
short Length; \\хранит размер Value в байтах
Byte Value[2]; \\данные неизвестного типа и размера помещаются сюда ввиде байт-массива. Зависит не от меня, а отпараметров сервера, которому все передается
};
public: short Length; //здесь помещается размер переменной типа DATA_SM (в байтах)
char service_type[6];
Byte source_addr_npi;
char source_addr[65];
Byte dest_addr_ton;
Byte dest_addr_npi;
char destination_addr[65];
Byte esm_class;
TLV source_port;
TLV source_addr_subunit;
Byte[] ToByteArray(); //тут бред, пока не занимался реализацией этой функции
};
Я хочу отправлять ввиде массива байтов перменную типа DATA_SM
например так:
DATA_SM A; //без new наверное будет если внутри нет указателей
.
.
.
A.Length=sizeof(A.ToByteArray());
sc->Send(A.ToByteArray());
....
Т.е. получаем что размер и содержимое связаны и соответсвуют друг другу.
ВОт приблизительно так...... Надеюсь более менее понятно.
Ну или пример с ошибкой или вы не правильно поняли зачем там применяется sizeof. Встретите такой код - несите сюда, тут и разберем.
Я посмотрел и так и не понял зачем вам тут sizeof. Тут обычная сериализации объекта. Для всех динамически выделяемых массивов в любом случае надо хранить их длину. Ну или хранить векторы. Из вектора всегда можно получить указатель на данные и они там гарантировано лежат в одном последовательном буфере. Что то у меня такое ощущение что мы на разных языках говорим и немного друг друга не понимаем. =)
Небольшое отвлечение. Вот такой пример:
char b[]="bbbbbbbb";
strcpy(a,b);
int c =sizeof(a);
String^ d=new String(a);
TextRichBox1->Text=c.ToString +"----"+ d;
Получаем "6----bbbbbbbb" Т.е. значение скопировалось, а размер остался прежним.
Не могу понять чем это объясняется?
И насчет sizeof() почитал. Типы оказывается мерить не так просто...
http://www.cyberguru.ru/programming/cpp/cpp-programming-guide-page24.html
Там похоже есть ошибка, но суть ясна. Пробовал мерить - длина не соответсвует посчитанному руками.
Я так понимаю, все на самом деле сложнее, чем мне казалось с отправкой данных. Может кто подскажет в каком направлении плясать? Какие методы использовать?
Небольшое отвлечение. Вот такой пример:
char b[]="bbbbbbbb";
strcpy(a,b);
int c =sizeof(a);
String^ d=new String(a);
TextRichBox1->Text=c.ToString +"----"+ d;
Получаем "6----bbbbbbbb" Т.е. значение скопировалось, а размер остался прежним.
Не могу понять чем это объясняется?
Этот пример некорректный и не рабочий в общем случае. Потому как у массвов a и b неправильно указаны типы. Правильно было бы инициализировать так:
const char b[] = "bbbbbbb";
Но из-за того что в c++ порой допускается такое неявное преобразование указателей при инициализации с константных в не константные и возникает проблема. В частности, поскольку a изначально константный - туда нельзя ничего писать. И в некоторых случая, а на ряде компиляторов - всегда, на этой записи у вас программа просто упадет. Если бы вы сразу описали тип массива как const char - тогда компилятор просто не скомпилировался бы на вызове strcpy, и подсказал бы об ошибке. А так, вы мало того что записали в память, в которую нельзя писать, так еще и вылезли за её пределы и записали остаток второй строки в чужую память, где могли находиться какие то объекты программы да и просто служебная информация. Таким образом программа принимает не рабочее состояние и в любой момент может начать вести себя совершенно случайным образом. ) При этом размер массива a конечно же никуда не менялся, просто вылезли за его пределы.
насчет sizeof() почитал. Типы оказывается мерить не так просто...
http://www.cyberguru.ru/programming/cpp/cpp-programming-guide-page24.html
Там похоже есть ошибка, но суть ясна. Пробовал мерить - длина не соответсвует посчитанному руками.
Я так понимаю, все на самом деле сложнее, чем мне казалось с отправкой данных. Может кто подскажет в каком направлении плясать? Какие методы использовать?
Да не надо никуда плясать - все ведь уже сказано. Не надо ничего мерить - для всех массивов просто всегда надо хранить их размер. Размер атомарных типов можно уже вычислять через sizeof. Хотя тоже на самом деле не желательно, если вы пишите какую то сетевую софтину (судя по коду). Потому что тогда данные ушедшие в сеть будут зависеть от вашего компилятора, операционной системы, разрядности процессора и т.п.
А надо сначала определить размерности всех ваших числовых полей в пакетах, в tlv вашего протокола. Определить порядок байт в них - это тоже немаловажно, порядок на вашей машине и удаленной может различаться. Кодировки строк и прочее - все это определить в спецификации протокола. И только потом, уже записывая очереной объект в сеть, в файл, в просто какой то абстрактный поток данных - вы приводите свои поля к виду определенному в протоколе. Массивы соответственно пишите как есть, если внутри них данные не подлежат конвертации. Для всех массивов у вас должны быть размеры и все.
const char b[] = "bbbbbbb";
В таком случае константно содержимое массива? А если сначала задается, например, значение по умолчание. А в дальнейшем будет необходимость его изменить?
Насчет протокола - да. поля идут друг за другом по спецификации - в том самом порядке. Допустим все размеры всех массивов char у меня заданы. Суммарный Размер всех полей атомарных типов посчитаю и запишу куда надо.
Затем, как мне лучше отправить сокетом на сервер? Преобразовть в массив байт? Какими методами это делать лучше? Про эти плясать я и спрашивал.
char b[]="bbbbbbbb";
strcpy(a,b);
int c =sizeof(a);
String^ d=new String(a);
TextRichBox1->Text=c.ToString +"----"+ d;
Э-эх... Какая каша у вас. Тут и строки в стиле Си, и попытки работать с ними, как со строками C++, и в довершение C++.NET.
Про строки в стиле Си aks уже расписал, в чём ваша ошибка.
Последние две строки вашего кода: это из C++.NET. Значок ^ - это управляемый указатель. Метод ToString - тоже из управляемого кода. Ну и ричтекстбокc без всякого сомнения из WinForms.
В первую очередь вам нужно понять, что C, C++, и C++.NET - это по сути три разных языка. Да, в Visual Studio их можно смешивать. Но лучше этого не делать! Во всяком случае, нужно понимать, когда и зачем это делается. Именно по этой причине я считаю VS крайне неудачным выбором для начинающих осваивать C/C++. Новички постоянно путаются и делают кровавое месиво из разных технологий (как и я в своё время...)
Короче: чтобы освоить чистый Си, нужно взять K&R и включить компиляцию проекта именно как чистый Си. Тогда среда разработки не позволит использовать С++.
Чтобы освоить нативный С++, нужно ни в коем случае не использовать управляемые возможности среды .NET. В частности, WinForms. А если хочется именно формочки легко клепать, то лучше перейти на C#.
Суммарный Размер всех полей атомарных типов посчитаю и запишу куда надо.
Повторюсь - надо не просто посчитать их размер, чтоб размер отправляемого вами в сеть зависел от размера полей, а наоборот. Надо привести ваши поля к виду описанному в протоколе. Например в протоколе указано что в вашем tlv поля тип и длина должны быть 4-х байтовыми целыми в сетевом порядке байт. А на вашем конкретном компьютере и ОС оказывается эти поля в структурах C++ 8-ми байтовые. Значит надо сделать так, чтоб или в структурах всегда были 4-х байтовые типы или переводить их в 4 байта перед отправкой. А затем перевести их в сетевой порядок байт, если они в обратном порядке.
Затем, как мне лучше отправить сокетом на сервер? Преобразовть в массив байт? Какими методами это делать лучше? Про эти плясать я и спрашивал.
Ну варианта два.
Либо ваша сериализующая функция принимает какой то сокет, поток или еще чего, куда можно писать и пишет туда по очереди все свои поля и массивы. Возможно это будет на самом деле какая то буферизующая обертка, накапливающая данные например в кольцевом буфере и отправляющая на совсем или по заполнению буфера или по специальной команде. Как вариант прямо внутри сериализующей функции - мелкие поля посылать не по одиночке, а сливать в некий буфер, который послать разом, чтобы работало быстрее.
Второй вариант - создать буфер достаточный, чтоб вместить туда все - записать в него и вернуть из сериализующей функции. Но надо пользоваться с осторожностью, ибо такой способ может оказаться довольно дорогим в плане производительности и использования памяти.
Про строки в стиле Си aks уже расписал, в чём ваша ошибка.
Последние две строки вашего кода: это из C++.NET. Значок ^ - это управляемый указатель. Метод ToString - тоже из управляемого кода. Ну и ричтекстбокc без всякого сомнения из WinForms.
В первую очередь вам нужно понять, что C, C++, и C++.NET - это по сути три разных языка. Да, в Visual Studio их можно смешивать. Но лучше этого не делать! Во всяком случае, нужно понимать, когда и зачем это делается. Именно по этой причине я считаю VS крайне неудачным выбором для начинающих осваивать C/C++. Новички постоянно путаются и делают кровавое месиво из разных технологий (как и я в своё время...)
Короче: чтобы освоить чистый Си, нужно взять K&R и включить компиляцию проекта именно как чистый Си. Тогда среда разработки не позволит использовать С++.
Чтобы освоить нативный С++, нужно ни в коем случае не использовать управляемые возможности среды .NET. В частности, WinForms. А если хочется именно формочки легко клепать, то лучше перейти на C#.
Я понимаю, что каша. Просто я привел, возможно, не удачный пример. Пока я ориентирован на создание Windows Forms Application. А С-шные типы использую, чтобы осуществлять манипуляции с обработкой и отправкой данных. Спасибо за замечание. Буду стараться делать делать все в одном стиле. Но пока я вот так делаю. И вообще потом реализовать прогу хочу на WinAPi, а сейчас как-то реализовать функционал таким образом. Потому как с API я вообще не практиковался.
.
Ну пока я принимаю, что компьютер только мой и ОС только моя - 32 бита. Просто хочу получить простой работоспособный функционал. Без всяких "а если...." Это все потом. Пока алгоритм.
Как вариант прямо внутри сериализующей функции - мелкие поля посылать не по одиночке, а сливать в некий буфер, который послать разом, чтобы работало быстрее.
Вот вот вот. Так я себе это и представляю. Подскажите, пожалуйста, какие инструменты для этого существуют?
В том то и дело, что нету никакого алгоритма. Нужно просто отсылать все поля в сеть. По порядку. Алгоритмом это язык назвать не поворачивается. Разве что алгоритм из одного действия. =)
Чтоб потом не огребать геммороя - лучше сразу их конвертировать в протокольный вид. Это не сложно и это делается сразу одновременно с отправкой.
Чтоб потом не огребать геммороя - лучше сразу их конвертировать в протокольный вид. Это не сложно и это делается сразу одновременно с отправкой.
Я представляю реализацию так:
1. Определить нужные типы данных с использованием атомарных типов( ПО спецификации). Т.к. они представляют собой пакеты обмена.
2. Функции работы с ним и(Инициализации, конвертирования и пр.). (Тут наверно и стоит выбрать язык). Допустим .Net
2. Осуществление обмена с сервером (подключение, очередь пакетов и пр.)
3. Создать интерфейс программы.
Можно конечно идти в обратном направлении....
Да никаких инструментов, кроме самого языка - просто пишете в массив и все.
Тоесть допустим у вас есть спецификация пакета протокола:
field1 - целое 4 байта.
field2 - целое 8 байта.
...
fieldN - целое 4 байта
dataLengh - длина массива, целое 8 байт. итого все эти поля M байт.
data - массив байт длиной dataLengh
тогда код что то типа такого:
char header[HEADER_SIZE]; /// или char * header = new char[HEADER_SIZE];
char * ptr = header;
netField1 = преобразовать_в_сетевой_вид(field1);
memcpy(ptr, &netField1, размер_netField1);
ptr += размер_netField1;
...
netDataSize = преобразовать_в_сетевой_вид(dataSize);
memcpy(ptr, &netFieldN, размер_netDataSize);
ptr += размер_netDataSize;
stream.write(header, HEADER_SIZE);
stream.write(data, dataSize);
естественно все можно написать более компактно - я развернуто на частично псевдокоде расписал чтоб было понятней. =)
Вот такие дела приблизительно получились:
public struct TLV {
short Length;
Byte* Value;
TLV( short tag, size_t LengthOfTypeValue, void* value) //конструктор
{
this->Tag=tag;
this->Length= (short)LengthOfTypeValue;
this->Value=(Byte*)value;
}
};
Далее:
public: int command_length;
int command_id;
int command_status;
int sequence_number;
Byte* ptr_array;
char system_id[16];
TLV sc_interface_version;
void ToByteArray ()
this->command_length=sizeof(command_length)+sizeof(command_id)+sizeof(command_status)+
sizeof(sequence_number)+sizeof(system_id)+ sizeof(sc_interface_version.Tag)+
sizeof(sc_interface_version.Length)+ sc_interface_version.Length; //подсчет размера пакета
static const size_t BUFER_SIZE=command_length;
static Byte* bufer = new Byte[BUFER_SIZE];
Byte* ptr = bufer;
memcpy(ptr, &command_length, sizeof(command_length)); //складывание таким образом всех полей
ptr += sizeof(command_length);
memcpy(ptr, &command_id, sizeof(command_id));
ptr += sizeof(command_id);
memcpy(ptr, &command_status, sizeof(command_status));
ptr += sizeof(command_status);
memcpy(ptr, &sequence_number, sizeof(sequence_number));
ptr += sizeof(sequence_number);
memcpy(ptr, system_id, sizeof(system_id));
ptr += sizeof(system_id);
//sc_interface_version
memcpy(ptr, &sc_interface_version.Tag, sizeof(sc_interface_version.Tag));
ptr += sizeof(sc_interface_version.Tag);
memcpy(ptr, &sc_interface_version.Length, sizeof(sc_interface_version.Length));
ptr += sizeof(sc_interface_version.Length);
memcpy(ptr, sc_interface_version.Value, sc_interface_version.Length);
ptr += sizeof(sc_interface_version.Length);
this->ptr_array=bufer;
}
};
Ну и далее с указателем на массив байт проводим последующие преобразовния и отправку через сокет. Надеюсь что-то получится. Aks огромное спасибо.