2010-04-29 39 views
8

Đây là mã:Làm thế nào để xác định kết thúc của một mảng số nguyên khi thao tác với con trỏ nguyên?

int myInt[] ={ 1, 2, 3, 4, 5 }; 
int *myIntPtr = &myInt[0]; 
while(*myIntPtr != NULL) 
{ 
    cout<<*myIntPtr<<endl; 
    myIntPtr++; 
} 

Output: 12345....<junks>.......... 

Đối với mảng nhân vật: (Kể từ khi chúng tôi có một nhân vật NULL ở cuối, không có vấn đề trong khi lặp lại)

char myChar[] ={ 'A', 'B', 'C', 'D', 'E', '\0' }; 
char *myCharPtr = &myChar[0]; 
while(*myCharPtr != NULL) 
{ 
    cout<<*myCharPtr<<endl; 
    myCharPtr++; 
} 

Output: ABCDE 

Câu hỏi của tôi là kể từ khi chúng ta nói thêm NULL nhân vật như là kết thúc của chuỗi, chúng tôi loại trừ các vấn đề như vậy! Nếu trong trường hợp, quy tắc thêm 0 vào cuối mảng nguyên, chúng ta có thể tránh được vấn đề này. Nói gì?

Trả lời

10

Quy ước C-strings là kết thúc char * bằng '\ 0' char. Đối với mảng hoặc bất kỳ vùng chứa C++ nào khác, có các thành ngữ khác có thể được áp dụng.Tiếp sau sở thích của tôi

Cách tốt nhất để lặp trên chuỗi được sử dụng Range-based cho vòng lặp bao gồm trên C++ 0x

int my_array[] = {1, 2, 3, 4, 5}; 
for(int& x : my_array) 
{ 
    cout<<x<<endl; 
} 

Nếu trình biên dịch của bạn không cung cấp này chưa, sử dụng lặp

for(int* it = std::begin(array); it!=std::end(array); ++it) 
{ 
    cout<<*it<<endl; 
} 

Và nếu bạn không thể sử dụng không std :: bắt đầu/kết thúc

for(int* it = &array[0]; it!=&array[sizeof(array)]; ++it) 
{ 
    cout<<*it<<endl; 
} 

PS Boost. Foreach mô phỏng vòng lặp dựa trên phạm vi trên các trình biên dịch C++ 98

+0

Đâu là tiêu chuẩn :: bắt đầu/kết thúc được xác định? Nó có đi kèm với C++ 0x không? –

+0

@Christian: Có hoặc với Boost. – UncleBens

+0

@Christian Có đây là một tính năng mới của C++ liên quan đến Phạm vi. Boost.Range bao gồm điều này và nhiều hơn nữa, đơn giản hóa rất nhiều thuật toán trên trình tự. –

11

Trong C++ giải pháp tốt nhất là sử dụng std :: vector, không phải mảng. vectơ mang kích thước của chúng xung quanh với chúng. Vấn đề với việc sử dụng số không (hoặc bất kỳ giá trị nào khác) như là một điểm đánh dấu kết thúc là tất nhiên nó không thể xuất hiện ở đâu đó trong mảng. Đây không phải là vấn đề lớn đối với các chuỗi, vì chúng ta hiếm khi muốn in ký tự bằng mã số 0, nhưng nó là một vấn đề khi sử dụng các mảng ints.

+0

Tôi ổn với việc sử dụng tiêu chuẩn :: vector tại đây. Một câu hỏi chung tôi có. Làm thế nào trong vectơ, họ có thể xác định kích thước của mảng số nguyên? – AKN

+0

@AKN Chúng mang kích thước xung quanh như một giá trị riêng biệt của một số loại. –

+0

@AKN std: vector không chỉ là một mảng, nó có lẽ là một cấu trúc có chứa một mảng và một int cho chiều dài và có thể nhiều hơn nữa. – phkahler

3

Bạn chắc chắn có thể tự quyết định số "sentinel" value của riêng mình để lưu trữ ở cuối mảng số nguyên. Nếu các số nguyên của bạn luôn được mong đợi là không âm, ví dụ, bạn có thể sử dụng -1 làm giá trị sentinel đánh dấu phần cuối của mảng.

int myInt[] ={ 1, 2, 3, 4, 5, -1 }; 
int *myIntPtr = &myInt[0]; 
while(*myIntPtr >= 0) 
{ 
    cout<<*myIntPtr<<endl; 
    myIntPtr++; 
} 
1

Giá trị char 0 có ý nghĩa đặc biệt, được chuẩn hóa theo quy ước và thực hành. Giá trị int 0 không, vì vậy đây không thể là một quy tắc chung. Nếu nó hoạt động trong trường hợp cụ thể của bạn, bạn có thể đi với nó. Tuy nhiên, nói chung nó là tốt hơn để chỉ theo dõi chiều dài của mảng số nguyên một cách riêng biệt, vì điều này hoạt động phổ quát. Hoặc sử dụng std::vector hoặc một vùng chứa tương tự để xử lý công việc đó cho bạn.

0

Sử dụng tiêu chuẩn :: vectơ, như Neil nói.

Hoặc làm theo cách iterator:

int myInt[] ={ 100, 200, 300, 400, 500 }; 
int *myIntPtr = &myInt[0]; 
int *myIntPtr_end = myIntPtr + 5; 
while(myIntPtr != myIntPtr_end) 
    { 
    cout<<*myIntPtr<<endl; 
    ++myIntPtr; 
    } 
+0

Ở đây chúng ta thấy '5' và ofcourse đó là chiều dài mà chúng ta có thể sử dụng trực tiếp ngay! :) – AKN

+0

int * myIntPtr_end = myIntPtr + 5; Con trỏ * myIntPtr_end trỏ đến SOME_JUNK_VALUE sau giá trị cuối cùng. (tức là) sau 500. Vì lỗi thiết kế này, tôi cảm thấy nó không được khuyến khích. Nói gì? – AKN

+0

Nó không quan trọng mà nó trỏ đến rác, miễn là bạn không dereference con trỏ. Đối với chiều dài của mảng - bạn rõ ràng có nó trong ví dụ của bạn. Nếu bạn đang nói về con trỏ đến mảng không có thông tin về độ dài, đó là điều bạn cần thêm vào câu hỏi/mô tả của mình. –

3

gì về việc sử dụng sizeof? http://www.cppreference.com/wiki/keywords/sizeof

+0

Bởi vì không có điều đó - bạn đang nghĩ về sizeof. và điều này không thể được sử dụng khi mảng đã bị phân rã thành con trỏ. –

+0

@Neil Butterworth cảm ơn bạn, đó chỉ là một trường hợp accute của dislexia :) Tuy nhiên, không có gì nói về việc không thể truy cập vào mảng. Điều này cũng có thể được sử dụng thêm để thực hiện một cái gì đó như lớp mẫu mảng kiểm tra ranh giới. –

0
for(i=0; i < sizeof(myInt); i++) 
{ 
    cout<<*myIntPtr<<endl; 
    myIntPtr++; 
} 

Nếu bạn đang đề xuất mã của bạn nơi myIntPtr được thao tác không có ý tưởng về kích thước đoạn nó trỏ đến, bạn đã quyết định cho một giá trị kỳ diệu trong mảng int của bạn, hoặc tái cấu trúc mã của bạn để sizeof(myInt) cũng có sẵn.

Chức năng thư viện chuẩn C sử dụng phương pháp sau: bất cứ khi nào bạn cần chuyển vùng đệm qua con trỏ, bạn phải chuyển kích thước của chúng trong cùng một cuộc gọi.

+1

Cảm ơn kostas. Nhưng sizeof (myInt) sẽ không trực tiếp cung cấp kích thước mảng. Vì vậy, thay vào đó, điều này có thể được thay đổi thành 'int nIntArrSize = sizeof (myInt)/sizeof (int)' – AKN

1

Thứ nhất, chúng ta không "thêm ký tự NULL" vào cuối chuỗi. Không có thứ như "ký tự NULL". Chúng tôi thêm số không, đôi khi được gọi là "ký tự NUL". Nhưng NULL hoàn toàn không liên quan gì đến nó. NULL thường được sử dụng trong ngữ cảnh con trỏ chứ không phải trong ngữ cảnh ký tự hoặc số nguyên. So sánh của bạn như *myCharPtr != NULL hoặc *myIntPtr != NULL sẽ biên dịch (do cách NULL được định nghĩa trong C++), nhưng hầu như không có ý nghĩa. Nếu bạn đang tìm kiếm một số không trong một mảng, bạn có thể kiểm tra nó là *myCharPtr != '\0' hoặc như *myCharPtr != 0 hoặc đơn giản là *myCharPtr, nhưng không bao giờ là *myCharPtr != NULL.

Thứ hai, ký tự 0 được gọi là ký tự 0 vì lý do: số này bằng với số nguyên không. Kiểu ký tự trong C++ chỉ là một kiểu số nguyên đơn giản. Lý do duy nhất chúng ta có thể sử dụng ký tự zero như một cái gì đó đặc biệt trong ngữ cảnh chuỗi là bởi vì ý nghĩa của nó được dành riêng cho mục đích cụ thể đó. Trong trường hợp chung trong bối cảnh số nguyên, đặt không cho mục đích đó là rõ ràng là không thể vì các lý do rõ ràng: số không là hữu ích như bất kỳ giá trị số nguyên nào khác. Tuy nhiên, nếu trong số nguyên ứng dụng cụ thể của bạn số không có thể được sử dụng như một giá trị dành riêng, cảm thấy tự do để sử dụng nó theo cách đó. Hoặc bạn có thể sử dụng bất kỳ giá trị số nguyên nào khác cho mục đích đó. Nhưng trong trường hợp chung, đề cập đến câu hỏi bạn đặt ra trong tiêu đề, có không có cách nào để xác định kết thúc của một mảng. Đó là trách nhiệm của bạn để biết nơi kết thúc là (bằng cách biết tổng số yếu tố hoặc bằng cách đánh dấu sự kết thúc với một giá trị dành riêng của sự lựa chọn của bạn hoặc theo một cách khác). Không có cách nào để xác định kết thúc của một mảng ngay cả với các chuỗi, bởi vì tất cả những gì bạn có thể hy vọng là tìm ra đầu của chuỗi , không nhất thiết là kết thúc của mảng lưu trữ chuỗi đó.

Nếu bạn đã thêm một số không vào cuối mảng số nguyên, chu kỳ đầu tiên của bạn sẽ vui vẻ dừng lại ở đó. Vì lý do nào đó, bạn đã thêm \0 vào cuối mảng ký tự của mình (và chu kỳ thứ hai dừng), nhưng bạn không thêm số 0 vào cuối mảng số nguyên (và chu kỳ đầu tiên không dừng). Bạn đang tự hỏi tại sao chu kỳ đầu tiên của bạn không dừng lại ở số không? Vì bạn đã không đặt số 0 vào đó. Nó là đơn giản.

1

Cả hai chuẩn ASCII và Unicode đều xác định một ký tự có giá trị 0 là ký tự NULL, không phải là điểm đánh dấu đầu cuối của mảng/chuỗi. Nó chỉ là quy ước C/C++ mà các chuỗi được chấm dứt với ký tự này. Pascal sử dụng ký pháp khác. Ngoài ra, ký tự NULL không nhất thiết chỉ ra phần cuối của mảng chứa chuỗi. Có một số hàm API Win32 sử dụng đôi null chấm dứt chuỗi (hộp thoại tập tin mở cho một), như thế này:

"one\0two\0three\0" // there's an implicit '\0' appended in C/C++ 

này được hợp lệ C/C++, nhân vật NULL không có nghĩa là kết thúc của mảng .

Để điều chỉnh ý tưởng này về giá trị NULL thành mảng nguyên có nghĩa là bạn phải hy sinh một trong các giá trị số nguyên của mình. Nếu dữ liệu của bạn bao gồm một tập hợp con của tập hợp các số nguyên thì đây không phải là vấn đề nhưng nếu dữ liệu của bạn có thể bao gồm bất kỳ giá trị nguyên nào, thì không có cách nào để xác định nếu một số nguyên nhất định là điểm đánh dấu kết thúc hoặc một giá trị hợp lệ. Trong trường hợp thứ hai này, bạn cần thêm thông tin về số phần tử trong mảng, theo cách thủ công hoặc tự động thông qua một std :: vector.

0

Cách chung để tạo con trỏ kết thúc cho bất kỳ mảng nào như sau: Đầu tiên xác định số phần tử trong mảng sử dụng sizeof(array)/sizeof(array[0]). Lưu ý rằng sizeof xuất hiện hai lần vì nó trả về kích thước của một mục theo byte. Vì vậy, đối với một mảng tĩnh, đây là kích thước của mảng chia cho kích thước của một phần tử trong mảng. Sau đó, con trỏ đến một mảng là array+number_of_elements. Vì vậy, điều này sẽ làm việc:

int myInt[]={1, 2, 3, 4, 5}; 
int myIntNumElements = sizeof(myInt)/sizeof(myInt[0]); 
int *myIntEnd = myInt + myIntNumElelents; 

for (int *myIntPtr = myInt; myInt != myIntEnd; myIntPtr++) 
    { 
    cout << *myIntPtr << endl; 
    } 

Và bây giờ đối với một số hãy cẩn thận:

điểm con trỏ
  • Các dấu chấm hết cho một vị trí chỉ sau khi kết thúc mảng! Vì vậy, *myIntPtr trả về rác, không phải giá trị của phần tử cuối cùng trong mảng.
  • Điều này chỉ tốt cho các mảng tĩnh, thông thường! Đối với vùng chứa, hãy sử dụng các chức năng thành viên và vòng lặp của thành viên beginend.
  • Cách tiếp cận này sẽ hoạt động với bất kỳ phiên bản nào của C++. Tuy nhiên, nếu bạn đang sử dụng C++ - 11 hoặc mới hơn, đó là khuyến khích sử dụng các chức năng std::beginstd::end trong câu lệnh for như sau:

    for (int *myIntPtr = std::begin(myInt); myIntPtr != std::end(myIntPtr); myIntPtr++)

  • Phương pháp này được thiết kế để được xem xét trong ngoài các câu trả lời khác. Cái nào tốt nhất là vấn đề ngữ cảnh.

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