2011-08-14 43 views
5

Tôi hiện đang làm việc trên một số mã C#/C++ sử dụng lời gọi. Trong phía C++ có một std :: vector đầy đủ các con trỏ được xác định bởi chỉ mục từ mã C#, ví dụ như một khai báo hàm sẽ trông như thế này:Có an toàn khi giữ các con trỏ C++ trong C# không?

void SetName(char* name, int idx) 

Nhưng bây giờ tôi nghĩ, vì tôi ' đang làm việc với con trỏ tôi không thể gửi đến C# địa chỉ con trỏ bản thân sau đó trong mã tôi có thể làm một cái gì đó như thế này:

void SetName(char*name, int ptr) 
{ 
    ((TypeName*)ptr)->name = name; 
} 

Rõ ràng đó là một phiên bản nhanh chóng về những gì tôi nhận được tại (và có lẽ sẽ không biên dịch).

Địa chỉ con trỏ có được đảm bảo giữ nguyên trong C++ sao cho tôi có thể lưu trữ địa chỉ của nó một cách an toàn trong C# hoặc điều này quá không ổn định hoặc nguy hiểm vì một lý do nào đó?

+4

Chỉ cần làm rõ: Bạn biết rằng mã trên sẽ chỉ biên dịch trong C# nếu nó nằm trong khối 'không an toàn'? –

Trả lời

2

Vâng, không sao cả. Phân bổ bộ nhớ riêng không bao giờ di chuyển để lưu trữ con trỏ trong IntPtr ở bên C# là tốt. Bạn cần một số loại chức năng pinvoked trả về con trỏ này, sau đó

[DllImport("something.dll", CharSet = CharSet.Ansi)] 
void SetName(IntPtr vector, string name, int index); 

nào cố tình nói dối về C++ chức năng:

void SetName(std::vector<std::string>* vect, const char* name, int index) { 
    std::string copy = name; 
    (*vect)[index] = copy; 
} 

Lưu ý việc sử dụng mới trong ++ mã C, bạn phải sao chép chuỗi.Các tên đối số được thông qua trỏ đến bộ đệm được chỉ định bởi trình khắc phục sự cố và chỉ hợp lệ trong thời gian của thân hàm. Mã ban đầu của bạn không thể hoạt động. Nếu bạn có ý định trả lại con trỏ tới vector <> các phần tử sau đó là rất cẩn thận. Một vectơ tái phân bổ mảng nội bộ của nó khi bạn thêm các phần tử. Như vậy một con trỏ trả về sau đó sẽ trở thành không hợp lệ và bạn sẽ bị hỏng heap khi bạn sử dụng nó sau này. Điều tương tự cũng xảy ra với Danh sách C# <> nhưng không có rủi ro về các con trỏ lơ lửng.

+2

Nhân tiện, việc sử dụng 'mới' đó hoàn toàn sai trong C++. Nó chỉ nên là 'vect [index] = name;'. Và tôi thậm chí không chắc chắn nếu giả vờ rằng một con trỏ thực sự là một tham chiếu thậm chí còn an toàn. – Puppy

+0

Đã sửa lỗi. Chứng minh việc sao chép chuỗi là khá quan trọng. Và có, nó là an toàn. –

+0

Đó không thực sự là những gì tôi định nghĩa là * tốt hơn *. Giống như, bạn đã trao đổi một vấn đề mà trình biên dịch sẽ nói với bạn là sai cho một vấn đề mà bạn sẽ chỉ tìm ra khi bạn nhận được tham nhũng đống vì con trỏ thô là không an toàn khủng khiếp và bạn đã xóa nó một cách tình cờ hoặc bị rò rỉ nó. Bất cứ ai có thể đọc C++ cơ bản sẽ lưu trữ 'std :: vector ' và chỉ làm 'vect [index] = name;' và biết rằng nó tạo thành một bản sao. Ít nhất sử dụng một con trỏ thông minh hoặc một cái gì đó. Xin vui lòng. Đưa ra mã xấu như vậy làm ví dụ là dạy người khác những điều sai trái. – Puppy

1

Tôi nghĩ nó ổn định cho đến khi bạn lệnh mã C++ và hoàn toàn nhận thức được những gì mình làm, và các nhà phát triển khác làm việc trên cùng một mã cũng biết về mối nguy hiểm đó.

Vì vậy, theo ý kiến ​​của tôi, nó không phải là cách rất an toàn của kiến ​​trúc, và tôi sẽ tránh nó nhiều nhất có thể.

Trân trọng.

7

Trong C#, bạn không cần phải sử dụng một con trỏ ở đây, bạn chỉ có thể sử dụng một chuỗi C# đơn giản.

[DllImport(...)] 
extern void SetName(string name, int id); 

Điều này hoạt động vì hành vi mặc định của chuỗi trong p/invoke là chuyển đổi thành char kiểu C *. Bạn có thể đánh dấu từng đối số trong khai báo C# một cách rõ ràng nếu nó yêu cầu một số cách khác để được marshalled, ví dụ: [MarshalAs(UnmanagedType.LPWStr)], đối với một arg sử dụng chuỗi 2 byte cho mỗi ký tự.

Lý do duy nhất để sử dụng con trỏ là nếu bạn cần giữ quyền truy cập vào dữ liệu được trỏ đến sau khi bạn đã gọi hàm. Thậm chí sau đó, bạn có thể sử dụng hầu hết các thông số out.

Bạn có thể p/gọi cơ bản mọi thứ mà không yêu cầu con trỏ (và do đó không yêu cầu mã không an toàn, yêu cầu thực thi đặc quyền trong một số môi trường).

1

C# GC di chuyển mọi thứ, nhưng vùng C++ không di chuyển bất cứ thứ gì - con trỏ đến đối tượng được phân bổ là đảm bảo để giữ nguyên cho đến khi bạn delete. Kiến trúc tốt nhất cho tình huống này chỉ là gửi con trỏ tới C# dưới dạng IntPtr và sau đó đưa nó trở lại trong C++.

Đó chắc chắn là một ý tưởng cực kỳ tuyệt vời, tuyệt vời hơn so với số nguyên vô hạn của BAD, số nguyên HORRIFIC mà bạn đã đến đó.

+0

C# GC không di chuyển chuỗi mặc dù. – Grozz

+1

@ Grozz - nó chắc chắn nhất, không phải mọi chuỗi được thực tập. –

Các vấn đề liên quan