2010-06-15 34 views
6

Tôi in giá trị mà tôi trả về ngay trước câu lệnh return và yêu cầu mã của tôi in giá trị đã được trả về ngay sau cuộc gọi hàm. Tuy nhiên, tôi nhận được một lỗi phân đoạn sau bản in đầu tiên của tôi và trước thứ hai của tôi (cũng thú vị để lưu ý, điều này luôn xảy ra vào lần thứ ba hàm được gọi, không bao giờ là thứ nhất hoặc thứ hai, không bao giờ thứ tư hoặc sau này). Tôi đã thử in tất cả dữ liệu mà tôi đang làm việc để xem liệu phần còn lại của mã của tôi có đang hoạt động không, nhưng dữ liệu của tôi cho đến thời điểm đó có vẻ ổn. Dưới đây là các chức năng:Lỗi phân đoạn C trước/trong quá trình trả về

int findHydrogen(struct Amino* amino, int nPos, float* diff, int totRead) { 

    struct Atom* atoms; 
    int* bonds; 
    int numBonds; 
    int i; 
    int retVal; 
    int numAtoms; 

    numAtoms = (*amino).numAtoms; 

    atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); 
    atoms = (*amino).atoms; 

    numBonds = atoms[nPos].numBonds; 

    bonds = (int *) malloc(sizeof(int) * numBonds); 
    bonds = atoms[nPos].bonds; 

    for(i = 0; i < (*amino).numAtoms; i++) 
     printf("ATOM\t\t%d %s\t0001\t%f\t%f\t%f\n", i + 1, atoms[i].type, atoms[i].x, atoms[i].y, atoms[i].z); 

    for(i = 0; i < numBonds; i++) 
     if(atoms[bonds[i] - totRead].type[0] == 'H') { 
      diff[0] = atoms[bonds[i] - totRead].x - atoms[nPos].x; 
      diff[1] = atoms[bonds[i] - totRead].y - atoms[nPos].y; 
      diff[2] = atoms[bonds[i] - totRead].z - atoms[nPos].z; 

      retVal = bonds[i] - totRead; 

      bonds = (int *) malloc(sizeof(int)); 
      free(bonds); 

      atoms = (struct Atom *) malloc(sizeof(struct Atom)); 
      free(atoms); 

      printf("2 %d\n", retVal); 

      return retVal; 
     } 
} 

Như tôi đã đề cập trước đó, nó hoạt động tốt hai lần đầu tiên tôi chạy nó, lần thứ ba nó in đúng giá trị của retval, sau đó lỗi seg ở đâu đó trước khi nó được đến nơi mà tôi gọi là chức năng, mà tôi làm như sau:

hPos = findHydrogen((&aminoAcid[i]), nPos, diff, totRead); 
printf("%d\n", hPos); 
+1

điều gì sẽ xảy ra nếu tôi == numBonds và không tìm thấy 'H'? –

+7

bạn đã bị rò rỉ bộ nhớ trong mã này. –

+0

Ban đầu tôi đã nói với nó chỉ cần thoát khỏi mã (với lệnh thoát (7);) nếu không tìm thấy 'H' vì điều đó có nghĩa là tôi đã làm gì đó sai với dữ liệu của mình ở nơi khác, nhưng tôi chưa bao giờ vấn đề, tôi loại bỏ dòng đó. – wolfPack88

Trả lời

8

Nó không phải dễ dàng để đoán xem lỗi ở đâu từ mã này (có một tiềm năng cho các lỗi trong chỉ là về tất cả các dòng mã ở đây) - có khả năng bạn có một bộ đệm tràn ngập ở đâu đó, tuy nhiên nếu bạn đang ở trên * nix, hãy chạy chương trình của bạn dưới valgrind, bạn sẽ có thể tìm thấy lỗi khá nhanh.

Những dòng này trông kỳ lạ mặc dù:

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); 
atoms = (*amino).atoms; 

Bạn đang rò rỉ bộ nhớ, khi bạn loại bỏ các con trỏ trả về bởi malloc. Cùng một điều với bonds, và điều tương tự trên một lần nữa trong vòng lặp của bạn.

+3

"có tiềm năng cho lỗi trong chỉ là về mọi dòng mã ở đây "- khắc nghiệt. đúng, nhưng khắc nghiệt. :-) –

10

Lỗi phân đoạn khi trở lại thường là dấu hiệu của một ngăn xếp bị xé.

+0

Tôi không hoàn toàn chắc chắn những gì sẽ gây ra điều này ... Bạn có thể cho tôi một ví dụ về ý của bạn để tôi có thể tìm kiếm nó trong mã của riêng tôi? Cảm ơn bạn đã phản hồi theo cách này. – wolfPack88

+2

Nói chung bạn sẽ viết qua ranh giới của một số biến trên ngăn xếp. Các chuỗi là khét tiếng cho điều này, nhưng nó có thể là một kiểu mảng khác, hoặc một kiểu sao chép cấu trúc hoặc kiểu memcpy(). –

+4

-1 cho một câu trả lời chung, tay lượn sóng. –

3

Có vẻ như bạn đang sử dụng báo cáo in lỗi debug Phân khúc: một không lớn không có trong C.

Vấn đề là stdout được đệm trên hầu hết các hệ thống, có nghĩa là các lỗi phân khúc thực sự xảy ra muộn hơn bạn nghĩ. Không thể xác định một cách đáng tin cậy khi chương trình của bạn được phân đoạn bằng cách sử dụng printf.

Thay vào đó, bạn nên sử dụng trình gỡ lỗi như gdb, sẽ cho bạn biết chính xác dòng mã đang gây ra lỗi phân đoạn.

Nếu bạn không biết làm thế nào để sử dụng gdb, đây là một hướng dẫn nhanh chóng tôi tìm thấy bởi Google'ing: http://www.cs.cmu.edu/~gilpin/tutorial/

+0

Bạn thường có thể gỡ lỗi bằng cách in nếu bạn nhớ kết thúc việc in bằng \ n hoặc thực hiện lệnh flush() sau khi gỡ lỗi. Tôi biết đó không phải là cách tốt nhất để làm điều đó, nhưng trong một số trường hợp, nó phù hợp. – Shaded

+2

In để 'stderr' hoạt động tốt hơn (sử dụng' fprintf (stderr, ...); 'thay vì' printf (...); '), vì đó thực sự là cái' stderr' là gì. Dĩ nhiên, trình gỡ lỗi thực sự tốt hơn. –

+0

@ David cả hai đều có sử dụng ... nếu bạn cần phải xem một chuỗi dài của những thứ giải nén thông tin từ một trình gỡ lỗi có thể gây đau đớn (thêm đặc biệt đau đớn nếu nó là một gui ...) – Spudd86

4

Có rất nhiều điều sai ở đây.

Điều đầu tiên tôi nhận thấy là bạn đang rò rỉ bộ nhớ (bạn cấp phát một số bộ nhớ tại (struct Atom *) malloc(sizeof(struct Atom) * numAtoms), sau đó ghi đè con trỏ bằng con trỏ trong cấu trúc amino); bạn làm điều tương tự với (int *) malloc(sizeof(int) * numBonds);.

Thứ hai, bạn không kiểm tra biểu thức bonds[i] - totRead. Thứ ba, và tôi nghĩ đây là nơi bạn đang gặp sự cố, bạn ghi đè lên con trỏ nguyên tử của mình tại đây: atoms = (struct Atom *) malloc(sizeof(struct Atom)); free(atoms); để nguyên tử trỏ đến bộ nhớ không hợp lệ.

3

Đây là số lẻ:

bonds = (int *) malloc(sizeof(int)); 
free(bonds); 

atoms = (struct Atom *) malloc(sizeof(struct Atom)); 
free(atoms); 

Bạn phân bổ bộ nhớ và sau đó bạn giải phóng nó ngay sau đó và để lại con trỏ của bạn trỏ đến bộ nhớ unallocated.

Dòng này cũng có vẻ nguy hiểm:

atoms[bonds[i] - totRead].type[0] == 'H' 

Hãy chắc chắn rằng bạn ở lại bên trong mảng với chỉ số của bạn.

+0

Vâng, con trỏ hiện trỏ đến dữ liệu đang được sử dụng trong một mảng khác. Tôi không muốn có thêm một con trỏ trỏ đến cùng một dữ liệu, vì vậy tôi có nó trỏ đến cái gì khác, và sau đó giải phóng nó để tránh rò rỉ bộ nhớ. Vì tôi là một lập trình viên tương đối mới, tôi không chắc đây là thực hành tốt, nhưng nó có vẻ như là một ý tưởng hay vào thời điểm đó. – wolfPack88

+0

Lucas có điểm chính xác.Nó có thể (gần như *) không bao giờ đúng để thực hiện một malloc(), và giải phóng cùng một bộ nhớ trong câu lệnh ngay lập tức sau đây. Từ đọc mã của bạn rõ ràng là bạn nên được tự do() - ing bộ nhớ của bạn trước khi tái sử dụng con trỏ cho bộ nhớ mới được phân bổ. –

+1

@ wolfPack88: Tôi không chắc tôi hiểu bạn. Nếu bạn không muốn con trỏ trỏ vào một số bộ nhớ khác, bạn nên trỏ nó vào 'NULL', nhưng tôi không nghĩ đó là những gì bạn muốn ở đây. – Lucas

5

EDIT Vâng, bạn đang rò rỉ bộ nhớ trái và phải, nhưng không hoàn toàn theo cách tôi nghĩ. chuỗi cố định dưới đây:

Cụ thể, khi bạn làm:

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); // 1 
atoms = (*amino).atoms; // 2 
// ... 
atoms = (struct Atom *) malloc(sizeof(struct Atom)); // 3 
free(atoms); // 4 

gì đang xảy ra là bạn đang phân bổ một số bộ nhớ và đưa địa chỉ trong atoms trong bước (1). Sau đó, bạn quăng đi địa chỉ đó và thay vào đó chỉ cần atoms ở một phần bên trong cấu trúc amino của bạn trong (2). Sau đó, bạn phân bổ một con trỏ giây thứ hai với một nguyên tử duy nhất. Cuối cùng, bạn gọi free về điều đó. Bạn đối xử với bonds theo cùng một cách. Bạn có thể có nghĩa là một cái gì đó như thế này:

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); // 1 
memcpy(atoms, (*amino).atoms, sizeof(struct Atom) * numAtoms); // 2 
// ... 
// delete 3 
free(atoms); // 4 

Lưu ý rằng nếu một Atom có ​​bất kỳ thành phần con trỏ bạn có thể muốn làm một vòng lặp for và sao chép các nguyên tử riêng biệt cùng với nội dung của họ, sau đó bạn sẽ phải riêng free tại điểm trả lại.

... hoặc có thể chỉ này nếu bạn chỉ muốn đọc các nguyên tử dữ liệu từ cấu trúc:

atoms = (*amino).atoms; // delete 1, 3, 4 entirely and just read directly from the structure 

câu trả lời khác nói về khoảng không gian trong diff và các vấn đề khác có lẽ cũng đáng được nghiên cứu .

EDIT: cố định chuỗi cuộc gọi để khớp với mẫu mã.

+2

Tôi không nghĩ rằng các cuộc gọi 'free()' là vấn đề (mặc dù chúng gây nhầm lẫn cho vấn đề này). Lệnh 'free()' được thực hiện sau khi 'atom' được đặt thành kết quả của' malloc() 'khác, vì vậy nó giải phóng bộ nhớ được trả về từ một cuộc gọi' malloc() '. Tuy nhiên, tất cả các lời gọi tới 'malloc()' và 'free()' trong hàm này dường như là vô nghĩa. –

+0

Tôi nghĩ bạn nói đúng. Tôi đã chỉnh sửa bài đăng của mình để cho biết rằng bộ nhớ bị rò rỉ mà không tuyên bố rằng các cuộc gọi miễn phí là vấn đề thực sự. –

4

Dưới đây là một viết lại nhỏ bộ phận của mã của bạn để chứng minh rò rỉ bộ nhớ:

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); // allocating new struct 
atoms = (*amino).atoms; // overriding the pointer with pointer to a struct allocated in the caller 
//... 
for (some counter on atoms) 
{ 
    if (something that succeeds) 
    { 
     atoms = (struct Atom *) malloc(sizeof(struct Atom)); // overwrite the pointer yet again with newly allocated struct 
     free(atoms); // free the last allocated struct 
     // at this point atoms points to invalid memory, so on the next iteration of the outer for it'll crash 
    } 
} 

Ngoài ra còn có cơ hội mà các tuyên bố bonds[i] - totRead có thể ra khỏi atoms[] giới hạn, mà có thể là segfault.

+0

nhưng vòng lặp sẽ không chạy lại vì nó trả về bên trong nếu ... – Spudd86

1

Trong trường hợp bạn đã viết:

/* Allocate new space for a copy of the input parameter "Atoms" */ 
atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); 
/* Immediately lose track of the pointer to that space, once was stored 
    in atoms, now being lost. */ 
atoms = (*amino).atoms; 

Tôi nghĩ rằng ý định của bạn phải được điều này:

/* Allocate new space for a copy of the input parameter "Atoms" */ 
atoms = (struct Atom *)malloc(sizeof(struct Atom) * numAtoms); 

/* Copy the input parameter into the newly-allocated memory. */ 
for (i = 0; i < numAtoms; i++) 
    atoms[i] = (*amino).atoms[i]; 

mà cũng có thể được viết như sau:

/* Allocate new space for a copy of the input parameter "Atoms" */ 
atoms = (struct Atom *)malloc(sizeof(struct Atom) * numAtoms); 

/* Copy the input parameter into the newly-allocated memory. */ 
memcpy(atoms, (*amino).atoms, sizeof(struct Atom) * numAtoms); 

Trong C không có được xây dựng trong bằng (=) nhà điều hành để sao chép mảng như bạn có vẻ đã dự định. Những gì bạn có thay vì mất theo dõi của con trỏ để phân bổ bộ nhớ, trước đây được lưu trữ trong biến atoms, và sau đó tiến hành để bắt đầu lặp đầu tiên của vòng lặp của bạn với atoms trỏ vào "bản sao đầu vào" của mảng nguyên tử.


Một phần của sự cố là bạn đang gọi số free trên bộ nhớ, nhưng sau đó bạn tiếp tục truy cập con trỏ tới bộ nhớ được giải phóng này. Bạn không được truy cập con trỏ để giải phóng bộ nhớ.Để tránh điều này, hãy thay thế tất cả các cuộc gọi của bạn thành miễn phí với:

#ifdef free 
# undef free 
#endif 
#define free(f) freeptr(&f) 

void freeptr(void **f) 
{ 
    /* This function intentionally segfaults if passed f==NULL, to alert 
     the programmer that an error has been made. Do not wrap this code 
     with a check "if (f==NULL)", fix the problem where it is called. 

     To pass (*f==NULL) is a harmless 'no-op' as per the C standard 
     free() function. 

     If you must access the original, built-in free(), use (free)() to 
     bypass the #define macro replacement of free(). 

    */ 

    (free)(*f); /* free() must accept NULL happilly; this is safe. */ 
    *f = NULL; /* Set the pointer to NULL, it cannot be used again. */ 
} 

Hiện tại bạn có thể chỉ cần cắt và dán mã ở trên ở đâu đó ở đầu chương trình của mình. Một địa điểm tốt là sau chỉ thị tối hậu #include, nhưng nó phải xảy ra ở phạm vi cấp tệp và trước khi bạn sử dụng lần đầu tiên free() trong mã.

Sau khi biên dịch lại mã của bạn, bạn sẽ tìm thấy Lỗi vi phạm và Lỗi vi phạm phân đoạn ngay lập tức sau khi bạn free(atom). Điều này là chính xác và mục đích của freeptr() là dẫn mã của bạn đến một sự cố tức thì thay vì tình huống hiện tại nơi mã của bạn lạm dụng con trỏ và dẫn đến các sự cố rất khó khăn để bạn gỡ lỗi.

Để cuối cùng sửa cấp phát bộ nhớ của bạn, chắc chắn transpose các dòng:

bonds = (int *) malloc(sizeof(int)); 
free(bonds); 

mà nên đọc:

free(bonds); 
bonds = (int *) malloc(sizeof(int)); 

Bạn dùng tham số diff như thể bạn đang đi qua trong một mảng ít nhất ba (3) yếu tố. Bạn nên xác minh rằng người gọi đang chuyển đủ bộ nhớ.


Khi phân bổ bonds, bạn phải phân bổ bộ nhớ cho không một (1) số nguyên, nhưng như nhiều số nguyên như numBonds:

free(bonds); 
bonds = (int *) malloc(sizeof(int) * numBonds); 

hay, tốt cho hầu hết các lập trình viên C:

free(bonds); 
/* The calloc function performs the multiplication internally, and 
    nicely zero-fills the allocated memory. */ 
bonds = calloc(numBonds, sizeof(int)); 

Bạn cần sửa lại phân bổ atoms để phân bổ một số số chính xác. Hiện tại, bạn cũng chỉ phân bổ một phần tử bộ nhớ duy nhất có kích thước sizeof(struct Atom). Một mảng các phần tử như vậy yêu cầu bạn nhân kích thước của một phần tử với số phần tử.

Chức năng calloc() là tốt vì nó phân bổ mảng cho bạn và khởi tạo nội dung của tất cả các phần tử thành 0. malloc() không có gì để khởi tạo bộ nhớ trả về và có thể dẫn đến các giá trị không thể đoán trước được lan truyền trong chương trình của bạn. Nếu bạn sử dụng malloc() thay vì calloc(), bạn phải cẩn thận để khởi tạo các phần tử mảng. Ngay cả khi sử dụng calloc(), bạn phải khởi tạo mọi phần tử khác 0.


Lưu ý rằng tôi đã xóa bỏ diễn viên khỏi giá trị trả về malloc. Nếu bạn đang viết mã C, bạn nên biên dịch nó thành mã C. Trình biên dịch sẽ không phàn nàn về việc thiếu một diễn viên từ void * trừ khi bạn đang biên dịch trong một chế độ C++. Tệp nguồn C phải kết thúc bằng .c đuôi tệp, không phải là .cpp.


Như Walter Mundt chỉ ra, bạn đang vô tình gọi free() về thành viên của một trong các thông số đầu vào của bạn, mà bạn đã gán cho con trỏ atoms. Bạn sẽ phải tự sửa lỗi này; trên freeptr() sẽ không làm nổi bật lỗi này cho bạn.


Những người khác đã viết rằng bạn không thể sử dụng printf() để phát hiện đáng tin cậy nơi chương trình của bạn đang gặp sự cố. Đầu ra từ printf() được đệm và hình dáng của nó bị trễ.

Đặt cược tốt nhất của bạn là sử dụng gdb để xác định dòng chính xác mà chương trình của bạn gặp sự cố. Bạn sẽ không phải học bất kỳ lệnh gdb nào để thực hiện việc này nếu bạn biên dịch mã để gỡ lỗi.

Thiếu rằng, thay thế:

printf("Program ran to point A.\n"); 

với:

fprintf(stderr, "Program ran to point A.\nPress return.\n"); 
fflush(stderr); /* Force the output */ 
fflush(stdin); /* Discard previously-typed keyboard input */ 
fgetc(stdin); /* Await new input */ 
fflush(stdin); /* Discard unprocessed input */ 

Nhìn chung, đề nghị của tôi là bạn không sử dụng ngôn ngữ C trong thời gian tới. Máy vi tính nhanh đến nỗi những ngày này tôi sẽ hỏi tại sao bạn lại coi C ở nơi đầu tiên.

Đừng làm cho tôi sai; Tôi yêu ngôn ngữ C. Nhưng C không dành cho mọi thứ. C là điều tuyệt vời cho các hệ điều hành, các hệ thống nhúng, tính toán hiệu năng cao, và đối với các trường hợp khác, nơi trở ngại chính cho sự thành công là thiếu truy cập cấp thấp đối với máy tính tính toán.

Trong trường hợp của bạn, bạn có vẻ là một nhà khoa học hoặc kỹ sư. Tôi khuyên bạn nên học và sử dụng Python. Python cho vay chính nó để dễ dàng đọc, dễ dàng xác minh các chương trình mà bạn có thể chia sẻ với các nhà hóa học hoặc kỹ sư của bạn. C không cho vay chính nó để nhanh chóng viết mã mạnh mẽ như Python. Trong trường hợp tương lai không chắc rằng Python không đủ nhanh cho các mục đích của bạn, có những giải pháp khác mà bạn sẽ sẵn sàng cho.

+2

không có sự tự do nào được gọi với bộ nhớ được phân bổ trên dòng trước đó, vấn đề của OP là chúng không thực sự biết cách C hoạt động – Spudd86

+0

@ spudd86 đó là chính xác những gì tôi đã viết, làm thế nào bạn bối rối? Vấn đề là họ sau đó ghi vào bộ nhớ được trỏ đến bởi cùng một con trỏ mà họ vừa giải phóng. –

+1

không, chúng trở lại ngay sau khi giải phóng nên không có tham chiếu lơ lửng (nó giống như nguyên tử này = nguyên tử; nguyên tử = amino-> nguyên tử; nguyên tử = malloc; tự do (nguyên tử); không giải phóng bất cứ thứ gì không được miễn phí – Spudd86

1

Bạn có một tệp #include <stdio.h> trong tệp không? Tôi tự hỏi liệu bạn có nhận được cuộc gọi đến số printf() đang sử dụng tuyên bố ngầm của printf() và do đó có thể đang sử dụng quy ước gọi sai.

Trình biên dịch/nền tảng bạn đang sử dụng là gì? Bạn có nhận được bất kỳ cảnh báo nào từ bản dựng không?

3

EDIT: đi đọc Accessing array values via pointer arithmetic vs. subscripting in C này nó sẽ giúp bạn hiểu những gì con trỏ và mảng là

Những dòng này không thực sự có bất kỳ tác dụng net thực về những gì mã lệnh thực hiện để bạn có thể loại bỏ chúng

bonds = (int *) malloc(sizeof(int)); 
free(bonds); 

atoms = (struct Atom *) malloc(sizeof(struct Atom)); 
free(atoms); 

Các dòng malloc ở đây vô dụng và dẫn đến rò rỉ vì bạn gán con trỏ từ cấu trúc amino cho các nguyên tử và trái phiếu ngay sau khi thực hiện nó.

numAtoms = (*amino).numAtoms; 

atoms = (struct Atom *) malloc(sizeof(struct Atom) * numAtoms); 
atoms = (*amino).atoms; 

numBonds = atoms[nPos].numBonds; 

bonds = (int *) malloc(sizeof(int) * numBonds); 
bonds = atoms[nPos].bonds; 

Bạn nên dừng mã hóa cho một chút và chắc chắn rằng bạn hiểu con trỏ trước khi bạn làm nhiều thứ khác nữa bởi vì bạn làm rõ ràng không và nó chỉ sẽ gây cho bạn rất nhiều và rất nhiều đau đớn cho đến khi bạn làm, tuy nhiên đây là một phiên bản mã của bạn nên làm điều gì đó giống như những gì bạn muốn:

int findHydrogen(struct Amino* amino, int nPos, float* diff, int totRead) { 

    struct Atom* atoms; 
    int* bonds; 
    int numBonds; 
    int i; 
    int retVal; 
    int numAtoms = amino->numAtoms; 

    numAtoms = amino->numAtoms; 
    atoms = amino->atoms; 

    numBonds = atoms[nPos].numBonds; 
    bonds = atoms[nPos].bonds; 

    for(i = 0; i < amino->numAtoms; i++) 
     printf("ATOM\t\t%d %s\t0001\t%f\t%f\t%f\n", i + 1, atoms[i].type, atoms[i].x, atoms[i].y, atoms[i].z); 

    for(i = 0; i < numBonds; i++) 
     if(atoms[bonds[i] - totRead].type[0] == 'H') { 
      diff[0] = atoms[bonds[i] - totRead].x - atoms[nPos].x; 
      diff[1] = atoms[bonds[i] - totRead].y - atoms[nPos].y; 
      diff[2] = atoms[bonds[i] - totRead].z - atoms[nPos].z; 

      retVal = bonds[i] - totRead; 

      printf("2 %d\n", retVal); 

      return retVal; 
     } 
}