If you're using P/Invoke and you want to pass a list of elements to a C function, you can use attributes in most cases to have the work done for you. Eg.
[DllImport("x.dll")]
extern void PassDoubleArray([MarshalAs(UnmanagedType.LPArray)]Double[] array);
If you have something a bit more complex and need to do it manually, the Marshal class has most of what you need to get it done.
First, allocate some unmanaged memory - enough to store your object or array, using Marshal.AllocHGlobal, which will give you an IntPtr. You can copy .NET primitives to and from the unmanaged memory by using Marshal.Copy. For non primitives, create structs and use Marshal.StructureToPtr to get an IntPtr for it. When you're done with the unmanaged memory, make a call to Marshal.FreeHGlobal to free it up.
eg, this will do the equivalent of the above attribute, but more long-winded.
[DllImport("x.dll")]
extern void PassDoubleArray(IntPtr array);
List<Double> vals = new List<Double>() { ... }
IntPtr unmanaged = Marshal.AllocHGlobal(vals.Length * sizeof(Double));
Marshal.Copy(vals.ToArray(), 0, unmanaged, vals.Length);
PassDoubleArray(unmanaged);
Marshal.FreeHGlobal(unmanaged);