2010-05-04 42 views
7

Tôi có file hai C++, nói file1.cpp và file2.cpp nhưkhai Chức năng trong C và C++

//file1.cpp 
#include<cstdio> 
void fun(int i) 
{ 
    printf("%d\n",i); 
} 

//file2.cpp 
void fun(double); 
int main() 
{ 
    fun(5); 
} 

Khi tôi biên dịch chúng và liên kết chúng như C++ file, tôi nhận được một lỗi "không xác định tham chiếu đến vui vẻ (gấp đôi) ".
Nhưng khi tôi làm điều này dưới dạng tệp C, tôi không nhận được lỗi và 0 được in thay vì 5.
Vui lòng giải thích lý do.
Hơn nữa, tôi muốn hỏi liệu chúng ta có cần khai báo một hàm trước khi xác định hàm đó không bởi vì
Tôi chưa khai báo nó trong tệp1.cpp nhưng không có lỗi nào trong quá trình biên dịch.

+2

Tắt chủ đề: nếu bất kỳ câu hỏi nào trước đây của bạn được trả lời thỏa mãn, bạn nên quay lại và chấp nhận các câu trả lời đã nói. Chỉ để được tốt đẹp. – outis

+0

Chỉ cần thêm vào @outis: làm như vậy sẽ làm tăng cơ hội ai đó trả lời câu hỏi của bạn. – ereOn

Trả lời

6

này rất có thể là do chức năng quá tải. Khi biên dịch với C, lệnh gọi tới fun(double) được dịch sang một cuộc gọi đến hàm assembly _fun, sẽ được liên kết ở giai đoạn sau. Định nghĩa thực tế cũng có tên assembly _fun, mặc dù nó lấy một int thay vì một đôi, và mối liên kết sẽ vui vẻ sử dụng điều này khi gọi fun(double).

C++ mặt khác mangles tên lắp ráp, vì vậy bạn sẽ nhận được một cái gì đó như [email protected] cho fun(int)[email protected] cho fun(double), để quá tải hoạt động. Người liên kết sẽ thấy những tên này có tên khác và phát sinh lỗi mà không thể tìm thấy định nghĩa cho fun(double).

Đối với câu hỏi thứ hai, bạn nên khai báo các nguyên mẫu hàm, thường được thực hiện trong tiêu đề, đặc biệt nếu hàm được sử dụng trong nhiều tệp. Nên có một tùy chọn cảnh báo cho thiếu nguyên mẫu trong trình biên dịch của bạn, gcc sử dụng -Wmissing-prototypes.Mã của bạn sẽ tốt hơn nếu được thiết lập như

// file1.hpp 
#ifndef FILE1_HPP 
#define FILE1_HPP 
void fun(int) 
#endif 

// file1.c 
#include "file1.hpp" 
... 

// file2.c 
#include "file1.hpp" 
int main() 
{ 
    fun(5); 
} 

Sau đó, bạn sẽ không có nhiều nguyên mẫu xung đột trong chương trình của mình.

+0

Nhưng tại sao nó in 0 thay vì 5? –

+0

@Hap Bởi vì nó giải thích các bit đại diện cho 5.0 là một số nguyên, trong đó có một kết quả không xác định –

+3

Biểu diễn hex của IEEE-754 double 5.0 sẽ là 0x4014000000000000. Nếu hệ thống của bạn là nhỏ như x86, đọc bốn byte đầu tiên của ngăn xếp đó sẽ là 0, sau đó được in. –

-3
#include <stdio.h> 

int Sum(int j, int f) 
{ 
    int k; 
    k = j + f; 
    return k; 
} 
main() 
{ 
    int i=3; 
    int j = 6; 

    int k = sum(i,j); 

    printf("%d",k); 
} 

Output là 9

+6

Tôi hoàn toàn bị mất như những gì này đã làm với câu hỏi. – MBennett

+1

Chương trình của bạn không biên dịch, hãy để một mình trả lời câu hỏi. – avakar

+0

Bạn trả lời có bất kỳ liên kết nào đến câu hỏi được hỏi không? – Ashish

5

Điều này là do C++ cho phép bạn quá tải chức năng và C thì không. Nó có giá trị để có điều này trong C++:

double fun(int i); 
double fun(double i); 
... 
double fun(int i) { return 1;} 
double fun(double i) { return 2.1; } 

nhưng không phải trong C.

Lý do bạn không thể biên dịch nó với C++ biên dịch là vì C++ biên dịch thấy việc kê khai như đôi và cố gắng tìm một định nghĩa cho nó. Với trình biên dịch C, bạn sẽ nhận được một lỗi cho điều này là tốt, tôi sẽ nghĩ rằng bạn đã không nhập mã chính xác như bạn đã nói bạn đã làm khi thử nghiệm này với trình biên dịch C.

Điểm chính: C++ có quá tải hàm, C thì không.

+0

Tôi đã biên soạn và liên kết hai tệp với trình biên dịch C là gcc -c file1.c gcc -c tệp2.c gcc -o prog file2.o file1.o Nó không hiển thị bất kỳ lỗi và kết quả nào là 0 . –

+0

Mã C thực sự không hợp lệ theo tiêu chuẩn, tuy nhiên, trên thực tế, tên biểu tượng chỉ chứa tên của hàm, chứ không phải toàn bộ chữ ký, làm cho lỗi ẩn với trình liên kết. – avakar

+0

khi bạn biên dịch với gcc, hãy xem điều gì sẽ xảy ra nếu bạn thêm các công tắc này -Wall -Werror –

1

C++ (một con thú tàn bạo, bạn sẽ đồng ý) thích xé tên các chức năng. Như vậy, trong tập tin của bạn tiêu đề cho phần C: ở đầu trang:

#ifdef __cplusplus 
extern "C" {` 
#endif 

ở phía dưới:

#ifdef __cplusplus 
} 
#endif 

này sẽ thuyết phục nó không để mangle một số tên. Nhìn here

OR, trong cpp của bạn, bạn có thể nói

extern "C" void fun(double); 
1

Việc lưu giữ ngôn ngữ C là nó cho phép các hàm được gọi mà không yêu cầu khai báo hiển thị trong bản dịch - nó chỉ giả định rằng các đối số của các hàm đó là tất cả int. Trong ví dụ của bạn, C++ cho phép quá tải và không hỗ trợ khai báo hàm ngầm định - trình biên dịch sử dụng hàm fun (double) và trình liên kết không thành công vì hàm fun (double) không bao giờ được triển khai. vui vẻ (int) có một chữ ký khác (trong C++), và tồn tại như một biểu tượng duy nhất, trong khi trình biên dịch C (hoặc trình liên kết, tùy thuộc vào khả năng hiển thị) sẽ tạo ra lỗi khi bạn khai báo cả vui (int) và vui (double) như Ký hiệu C.

Đó chỉ là cách ngôn ngữ phát triển qua nhiều năm (hoặc không). Trình biên dịch của bạn có thể có một cảnh báo cho vấn đề này (khai báo hàm ngầm).

Bạn sẽ thấy các kết quả khác nhau khi bạn khai báo hàm dưới dạng hàm C (chúng được khai báo là hàm C++ trong ví dụ của bạn khi được biên dịch dưới dạng tệp nguồn C++).

C++ yêu cầu hàm được khai báo trước khi nó được sử dụng, C không (trừ khi bạn yêu cầu trình biên dịch cảnh báo bạn về vấn đề này).

0

Khi được biên dịch thành C++, bạn được phép có hai hàm có cùng tên (miễn là chúng có các tham số khác nhau). Trong C++ tên mangling được sử dụng để mối liên kết có thể phân biệt hai:

fun_int(int x); 
fun_double(double x); 

Khi được biên dịch trong C chỉ có một hàm có tên cụ thể.
Khi bạn biên dịch tập tin1.c nó tạo ra một hàm đọc một số nguyên từ ngăn xếp và in nó.

Khi bạn biên dịch tệp2.c, bạn thấy rằng sự thú vị() mất gấp đôi. Vì vậy, nó chuyển đổi các tham số đầu vào thành một cú đúp đẩy nó vào ngăn xếp sau đó chèn một cuộc gọi đến fun() vào mã. Vì hàm này nằm trong một đơn vị biên dịch khác, địa chỉ thực tế không được giải quyết ở đây mà chỉ khi trình liên kết được gọi. Khi trình liên kết được gọi, nó sẽ thấy một cuộc gọi đến giải trí cần phải được giải quyết và chèn địa chỉ chính xác, nhưng nó không có thông tin kiểu để xác thực cuộc gọi.

Khi thời gian chạy 5 giờ được chuyển đổi thành đôi và được đẩy lên ngăn xếp. Sau đó, fun() được gọi. fun() đọc một số nguyên từ ngăn xếp và sau đó in nó. Bởi vì một đôi có một bố trí khác nhau để một số nguyên những gì sẽ được in sẽ được thực hiện xác định và phụ thuộc vào cách cả hai đôi và int được bố trí trong bộ nhớ.