Về cơ bản, tôi hiểu cách sử dụng con trỏ, nhưng không phải cách sử dụng chúng tốt nhất để lập trình tốt hơn.
Các dự án hoặc vấn đề tốt để giải quyết liên quan đến việc sử dụng con trỏ là gì để tôi có thể hiểu chúng tốt hơn?
Về cơ bản, tôi hiểu cách sử dụng con trỏ, nhưng không phải cách sử dụng chúng tốt nhất để lập trình tốt hơn.
Các dự án hoặc vấn đề tốt để giải quyết liên quan đến việc sử dụng con trỏ là gì để tôi có thể hiểu chúng tốt hơn?
Câu trả lời:
Thao tác một lượng lớn dữ liệu trong bộ nhớ là nơi con trỏ thực sự tỏa sáng.
Truyền một đối tượng lớn bằng tham chiếu tương đương với việc truyền dọc theo một số cũ đơn giản. Bạn có thể thao tác trực tiếp các bộ phận cần thiết thay vì sao chép một đối tượng, thay đổi nó, sau đó trả lại bản sao để đặt vào vị trí ban đầu.
Khái niệm con trỏ cho phép bạn tham chiếu dữ liệu theo địa chỉ mà không cần sao chép việc lưu trữ dữ liệu. Cách tiếp cận này cho phép viết các thuật toán hiệu quả như:
Sắp xếp
Khi di chuyển dữ liệu theo thuật toán sắp xếp, bạn có thể di chuyển con trỏ thay vì dữ liệu mà chính mình nghĩ là sắp xếp hàng triệu hàng trên chuỗi 100 ký tự; bạn tiết kiệm rất nhiều chuyển động dữ liệu không cần thiết.
Danh sách được liên kết
Bạn có thể lưu trữ vị trí mục tiếp theo và / hoặc trước đó và không phải toàn bộ dữ liệu được liên kết với bản ghi.
Truyền tham số
Trong trường hợp này, bạn chuyển địa chỉ của dữ liệu thay vì chính dữ liệu. Một lần nữa, hãy nghĩ về một thuật toán nén tên chạy trên hàng triệu hàng.
Khái niệm này có thể được mở rộng cho các cấu trúc dữ liệu như cơ sở dữ liệu quan hệ trong đó con trỏ gần giống với khóa ngoại . Một số ngôn ngữ không khuyến khích sử dụng các con trỏ như C # và COBOL.
Ví dụ có thể được tìm thấy ở nhiều nơi như:
Bài viết sau đây có thể có liên quan theo một cách nào đó:
Tôi ngạc nhiên không có câu trả lời nào khác đề cập đến điều này: con trỏ cho phép bạn tạo các cấu trúc dữ liệu không liền kề và phi tuyến tính, trong đó một phần tử có thể liên quan đến nhiều phần tử khác theo những cách phức tạp.
Danh sách được liên kết (đơn lẻ, gấp đôi và liên kết tròn), cây (đỏ-đen, AVL, trie, nhị phân, phân vùng không gian) và đồ thị là tất cả các ví dụ về cấu trúc có thể được xây dựng một cách tự nhiên nhất về mặt tham chiếu thay vì chỉ là giá trị .
Một cách đơn giản là trong Đa hình. Đa hình chỉ hoạt động với con trỏ.
Ngoài ra, bạn sử dụng con trỏ bất cứ lúc nào bạn cần phân bổ bộ nhớ động. Trong C, điều này thường xảy ra khi bạn cần lưu trữ dữ liệu vào một mảng nhưng bạn không biết kích thước khi biên dịch. Sau đó, bạn sẽ gọi malloc để phân bổ bộ nhớ và một con trỏ để truy cập nó. Ngoài ra, cho dù bạn có biết hay không, khi bạn sử dụng một mảng bạn đang sử dụng con trỏ.
for(int i = 0; i < size; i++)
std::cout << array[i];
tương đương với
for(int i = 0; i < size; i++)
std::cout << *(array + i);
Kiến thức này cho phép bạn thực hiện những điều thực sự thú vị như sao chép toàn bộ một mảng trong một dòng:
while( (*array1++ = *array2++) != '\0')
Trong c ++, bạn sử dụng mới để phân bổ bộ nhớ cho một đối tượng và lưu trữ nó vào một con trỏ. Bạn làm điều này bất cứ lúc nào bạn cần để tạo một đối tượng trong thời gian chạy thay vì trong thời gian biên dịch (tức là một phương thức tạo một đối tượng mới và lưu trữ nó vào một danh sách).
Để hiểu con trỏ tốt hơn:
Tìm một số dự án sử dụng thừa kế.
Đây là dự án mà tôi đã cắt răng:
Đọc hai ma trận nxn từ một tệp và thực hiện các thao tác không gian vectơ cơ bản trên chúng và in kết quả của chúng ra màn hình.
Để làm điều này, bạn phải sử dụng mảng động và tham khảo các mảng của bạn bằng các con trỏ vì bạn sẽ có hai mảng mảng (mảng động đa chiều). Sau khi bạn hoàn thành dự án đó, bạn sẽ có một ý tưởng khá hay về cách sử dụng con trỏ.
Để thực sự hiểu tại sao con trỏ rất quan trọng, bạn cần hiểu sự khác biệt giữa phân bổ heap và phân bổ stack.
Sau đây là một ví dụ về phân bổ ngăn xếp:
struct Foo {
int bar, baz
};
void foo(void) {
struct Foo f;
}
Các đối tượng được phân bổ trên ngăn xếp chỉ tồn tại trong suốt thời gian thực hiện chức năng hiện tại. Khi cuộc gọi foo
đi ra khỏi phạm vi, biến cũng vậy f
.
Một trường hợp điều này trở thành một vấn đề là khi bạn cần trả về một cái gì đó không phải là một kiểu tích phân từ một hàm (ví dụ cấu trúc Foo từ ví dụ trên).
Ví dụ, hàm sau sẽ dẫn đến cái gọi là "hành vi không xác định."
struct Foo {
int bar, baz
};
struct Foo *foo(void) {
struct Foo f;
return &f;
}
Nếu bạn muốn trả về một cái gì đó giống như struct Foo *
từ một chức năng, thứ bạn thực sự cần là phân bổ heap:
struct Foo {
int bar, baz
};
struct Foo *foo(void) {
return malloc(sizeof(struct Foo));
}
Hàm malloc
phân bổ một đối tượng trên heap và trả về một con trỏ tới đối tượng đó. Lưu ý rằng thuật ngữ "đối tượng" được sử dụng một cách lỏng lẻo ở đây, có nghĩa là "một cái gì đó" chứ không phải là đối tượng theo nghĩa lập trình hướng đối tượng.
Tuổi thọ của các đối tượng phân bổ heap được điều khiển bởi lập trình viên. Bộ nhớ cho đối tượng này sẽ được bảo lưu cho đến khi lập trình viên giải phóng nó, tức là bằng cách gọi free()
hoặc cho đến khi chương trình thoát.
Chỉnh sửa : Tôi không nhận thấy câu hỏi này được gắn thẻ dưới dạng câu hỏi C ++. Các toán tử C ++ new
và new[]
thực hiện chức năng tương tự như malloc
. Các toán tử delete
và delete[]
tương tự như free
. Mặc dù new
và delete
nên được sử dụng riêng để phân bổ và giải phóng các đối tượng C ++, việc sử dụng malloc
và free
hoàn toàn hợp pháp trong mã C ++.
Viết bất kỳ dự án không tầm thường nào trong C và bạn S to phải tính toán cách thức / thời điểm sử dụng con trỏ. Trong C ++, bạn chủ yếu sẽ sử dụng các đối tượng hỗ trợ RAII để quản lý các con trỏ bên trong, nhưng trong C con trỏ thô có vai trò phổ biến hơn nhiều. Đối với loại dự án bạn nên làm, nó có thể là bất cứ điều gì không tầm thường:
Tôi đề nghị cái cuối cùng.
Hầu như tất cả các vấn đề lập trình có thể được giải quyết bằng con trỏ đều có thể được giải quyết bằng các loại tham chiếu an toàn khác (không tham chiếu đến các tham chiếu C ++ , nhưng khái niệm CS chung về việc có một biến tham chiếu đến giá trị của dữ liệu được lưu trữ ở nơi khác).
Con trỏ bằng cách triển khai các tham chiếu cấp thấp cụ thể, trong đó bạn có thể thao tác trực tiếp các địa chỉ bộ nhớ rất mạnh, nhưng có thể hơi nguy hiểm khi sử dụng (ví dụ: trỏ đến các vị trí bộ nhớ bên ngoài chương trình).
Lợi ích của việc sử dụng con trỏ trực tiếp là chúng sẽ nhanh hơn một chút bằng cách không phải thực hiện bất kỳ kiểm tra an toàn nào. Các ngôn ngữ như Java không trực tiếp triển khai con trỏ kiểu C sẽ bị ảnh hưởng hiệu năng nhẹ, nhưng sẽ giảm nhiều loại tình huống khó gỡ lỗi.
Đối với lý do tại sao bạn cần gián tiếp, danh sách khá dài, nhưng về cơ bản hai ý chính là:
selected_object
là một tham chiếu đến một trong các đối tượng sẽ hiệu quả hơn nhiều so với việc sao chép giá trị của đối tượng hiện tại vào một biến mới.Thao tác hình ảnh ở mức pixel hầu như luôn luôn dễ dàng hơn và nhanh hơn bằng cách sử dụng con trỏ. Đôi khi chỉ có thể sử dụng con trỏ.
Con trỏ được sử dụng trong rất nhiều ngôn ngữ lập trình bên dưới bề mặt mà không làm phiền người dùng về nó. C / C ++ chỉ cung cấp cho bạn quyền truy cập vào chúng.
Khi nào nên sử dụng chúng: Càng thường xuyên càng tốt, vì sao chép dữ liệu không hiệu quả. Khi nào không sử dụng chúng: Khi bạn muốn hai bản sao có thể thay đổi riêng lẻ. (Về cơ bản, cuối cùng sẽ sao chép nội dung của đối tượng_1 vào một nơi khác trong bộ nhớ và trả về một con trỏ - lần này chỉ vào đối tượng_2)
Con trỏ là một phần thiết yếu cho bất kỳ triển khai cấu trúc dữ liệu nào trong C và cấu trúc dữ liệu là một phần thiết yếu của bất kỳ chương trình không tầm thường nào.
Nếu bạn muốn tìm hiểu lý do tại sao con trỏ lại rất quan trọng, thì tôi khuyên bạn nên tìm hiểu danh sách được liên kết là gì và cố gắng viết một danh sách mà không sử dụng con trỏ. Tôi chưa đặt cho bạn một thử thách không thể, (GỢI Ý: con trỏ được sử dụng để tham chiếu các vị trí trong bộ nhớ, làm thế nào để bạn tham chiếu mọi thứ trong mảng?).
Như một ví dụ thực tế, xây dựng một cuốn sách đặt hàng giới hạn.
ví dụ, nguồn cấp dữ liệu ITCH 4.1 có khái niệm về các đơn đặt hàng "thay thế", trong đó giá cả (và do đó, mức độ ưu tiên) có thể thay đổi. Bạn muốn có thể nhận đơn đặt hàng và di chuyển chúng đi nơi khác. Việc thực hiện một hàng đợi hai đầu với các con trỏ làm cho hoạt động rất dễ dàng.
Lướt qua các trang web StackExchange khác nhau, tôi nhận ra rằng thật khó để hỏi một câu hỏi như thế này. Có nguy cơ bị chỉ trích và hạ bệ, tôi sẽ thành thật. Điều này không có nghĩa là để troll hoặc ngọn lửa, tôi chỉ có nghĩa là để giúp đỡ, bằng cách đưa ra một đánh giá trung thực của câu hỏi.
Và đánh giá đó như sau: Đây là một câu hỏi rất lạ đối với một lập trình viên C. Gần như tất cả những gì nó nói là "Tôi không biết C." Nếu tôi phân tích sâu hơn và cay độc hơn, sẽ có một câu hỏi tiếp theo cho "câu hỏi" này: "Có một số phím tắt tôi có thể thực hiện để nhanh chóng và có được kiến thức của một lập trình viên C có kinh nghiệm, mà không cần dành thời gian cho nghiên cứu độc lập? " Bất kỳ "câu trả lời" nào mà ai đó có thể đưa ra sẽ không thay thế cho việc đi và thực hiện công việc tìm hiểu khái niệm cơ bản và cách sử dụng.
Tôi cảm thấy việc đi học C tốt hơn là trực tiếp hơn là truy cập web và hỏi mọi người điều này. Khi bạn biết rõ về C, bạn sẽ không buồn hỏi những câu hỏi như thế này, nó sẽ giống như hỏi "vấn đề nào được giải quyết tốt nhất bằng cách sử dụng bàn chải đánh răng?"
Mặc dù con trỏ thực sự tỏa sáng trong khi làm việc với các đối tượng bộ nhớ lớn, vẫn có một cách để làm điều tương tự mà không có chúng.
Con trỏ thực sự cần thiết cho cái gọi là lập trình động , đó là khi bạn không biết mình sẽ cần bao nhiêu bộ nhớ trước khi chương trình thực thi. Trong lập trình động, bạn có thể yêu cầu các khối bộ nhớ trong thời gian chạy và đặt dữ liệu của riêng bạn vào đó - vì vậy bạn cần con trỏ hoặc tham chiếu (sự khác biệt không quan trọng ở đây) để có thể làm việc với các khối dữ liệu đó.
Miễn là bạn có thể yêu cầu bộ nhớ nhất định trong thời gian chạy và đặt dữ liệu của bạn vào bộ nhớ mới thu được, bạn có thể thực hiện các thao tác sau:
Bạn có thể có cấu trúc dữ liệu tự mở rộng. Đó là những cấu trúc có thể tự mở rộng bằng cách yêu cầu bộ nhớ bổ sung miễn là hết dung lượng. Thuộc tính quan trọng của mọi cấu trúc tự mở rộng là nó bao gồm các khối bộ nhớ nhỏ (các nút được gọi là, các mục danh sách, v.v. tùy thuộc vào cấu trúc) và mỗi khối chứa các tham chiếu đến (các) khối khác. Cấu trúc "được liên kết" này xây dựng phần lớn các loại dữ liệu hiện đại: biểu đồ, cây, danh sách, v.v.
Bạn có thể lập trình bằng cách sử dụng mô hình OOP (Lập trình hướng đối tượng). Toàn bộ OOP dựa trên việc sử dụng các biến không trực tiếp, nhưng tham chiếu đến các thể hiện của lớp (các đối tượng được gọi là) và thao tác với chúng. Không có cá thể đơn lẻ nào có thể tồn tại mà không có con trỏ (mặc dù có thể sử dụng các lớp chỉ tĩnh ngay cả khi không có con trỏ, đó là một ngoại lệ).
Thật buồn cười, tôi vừa trả lời một câu hỏi về C ++ và nói về con trỏ.
Phiên bản ngắn là bạn KHÔNG BAO GIỜ cần con trỏ trừ khi 1) thư viện bạn đang sử dụng bắt buộc bạn 2) Bạn cần một tài liệu tham khảo không thể bỏ qua.
Nếu bạn cần một mảng, danh sách, chuỗi, v.v. chỉ cần có nó trên ngăn xếp và sử dụng một đối tượng stl. Trả lại hoặc chuyển các đối tượng stl rất nhanh (thực tế không được kiểm tra) vì chúng có mã bên trong sao chép một con trỏ thay vì một đối tượng và sẽ chỉ sao chép dữ liệu nếu bạn ghi vào nó. Đây là C ++ thông thường, thậm chí không phải là C ++ 11 mới, điều này sẽ giúp người viết thư viện dễ dàng hơn.
Nếu bạn sử dụng một con trỏ, hãy chắc chắn rằng nó thuộc một trong hai điều kiện sau. 1) Bạn đang chuyển đầu vào có thể là null. Một ví dụ là một tên tệp tùy chọn. 2) Nếu bạn muốn cho đi quyền sở hữu. Như trong trường hợp bạn vượt qua hoặc trả lại con trỏ, bạn không còn BẤT K copy bản sao nào của nó và cũng không sử dụng con trỏ bạn cho đi
ptr=blah; func(ptr); //never use ptr again for here on out.
Nhưng tôi chưa sử dụng con trỏ hoặc con trỏ thông minh trong một thời gian rất dài và tôi đã lập hồ sơ ứng dụng của mình. Nó chạy rất nhanh.
BỔ SUNG: Tôi nhận thấy rằng tôi viết các cấu trúc của riêng mình và tôi vượt qua chúng. Vì vậy, làm thế nào để tôi làm điều này mà không sử dụng con trỏ? Nó không phải là một container STL nên đi qua ref là chậm. Tôi luôn tải danh sách dữ liệu / deques / maps của mình và như vậy. Tôi không nhớ trả lại bất kỳ đối tượng nào trừ khi đó là một loại danh sách / bản đồ. Thậm chí không phải là một chuỗi. Tôi đã xem mã cho các đối tượng đơn lẻ và tôi nhận thấy tôi làm một cái gì đó như thế này { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }
vì vậy tôi trả lại khá nhiều đối tượng (nhiều) hoặc preallocate / pass trong một tham chiếu để điền (đơn).
Bây giờ nếu bạn đang viết deque của riêng bạn, bản đồ, vv bạn sẽ cần sử dụng con trỏ. Nhưng bạn không cần. Hãy để STL và có thể thúc đẩy lo lắng về điều đó. Bạn chỉ cần viết dữ liệu và các giải pháp. Không phải container để giữ chúng;)
Tôi hy vọng bạn bây giờ không bao giờ sử dụng con trỏ: D. Chúc may mắn đối phó với libs buộc bạn phải
Con trỏ rất hữu ích để làm việc với các thiết bị ánh xạ bộ nhớ. Bạn có thể định nghĩa một cấu trúc phản ánh (giả sử) một thanh ghi điều khiển, sau đó gán nó cho địa chỉ của thanh ghi điều khiển thực tế trong bộ nhớ và thao tác trực tiếp với nó. Bạn cũng có thể trỏ trực tiếp vào bộ đệm chuyển trên thẻ hoặc chip nếu MMU đã ánh xạ nó vào không gian bộ nhớ hệ thống.
Tôi thấy con trỏ là ngón trỏ của mình, chúng tôi sử dụng để làm một vài điều:
xin vui lòng tha thứ cho câu trả lời nghèo nàn này
Khi câu hỏi của bạn được gắn thẻ C ++, tôi sẽ trả lời câu hỏi của bạn cho ngôn ngữ đó.
Trong C ++ có sự phân biệt giữa con trỏ và tham chiếu, do đó, có hai kịch bản trong đó con trỏ (hoặc con trỏ thông minh) là cần thiết để tạo thuận lợi cho một số hành vi nhất định. Chúng có thể được sử dụng trong các trường hợp khác, tuy nhiên bạn hỏi làm thế nào "tốt nhất để sử dụng chúng" và trong tất cả các trường hợp khác, có những lựa chọn thay thế tốt hơn.
Một con trỏ lớp cơ sở cho phép bạn gọi một phương thức ảo phụ thuộc vào loại đối tượng mà con trỏ trỏ tới.
Con trỏ là cần thiết khi tạo một đối tượng động (trên heap thay vì stack). Điều này là cần thiết khi bạn muốn các đối tượng đó dài hơn phạm vi mà nó được tạo.
Về mặt "các dự án tốt hoặc các vấn đề cần giải quyết", như những người khác đã nói ở đây đã có, bất kỳ dự án không tầm thường nào cũng sẽ sử dụng các con trỏ.