2016-03-24 12 views
9

Khi tôi chuyển một dấu phẩy phức tạp (complex.h) từ trình gọi C++ sang thư viện ac, giá trị không vượt qua chính xác khi chạy trên một máy điện 32 bit. Tôi đã sử dụng hai thư viện phần mềm mã nguồn mở khác nhau khi phát hiện vấn đề này. Tôi đã cô lập nó xuống đến boundry của khi C + + là đi qua một loại giá trị phức tạp đến một chức năng loại C tinh khiết. Tôi đã viết một số mã đơn giản để chứng minh nó.Số phức được chuyển bởi giá trị từ C++ tới C dường như không hoạt động trên powerpc

#ifndef MMYLIB_3A8726C1_H 
#define MMYLIB_3A8726C1_H 

typedef struct aComplexStructure { 
    float r; 
    float i; 
} myComplex_t; 

#ifdef __cplusplus 
#include <complex> 
extern "C" { 
    void procWithComplex(float a, std::complex<float> *pb, std::complex<float> c, float d); 
    void procWithStruct(float a, myComplex_t *pb, myComplex_t c, float d); 
} 

#else /* __cplusplus */ 

#include <complex.h> 
void procWithComplex(float a, float complex *pb, float complex c, float d); 
void procWithStruct(float a, myComplex_t *pb, myComplex_t c, float d); 

#endif 

#endif /* MYLIB_3A8726C1_H */ 

Các tập tin nguồn C như sau chương trình

#include <stdio.h> 
#include "myLib.h" 

void procWithComplex(float a, complex float * pb, complex float c, float d) 
{ 
    printf("a=%f\n", a); 
    printf("b=%f + %fi\n", creal(*pb), cimag(*pb)); 
    printf("c=%f + %fi\n", creal(c), cimag(c)); 
    printf("d=%f\n", d); 
} 


void procWithStruct(float a, myComplex_t* pb, myComplex_t c, float d) 
{ 
    printf("a=%f\n", a); 
    printf("b=%f + %fi\n", pb->r, pb->i); 
    printf("c=%f + %fi\n", c.r, c.i); 
    printf("d=%f\n", d); 
} 

Việc kêu gọi C++ như sau

#include <iostream> 
#include "myLib.h" 

int main() 
{ 
    float a = 1.2; 
    std::complex<float> b = 3.4 + 3.4I; 
    std::complex<float> c = 5.6 + 5.6I; 
    float d = 9.876; 

    myComplex_t b_s, c_s; 

    b_s.r = b.real(); 
    b_s.i = b.imag(); 

    c_s.r = c.real(); 
    c_s.i = c.imag(); 

    std::cout << "a=" << a << std::endl; 
    std::cout << "b=" << b << std::endl; 
    std::cout << "c=" << c << std::endl; 
    std::cout << "d=" << d << std::endl << std::endl; 

    // c is a 64 bit structure being passed by value. 
    // on my 32 bit embedded powerpc platform, it is being 
    // passed by reference, but the underlying C library is 
    // reading it by value. 
    procWithComplex(a, &b, c, d); 
    std::cout << std::endl; 

    // This is only here to demonstrate that a 64 bit value field 
    // does pass through the C++ to C boundry 
    procWithStruct(a, &b_s, c_s, d); 
    return 0; 
} 

Thông thường tôi mong chờ đầu ra được

a=1.2 
b=(3.4,3.4) 
c=(5.6,5.6) 
d=9.876 

a=1.200000 
b=3.400000 + 3.400000i 
c=5.600000 + 5.600000i 
d=9.876000 

a=1.200000 
b=3.400000 + 3.400000i 
c=5.600000 + 5.600000i 
d=9.876000 

Nhưng khi tôi chạy nguồn trên n máy điện nhúng pc Tôi nhận được đầu ra cho thấy rằng loại giá trị cho phức tạp không được truyền đúng cách.

a=1.2 
b=(3.4,3.4) 
c=(5.6,5.6) 
d=9.876 

a=1.200000 
b=3.400000 + 3.400000i 
c=-0.000000 + 9.876000i 
d=0.000000 

a=1.200000 
b=3.400000 + 3.400000i 
c=5.600000 + 5.600000i 
d=9.876000 

Tôi đã kiểm tra sizeof paramaters từ gdb và từ cả sự kêu gọi và khung chức năng kích thước là 4 byte, 4 byte, 8 byte và 4 byte, cho float, con trỏ float phức tạp, float phức tạp, và nổi.

Tôi nhận ra mình có thể thay đổi tham số giá trị phức tạp thành con trỏ, hoặc cấu trúc của riêng tôi khi chuyển C++ thành c boundry, nhưng tôi muốn biết tại sao tôi không thể chuyển kiểu phức tạp từ C++ sang c một máy tính điện.

Tôi đã tạo một ví dụ khác chỉ trong lần này tôi đã bán một số bản lắp ráp cũng như các giá trị đăng ký.

int x = 22; 
std::complex<float> y = 55 + 88I; 
int z = 77; 
void simpleProc(int x, complex float y, int z) 

Ngay trước khi cuộc gọi nơi các thông số được truyền vào.

x = 22 
y = {_M_value = 55 + 88 * I} 
Looking at raw data *(int*)&y = 1113325568 
z = 77 

này mã lắp ráp nên nơi nó tiết kiệm địa chỉ sự trở lại và tiết kiệm thì các tham số để vượt qua thành thói quen.

x0x10000b78 <main()+824> lwz  r9,40(r31) 
x0x10000b7c <main()+828> stw  r9,72(r31) 
x0x10000b80 <main()+832> lwz  r9,44(r31) 
x0x10000b84 <main()+836> stw  r9,76(r31) 
x0x10000b88 <main()+840> addi r9,r31,72  
x0x10000b8c <main()+844> lwz  r3,16(r31) 
x0x10000b90 <main()+848> mr  r4,r9  
x0x10000b94 <main()+852> lwz  r5,20(r31) 
x0x10000b98 <main()+856> bl  0x10000f88 <simpleProc> 

Nhìn vào lắp ráp ngay sau khi chi nhánh :)

x0x10000f88 <simpleProc>   stwu r1,-48(r1) 
x0x10000f8c <simpleProc+4>  mflr r0  
x0x10000f90 <simpleProc+8>  stw  r0,52(r1) 
x0x10000f94 <simpleProc+12>  stw  r29,36(r1) 
x0x10000f98 <simpleProc+16>  stw  r30,40(r1) 
x0x10000f9c <simpleProc+20>  stw  r31,44(r1) 
x0x10000fa0 <simpleProc+24>  mr  r31,r1  
x0x10000fa4 <simpleProc+28>  stw  r3,8(r31) 
x0x10000fa8 <simpleProc+32>  stw  r5,12(r31) 
x0x10000fac <simpleProc+36>  stw  r6,16(r31) 
x0x10000fb0 <simpleProc+40>  stw  r7,20(r31) 
x0x10000fb4 <simpleProc+44>  lis  r9,4096 

Đây là những giá trị khi chúng tôi là hoàn toàn trong các thói quen (sau khi giá trị biến được giao.

x = 22 
y = 1.07899982e-43 + 0 * I 
z = 265134296 

$r3 = 22 
$r4 = 0x9ffff938 
*(int*)$r4 = 1113325568 
$r5 = 77 

*(int*)(&y) = 77 

My laymans xem là nó trông giống như C + + là đi qua các loại giá trị phức tạp như là một tài liệu tham khảo hoặc loại con trỏ ?, C là xử lý nó như một loại giá trị? Vì vậy, đây là một vấn đề với gcc trên pc điện? Tôi đang sử dụng gcc4.7.1. Tôi a m trong quá trình xây dựng gcc4.9.3 như một trình biên dịch chéo trên một máy khác. Tôi sẽ cập nhật bài đăng này một trong hai cách một khi tôi nhận được đầu ra từ một trình biên dịch mới hơn làm việc.

Có vấn đề với trình biên dịch chéo hoạt động, nhưng nhìn vào kết xuất bộ nhớ của vấn đề ban đầu, nó cho thấy rằng trên nền tảng máy tính điện, giá trị phức tạp không được truyền theo giá trị. Tôi đặt ví dụ về cấu trúc ở đây để cho thấy rằng một giá trị 64 bit có thể được truyền qua giá trị, trên một máy 32 bit.

+3

Đây không phải là vấn đề, nhưng tên bắt đầu bằng dấu gạch dưới theo sau là chữ cái viết hoa ('_MYLIB_H_') và tên có chứa hai dấu gạch dưới liên tiếp được dành riêng cho việc triển khai. Không sử dụng chúng. –

+0

@PeteBecker: Vâng, nó có thể được, ít nhất là trong C nó có thể gọi UB bằng cách thay đổi hành vi của stdlib. – Olaf

+0

Bạn có thể xác minh ABIs (không chắc) và kiểm tra mã Assembler để nhận gợi ý. Nói chung, bạn nên sử dụng các loại tiêu chuẩn. – Olaf

Trả lời

4

Mã của bạn gây ra hành vi không xác định. Trong ++ đơn vị C hàm được khai báo là:

extern "C" void procWithComplex(float a, std::complex<float> *pb, std::complex<float> c, float d); 

nhưng cơ quan chức năng là:

void procWithComplex(float a, complex float * pb, complex float c, float d) 

mà không phù hợp.

Để giúp trình biên dịch chẩn đoán lỗi này, bạn nên tránh sử dụng bộ tiền xử lý để chuyển đổi các nguyên mẫu khác nhau cho cùng một chức năng.

Để tránh lỗi này, bạn cần phải có mẫu thử nghiệm chức năng chỉ sử dụng các loại có giá trị trong cả C và C++. Chẳng hạn như bạn đã làm trong ví dụ myComplex_t.

0

Chúng tôi đã kết thúc bằng cách sử dụng trình biên dịch chéo so với trình biên dịch gốc trên bảng dev để tạo các tệp nhị phân. Rõ ràng các số phức không được xử lý đúng trên đường biên C đến C++ cho trình biên dịch gốc mà chúng ta đã sử dụng.

Tất cả thay đổi được đề xuất đã được thử và không thành công, nhưng tất cả đều là những đề xuất tốt. Nó giúp xác nhận suy nghĩ của chúng tôi rằng nó có thể là một vấn đề trình biên dịch cho phép chúng tôi thử sử dụng một trình biên dịch chéo. Cảm ơn tất cả!

+1

Đây không phải là giải pháp phù hợp. Bạn đang gọi UB nhưng nhận được may mắn, và mã của bạn có thể phát nổ bất cứ lúc nào. –

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