2012-02-27 38 views
14
#include<stdio.h> 

int main() 
{ 
    char *name = "Vikram"; 
    printf("%s",name); 
    name[1]='s'; 
    printf("%s",name); 
    return 0; 
} 

Không có đầu ra được in trên thiết bị đầu cuối và chỉ nhận được lỗi phân đoạn. Nhưng khi tôi chạy nó trong GDB, tôi nhận được sau đây -Thực hiện printf() và Phân đoạn lỗi

Program received signal SIGSEGV, Segmentation fault. 
0x0000000000400525 in main() at seg2.c:7 
7  name[1]='s'; 
(gdb) 

Chương trình này có nghĩa là nhận lỗi SEG trên dòng 7 (rõ ràng là tôi không thể ghi trên mảng char không đổi). Vậy tại sao printf() của dòng số 6 không được thực thi?

+0

Tôi không chắc chắn lắm. Nó hoạt động như mong đợi trên mac của tôi chạy OSX Lion (tuân thủ LLVM, gỡ rối với LLDB). –

Trả lời

30

Điều này là do bộ đệm luồng là stdout. Trừ khi bạn làm fflush(stdout) hoặc bạn in một dòng mới "\n" đầu ra có thể được đệm.

Trong trường hợp này, nó phân đoạn trước khi bộ đệm bị xóa và in.

Bạn có thể thử này để thay thế:

printf("%s",name); 
fflush(stdout);  // Flush the stream. 
name[1]='s';   // Segfault here (undefined behavior) 

hay:

printf("%s\n",name); // Flush the stream with '\n' 
name[1]='s';   // Segfault here (undefined behavior) 
+6

Lưu ý rằng 'fflush' thực sự là một cách phù hợp để làm điều đó - một dòng mới không được đảm bảo kích hoạt sự tuôn ra (và trước đây tôi bị cắn bởi hành vi đó). –

4

Lý do bạn đang nhận được một lỗi segmentation là xâu C được đọc chỉ theo tiêu chuẩn C, và bạn đang cố viết 's' qua phần tử thứ hai của mảng chữ "Vikram".

Lý do bạn không nhận được kết quả đầu ra là vì chương trình của bạn đang lưu vào bộ đệm và bị treo trước khi có cơ hội xóa bộ đệm của nó. Mục đích của thư viện stdio, ngoài việc cung cấp các chức năng định dạng thân thiện như printf (3), là giảm chi phí của các hoạt động i/o bằng cách đệm dữ liệu trong bộ đệm trong bộ nhớ và chỉ xuất ra khi cần thiết, và chỉ thực hiện đầu vào thay vì liên tục. Thực tế đầu vào và đầu ra sẽ không, trong trường hợp chung, xảy ra tại thời điểm khi bạn gọi chức năng stdio, nhưng chỉ khi bộ đệm đầu ra là đầy đủ (hoặc bộ đệm đầu vào là sản phẩm nào).

Mọi thứ hơi khác nếu đối tượng FILE đã được đặt để nó tuôn ra liên tục (như stderr), nhưng nói chung, đó là ý chính.

Nếu bạn đang gỡ lỗi, tốt nhất là fprintf để stderr để đảm bảo rằng bản in gỡ lỗi của bạn sẽ bị xóa trước khi xảy ra sự cố.

9

Trước tiên, bạn nên kết thúc printfs bằng "\ n" (hoặc ít nhất là tệp cuối cùng). Nhưng điều đó không liên quan đến segfault.

Khi trình biên dịch biên dịch mã của bạn, nó chia phần nhị phân thành nhiều phần. Một số chỉ đọc, trong khi một số khác có thể ghi. Việc ghi vào phần chỉ đọc có thể gây ra sự phân đoạn. Chuỗi ký tự thường được đặt trong phần chỉ đọc (gcc nên đặt trong ".rodata"). Tên con trỏ trỏ tới phần ro đó. Do đó bạn phải sử dụng

const char *name = "Vikram"; 

Trong phản hồi của tôi, tôi đã sử dụng một vài "có thể" "nên". Hành vi này phụ thuộc vào hệ điều hành, trình biên dịch và cài đặt biên dịch của bạn (Kịch bản trình liên kết xác định các phần).

Thêm

-Wa,-ahlms=myfile.lst 

vào dòng lệnh gcc của tạo ra một tập tin gọi là myfile.lst với mã lắp ráp tạo ra. Ở trên cùng, bạn có thể thấy

.section .rodata 
.LC0: 
    .string "Vikram" 

Điều này cho thấy chuỗi nằm trong Vikram.

Cùng mã sử dụng (Phải ở phạm vi toàn cầu, khác gcc có thể lưu trữ nó trên stack, thấy nó là một mảng và không phải là một con trỏ)

char name[] = "Vikram"; 

sản xuất

.data 
    .type name, @object 
    .size name, 7 
name: 
    .string "Vikram" 

Các cú pháp có một chút khác biệt nhưng xem cách nó nằm trong phần .data bây giờ, đó là đọc-ghi. Bằng cách này ví dụ hoạt động.

+1

Nếu bạn nhận thấy, OP không hỏi lý do tại sao segfault xảy ra, nhưng tại sao chuỗi không được in ra ở nơi đầu tiên. –

+1

mặc dù điều này có thể không chính xác trả lời cho câu hỏi, mẹo và giải thích về .rodata và .data là hữu ích. – vts

0

Theo mặc định khi stdout được kết nối với thiết bị đầu cuối, luồng được xếp theo bộ đệm. Trong thực tế, trong ví dụ của bạn sự vắng mặt của '\n' (hoặc của một dòng tuôn ra rõ ràng) là lý do tại sao bạn không nhận được các ký tự in. Tuy nhiên, trong lý thuyết hành vi không xác định không bị chặn (từ tiêu chuẩn "hành vi [...] mà tiêu chuẩn này không yêu cầu") và segfault có thể xảy ra ngay cả trước khi hành vi không xác định xảy ra, ví dụ trước cuộc gọi printf đầu tiên!

+0

Vì vậy, ... bạn đang nói rằng hành vi này là không xác định rằng nó có thể hành động * ngược thời gian *? –