Может ли прямой доступ к переменной (а не с помощью указателя и разыменователя) давать разные значения?

#c# #c #pointers

#c# #c #указатели

Вопрос:

Я знаю, что это странный вопрос, и я рискую показать свою неопытность, но я тут рву на себе волосы.

У меня есть пример C , который работает так, как ожидалось. Вот фрагмент надоедливого бита:

 BIRDFRAME frame;
birdGetMostRecentFrame(GROUP_ID,amp;frame);
BIRDREADING *bird_data;
bird_time=frame.dwTime;
for(FBBloop=FBBstart; FBBloop<FBBend; FBBloop   )
{                   
    bird_data = amp;frame.reading[FBBloop];
    // Do stuff
}
  

Обратите внимание, что bird_data это указатель, которому присваивается адрес frame.reading[FBBloop] . Мое приложение написано на C # и поэтому не имеет ничего из этих ошибок с указателями. Соответствующий бит выглядит следующим образом:

 FlockOfBirds.BIRDFRAME frame = new FlockOfBirds.BIRDFRAME();
FlockOfBirds.birdGetMostRecentFrame(1, ref frame);
Console.WriteLine("T:{0}",frame.dwTime.ToString());
FlockOfBirds.BIRDREADING reading;
for (int k = 1; k <= sysconf.byNumDevices; k  )
{
    reading = frame.readings[k];
    Console.WriteLine("  [{0}][{1}][{2}]", reading.position.nX.ToString(), reading.position.nY.ToString(), reading.position.nZ.ToString());
}
  

Проблема в том, что в примере C # только reading[1] значимые данные. В примере C оба bird_data[1] и bird_data[2] имеют хорошие данные. Как ни странно, reading[2-n] не все значения обнулены, но кажутся случайными и постоянными. Вот как выглядит вывод из приложения C #:

 T:17291325
  [30708][-2584][-5220]
  [-19660][1048][-31310]

T:17291334
  [30464][-2588][-5600]
  [-19660][1048][-31310]

T:17291346
  [30228][-2600][-5952]
  [-19660][1048][-31310]

T:17291354
  [30120][-2520][-6264]
  [-19660][1048][-31310]

T:17291363
  [30072][-2388][-6600]
  [-19660][1048][-31310]
  

Обратите внимание, что верхний триплет каждой записи немного отличается от верхнего триплета записей, предшествующих ей и следующих за ней. В приложении C нижняя строка ведет себя аналогично, но здесь она остается неизменной повсюду.

Может ли это быть связано с отсутствием указания и разыменования? Или я вызываю совершенно неправильное дерево? Я сделал что-то еще, что явно глупо? Самое главное: как мне заставить это работать?


Обновление: Структуры и внешние

C

 // Bird reading structure
typedef struct tagBIRDREADING
{
    BIRDPOSITION    position;   // position of receiver
    BIRDANGLES      angles;     // orientation of receiver, as angles
    BIRDMATRIX      matrix;     // orientation of receiver, as matrix
    BIRDQUATERNION  quaternion; // orientation of receiver, as quaternion
    WORD            wButtons;   // button states
}
BIRDREADING;

// Bird frame structure
//
// NOTE: In stand-alone mode, the bird reading is stored in reading[0], and
//  all other array elements are unused.  In master/slave mode, the "reading"
//  array is indexed by bird number - for example, bird #1 is at reading[1],
//  bird #2 is at reading[2], etc., and reading[0] is unused.
typedef struct tagBIRDFRAME
{
    DWORD           dwTime;     // time at which readings were taken, in msecs
    BIRDREADING     reading[BIRD_MAX_DEVICE_NUM   1];  // reading from each bird
}
BIRDFRAME;

BOOL DLLEXPORT birdGetMostRecentFrame(int nGroupID, BIRDFRAME *pframe);
  

C#:

 [StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct BIRDREADING
{
        public BIRDPOSITION position;
        public BIRDANGLES angles;
        public BIRDMATRIX matrix;
        public BIRDQUATERNION quaternion;
        public ushort wButtons;
}

[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct BIRDFRAME
{
        public uint dwTime;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 127)]
        public BIRDREADING[] readings;
}

[DllImport(@"Bird.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool birdGetMostRecentFrame(int nGroupID, ref BIRDFRAME frame);
  

** Обновление 2: больше структур: **

C

 #pragma pack(1) // pack the following structures on one-byte boundaries

// Bird position structure
typedef struct tagBIRDPOSITION
{
    short   nX;         // x-coordinate
    short   nY;         // y-coordinate
    short   nZ;         // z-coordinate
}
BIRDPOSITION;

// Bird angles structure
typedef struct tagBIRDANGLES
{
    short   nAzimuth;   // azimuth angle
    short   nElevation; // elevation angle
    short   nRoll;      // roll angle
}
BIRDANGLES;

// Bird matrix structure
typedef struct tagBIRDMATRIX
{
    short   n[3][3];    // array of matrix elements
}
BIRDMATRIX;

// Bird quaternion structure
typedef struct tagBIRDQUATERNION
{
    short   nQ0;        // q0
    short   nQ1;        // q1
    short   nQ2;        // q2
    short   nQ3;        // q3
}
BIRDQUATERNION;

#pragma pack()  // resume normal packing of structures
  

C#

 [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BIRDPOSITION
{
    public short nX;
    public short nY;
    public short nZ;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BIRDANGLES
{
    public short nAzimuth;  // azimuth angle
    public short nElevation;    // elevation angle
    public short nRoll;     // roll angle
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct BIRDMATRIX
{
    public fixed short n[9];
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BIRDQUATERNION
{
    public short nQ0;       // q0
    public short nQ1;       // q1
    public short nQ2;       // q2
    public short nQ3;       // q3
}
  

Комментарии:

1. Я не могу сказать, что понимаю, что могут делать все ваши объекты или почему это не приведет к сбою, но большинство массивов основаны на нуле, это определенно должно начинаться с int k = 1; ?

2. Да, это странно, но определенно, начиная с 1. 0 зарезервировано для особого случая, когда запрашивается только одно значение.

3. «0 зарезервировано для особого случая, когда имеется только одно значение» — Значит, если у вас есть 2 элемента, в массиве будет 3 элемента, причем первый элемент с индексом 0 не используется? Если да, то, похоже, над этим следует подумать. Тем более, что индекс 0 уже довольно хорошо обрабатывает «особый случай» массива с одним элементом.

4. Эй, я не писал библиотеку! 😉 Если вы хотите смелости, массив всегда фиксируется на 127. Если подключен только один инструмент и он находится в «автономном» режиме, индексы 1-126 игнорируются. Если, как в моем случае, подключены 2, 0 и 3-126 игнорируются.

5. Мы вынуждены строить догадки без определений структур и некоторого знания того, что за ними стоит birdGetMostRecentFrame . Есть ли где-нибудь еще P / Invoke, смешанный где-нибудь?

Ответ №1:

Я попробовал вашу структуру в двух проектах (C / C #), и, кажется, она работает правильно.

Я использую это в C :

 #include "stdafx.h"
#include "windows.h"

#pragma pack(1) // pack the following structures on one-byte boundaries

// Bird position structure
typedef struct tagBIRDPOSITION
{
    short   nX;         // x-coordinate
    short   nY;         // y-coordinate
    short   nZ;         // z-coordinate
}
BIRDPOSITION;

// Bird angles structure
typedef struct tagBIRDANGLES
{
    short   nAzimuth;   // azimuth angle
    short   nElevation; // elevation angle
    short   nRoll;      // roll angle
}
BIRDANGLES;

// Bird matrix structure
typedef struct tagBIRDMATRIX
{
    short   n[3][3];    // array of matrix elements
}
BIRDMATRIX;

// Bird quaternion structure
typedef struct tagBIRDQUATERNION
{
    short   nQ0;        // q0
    short   nQ1;        // q1
    short   nQ2;        // q2
    short   nQ3;        // q3
}
BIRDQUATERNION;

#pragma pack()  // resume normal packing of structures

typedef struct tagBIRDREADING
{
    BIRDPOSITION    position;   // position of receiver
    BIRDANGLES      angles;     // orientation of receiver, as angles
    BIRDMATRIX      matrix;     // orientation of receiver, as matrix
    BIRDQUATERNION  quaternion; // orientation of receiver, as quaternion
    WORD            wButtons;   // button states
}
BIRDREADING;

#define BIRD_MAX_DEVICE_NUM 126

// Bird frame structure
//
// NOTE: In stand-alone mode, the bird reading is stored in reading[0], and
//  all other array elements are unused.  In master/slave mode, the "reading"
//  array is indexed by bird number - for example, bird #1 is at reading[1],
//  bird #2 is at reading[2], etc., and reading[0] is unused.
typedef struct tagBIRDFRAME
{
    DWORD           dwTime;     // time at which readings were taken, in msecs
    BIRDREADING     reading[BIRD_MAX_DEVICE_NUM   1];  // reading from each bird
}
BIRDFRAME;

extern "C" __declspec( dllexport ) BOOL birdGetMostRecentFrame(int nGroupID, BIRDFRAME *pframe) {

    pframe->dwTime = GetTickCount();
    for (int i = 0; i < sizeof(pframe->reading)/sizeof(*pframe->reading); i  ) {
        pframe->reading[i] = BIRDREADING();
        pframe->reading[i].position.nX = (short)i;
        pframe->reading[i].position.nY = (short)i   1;
        pframe->reading[i].position.nZ = (short)i   2;
    }
    return TRUE;
}
  

И это в C#:

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BIRDPOSITION
{
    public short nX;
    public short nY;
    public short nZ;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BIRDANGLES
{
    public short nAzimuth;  // azimuth angle
    public short nElevation;    // elevation angle
    public short nRoll;     // roll angle
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct BIRDMATRIX
{
    public fixed short n[9];
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BIRDQUATERNION
{
    public short nQ0;       // q0
    public short nQ1;       // q1
    public short nQ2;       // q2
    public short nQ3;       // q3
}

    [StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct BIRDREADING
{
        public BIRDPOSITION position;
        public BIRDANGLES angles;
        public BIRDMATRIX matrix;
        public BIRDQUATERNION quaternion;
        public ushort wButtons;
}

[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct BIRDFRAME
{
        public uint dwTime;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 127)]
        public BIRDREADING[] readings;
}

    public partial class Form1 : Form
    {
        [DllImport(@"Bird.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern bool birdGetMostRecentFrame(int nGroupID, ref BIRDFRAME frame);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            BIRDFRAME bf = new BIRDFRAME();

            if (checkBox1.Checked)
            {                
                bf.readings = new BIRDREADING[127];
            }

            if (birdGetMostRecentFrame(0, ref bf)) {
                listBox1.Items.Add(bf.dwTime.ToString());
                Console.WriteLine(bf.dwTime.ToString());
                for (int k = 1; k <= 3; k  )
                {
                    var reading = bf.readings[k];
                    Console.WriteLine(String.Format("  [{0}][{1}][{2}]", reading.position.nX.ToString(), reading.position.nY.ToString(), reading.position.nZ.ToString()));
                    listBox1.Items.Add(String.Format("  [{0}][{1}][{2}]", reading.position.nX.ToString(), reading.position.nY.ToString(), reading.position.nZ.ToString()));
                }
            }
        }
    }
  

В обоих случаях (чтение, инициализированное как новое в C #, или нет, возвращает значимые данные:

 40067905
  [1][2][3]
  [2][3][4]
  [3][4][5]
40068653
  [1][2][3]
  [2][3][4]
  [3][4][5]
40069418
  [1][2][3]
  [2][3][4]
  [3][4][5]
40072585
  [1][2][3]
  [2][3][4]
  [3][4][5]
  

Сделал это на 64-разрядной версии Windows7.
Я прикрепляю ZIP-файл с обоими проектами для VS2010.

Вот ссылка:

http://hotfile.com/dl/115296617/9644496/testCS.zip.html

Похоже, что вы устанавливаете неправильные значения внутри этой структуры position :/

Комментарии:

1. Хорошо, итак, кажется довольно очевидным, что все маршалируется правильно. Я предполагаю, что проблема должна заключаться где-то еще в приложении C #.

Ответ №2:

Кажется, что sysconf.byNumDevices == 1 so i <= sysconf.byNumDevices выполняется только один раз, и вы читаете неинициализированные сегменты памяти.

Попробуйте распечатать sysconf.byNumDevices , и если это 1, попробуйте отследить эту проблему.

Ответ №3:

Массивы в .NET основаны на нуле, а не на единице. Может ли это быть так? Конечно, в .NET также предусмотрена строгая проверка границ, поэтому я ожидал, что ваш «последний» доступ к массиву также завершится ошибкой с исключением.

Не имея доступа к FlockOfBirds исходному коду, я не уверен, что еще вам сказать.

Комментарии:

1. Я должен был указать, что 0-й элемент пропускается, поскольку он зарезервирован для особого случая. Я полагаю, что массивы C тоже индексируются на 0.

2. Да, массивы c также основаны на 0.

Ответ №4:

Просто мысль: вам действительно нужен ref при переходе к вашему методу?

 FlockOfBirds.BIRDFRAME frame = new FlockOfBirds.BIRDFRAME();
FlockOfBirds.birdGetMostRecentFrame(1, ref frame);
  

Если я не ошибаюсь, то в c # все классы в любом случае передаются по ссылке. Возможно, я злюсь, но разве вы не передаете ссылку на ссылку?

Комментарии:

1. Я действительно помню что-то о том, что все непростые типы передаются через ref, но есть большая разница между передачей через ref и передачей ref.

2. Еще раз подумал об этом, и вот что я нашел: codeguru.com/forum/showthread.php?t=474375 Итак, как насчет того, чтобы попытаться преобразовать DWORD / WORD в int / short, а не в uint / ushort ?