2012-01-22 27 views
10

Tôi cố gắng để hiểu tại sao các đoạn mã sau đây được đưa ra một lỗi segmentation:strtok Phân khúc lỗi

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    tokenize("this is a test"); 
} 

Tôi biết rằng strtok() không thực sự tokenize trên xâu, nhưng trong trường hợp này, line điểm trực tiếp vào chuỗi "this is a test" là nội bộ một mảng của char. Có bất kỳ tokenization line mà không cần sao chép nó vào một mảng?

+2

Dude - "đây là bài kiểm tra" là STRING LITERAL. Có nghĩa là nó là một mảng * READ ONLY * "của char". Bạn thậm chí có thể thoát khỏi với cố gắng sửa đổi nó mà không bị rơi trên các nền tảng nhất định. Nhưng nó chắc chắn là một không-không có trên nền tảng * BẤT CỨ :) – paulsm4

Trả lời

14

Vấn đề là bạn đang cố sửa đổi chuỗi ký tự. Làm như vậy làm cho hành vi của chương trình của bạn không được xác định.

Giả sử bạn không được phép sửa đổi chuỗi chữ là một sự đơn giản hóa. Nói rằng các chuỗi ký tự là const là không chính xác; họ không có.

CẢNH BÁO: Thông báo sau.

Chuỗi ký tự "this is a test" có biểu thức loại char[15] (14 cho độ dài, cộng 1 cho kết thúc '\0').Trong hầu hết các ngữ cảnh, kể cả ngữ cảnh này, một biểu thức như vậy được chuyển đổi hoàn toàn thành con trỏ tới phần tử đầu tiên của mảng, thuộc loại char*.

Hành vi cố gắng sửa đổi mảng được tham chiếu bằng một chuỗi ký tự là không xác định - không phải vì nó là const (không phải), mà vì tiêu chuẩn C cụ thể nói rằng nó không xác định.

Một số trình biên dịch có thể cho phép bạn thoát khỏi điều này. Mã của bạn thực sự có thể sửa đổi mảng tĩnh tương ứng với chữ (có thể gây ra sự nhầm lẫn lớn sau này).

Hầu hết các trình biên dịch hiện đại, sẽ lưu trữ mảng trong bộ nhớ chỉ đọc - không phải ROM vật lý, nhưng trong vùng bộ nhớ được bảo vệ khỏi hệ thống bộ nhớ ảo. Kết quả của việc cố gắng sửa đổi bộ nhớ như vậy thường là lỗi phân đoạn và lỗi chương trình.

Vậy tại sao không phải là chuỗi ký tự const? Vì bạn thực sự không nên cố gắng sửa đổi chúng, nó chắc chắn sẽ có ý nghĩa - và C++ không tạo chuỗi ký tự const. Lý do là lịch sử. Từ khóa const không tồn tại trước khi nó được giới thiệu bởi tiêu chuẩn ANSI C 1989 (mặc dù nó có thể được thực hiện bởi một số trình biên dịch trước đó). Vì vậy, một chương trình pre-ANSI có thể trông như thế này:

#include <stdio.h> 

print_string(s) 
char *s; 
{ 
    printf("%s\n", s); 
} 

main() 
{ 
    print_string("Hello, world"); 
} 

Không có cách nào để thực thi thực tế là print_string không được phép sửa đổi các chuỗi được trỏ đến bởi s. Tạo chuỗi ký tự const trong ANSI C có thể đã phá vỡ mã hiện có, mà ủy ban ANSI C đã cố gắng rất nhiều để tránh làm. Đã không có một cơ hội tốt kể từ đó để thực hiện một sự thay đổi như vậy đối với ngôn ngữ. (Các nhà thiết kế của C++, chủ yếu là Bjarne Stroustrup, không quan tâm đến khả năng tương thích ngược với C.)

+0

giải thích tuyệt vời !!! – ademar111190

+1

Liệu người chăm sóc downvoter có giải thích được không? –

2

Như bạn đã nói, bạn không thể sửa đổi chuỗi ký tự, đó là những gì strtok thực hiện. Bạn phải làm

char str[] = "this is a test"; 
tokenize(str); 

Điều này tạo ra các mảng str và khởi nó với this is a test\0, và vượt qua một con trỏ đến nó để tokenize.

0

Tôi chắc chắn bạn sẽ bị đánh đập về điều này ... nhưng "strtok()" vốn đã không an toàn và dễ bị những thứ như vi phạm truy cập.

Ở đây, câu trả lời gần như chắc chắn là sử dụng hằng số chuỗi.

Hãy thử điều này thay vì:

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    char buff[80]; 
    strcpy (buff, "this is a test"); 
    tokenize(buff); 
} 
+1

Nếu bạn đang đi để đưa lên bản chất không an toàn của strtok, chúng tôi cũng có thể nhớ rằng strncpy là an toàn hơn nhiều so với strcpy. Mặc dù strcpy là hoàn toàn an toàn cho một chuỗi liên tục biên dịch thời gian, một tái cấu trúc sau này có thể biến cuộc gọi strcpy thành một lỗ hổng tràn bộ đệm. –

1

Strok đổi đối số đầu tiên của nó để tokenize nó. Do đó bạn không thể vượt qua nó một chuỗi chữ, vì nó thuộc loại const char * và không thể sửa đổi, do đó hành vi không xác định. Bạn phải sao chép chuỗi ký tự thành một mảng char có thể được sửa đổi.

2

Có một lý do rất tốt là cố gắng mã hóa chuỗi liên tục biên dịch sẽ gây ra lỗi phân đoạn: chuỗi không đổi nằm trong bộ nhớ chỉ đọc.

Trình biên dịch C sẽ tạo chuỗi liên tục biên dịch thành tệp thực thi và hệ điều hành tải chúng vào bộ nhớ chỉ đọc (.rodata trong tệp * nix ELF). Vì bộ nhớ này được đánh dấu là chỉ đọc, và kể từ khi strtok ghi vào chuỗi mà bạn truyền vào nó, bạn nhận được một lỗi phân đoạn để ghi vào bộ nhớ chỉ đọc.

1

Bạn đang cố gắng thực hiện điều gì ở số "... của bạn là một mảng số char" nhận xét?

Thực tế là "this is a test" là nội bộ một mảng char không thay đổi gì cả. Nó vẫn là một chuỗi chữ (tất cả các chuỗi ký tự là các mảng không thể sửa đổi của char). strtok của bạn vẫn cố gắng mã hóa một chuỗi chữ. Đây là lý do tại sao nó bị treo.

0

Tôi chỉ nhấn lỗi Phân đoạn lỗi khi cố gắng sử dụng printf để in mã thông báo (cmd trong trường hợp của bạn) sau nó đã trở thành NULL.

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