2009-04-08 25 views
7

Tôi đang đọc mã nguồn CRT của Microsoft và tôi có thể đưa ra mã sau đây, trong đó hàm __initstdio1 sẽ được thực hiện trước chính quy trình().Làm thế nào để thực hiện một số mã trước khi nhập chính() thường xuyên trong VC?

Câu hỏi đặt ra là, làm cách nào để thực thi một số mã trước khi nhập thông thường() trong VC (không phải mã VC++)?

#include <stdio.h> 

#pragma section(".CRT$XIC",long,read) 

int __cdecl __initstdio1(void); 

#define _CRTALLOC(x) __declspec(allocate(x)) 

_CRTALLOC(".CRT$XIC") static pinit = __initstdio1; 

int z = 1; 

int __cdecl __initstdio1(void) { 
    z = 10; 
    return 0; 
} 

int main(void) { 
    printf("Some code before main!\n"); 
    printf("z = %d\n", z); 
    printf("End!\n"); 
    return 0; 
} 

Kết quả sẽ là:

Some code before main! 
z = 10 
End! 

Tuy nhiên, tôi không thể hiểu được mã.

Tôi đã thực hiện một số google trên .CRT $ XIC nhưng không tìm thấy may mắn nào. Một số chuyên gia có thể giải thích đoạn mã trên cho tôi, đặc biệt là các phần sau:

  1. Dòng này _CRTALLOC(".CRT$XIC") static pinit = __initstdio1; có nghĩa là gì? Ý nghĩa của biến pinit là gì?
  2. Trong biên soạn trình biên dịch (cl.exe) ném một cảnh báo nói như sau:

Microsoft (R) 32-bit C/C++ Compiler Tối ưu hóa Version 15.00.30729.01 cho 80x86 Bản quyền (C) Microsoft Corporation . Đã đăng ký Bản quyền.

stdmacro.c 
stdmacro.c(9) : warning C4047: 'initializing' : 'int' differs in levels of indirection from 'int (__ 
cdecl *)(void)' 
Microsoft (R) Incremental Linker Version 9.00.30729.01 
Copyright (C) Microsoft Corporation. All rights reserved. 

/out:stdmacro.exe 
stdmacro.obj 

Hành động sửa chữa cần phải được thực hiện để xóa thông điệp cảnh báo là gì?

Xin cảm ơn trước.


Added:

tôi đã sửa đổi mã số và cung cấp cho loại để pinit như _PIFV. Bây giờ thông điệp cảnh báo đã biến mất.

Các mã mới như sau:

#include <stdio.h> 

#pragma section(".CRT$XIC1",long,read) 

int __cdecl __initstdio1(void); 

typedef int (__cdecl *_PIFV)(void); 

#define _CRTALLOC(x) __declspec(allocate(x)) 

_CRTALLOC(".CRT$XIC1") static _PIFV pinit1 = __initstdio1; 

int z = 1; 

int __cdecl __initstdio1(void) { 
    z = 100; 

    return 0; 
} 

int main(void) { 
    printf("Some code before main!\n"); 
    printf("z = %d\n", z); 
    printf("End!\n"); 
    return 0; 
} 

Trả lời

1

Có một số thông tin here (tìm kiếm CRT). Ý nghĩa của biến số pinit là không, nó chỉ là một phần dữ liệu được đặt trong tệp thực thi, nơi mà thời gian chạy có thể tìm thấy nó.Tuy nhiên, tôi sẽ tư vấn cho bạn để cung cấp cho nó một loại, như thế này:

_CRTALLOC(".CRT$XIC") static void (*pinit)()=... 

Cảnh báo mối liên kết có lẽ chỉ cảnh báo bạn bạn có một hàm có int kiểu trả về, nhưng không trả lại bất cứ điều gì (có lẽ bạn muốn thay đổi tốt hơn kiểu trả về thành void).

3

Trong C++ ít nhất, bạn không cần tất cả những thứ cụ thể thực hiện:

#include <iostream> 

struct A { 
    A() { std::cout << "before main" << std::endl; } 
}; 

A a; 

int main() { 
    std::cout << "in main" << std::endl; 
} 
+0

Đây là một ý tưởng hay. Nhưng mã của bạn có thể chuyển biên dịch chỉ trong C++; không phải trong C. – yinyueyouge

+0

Câu hỏi được gắn thẻ C++ – mouviciel

4

Đây là những gì _CRTALLOC được định nghĩa là:

extern _CRTALLOC(".CRT$XIA") _PVFV __xi_a[]; 
extern _CRTALLOC(".CRT$XIZ") _PVFV __xi_z[];// C initializers 
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[]; 
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];// C++ initializers 

Đó là bảng những điều cần tiền khởi tạo, trong đó con trỏ đến hàm của bạn __initstdio1 được đặt.

này trang mô tả hình CRT khởi động:

http://msdn.microsoft.com/en-us/library/bb918180.aspx

5

Cách đơn giản để thực hiện việc này.

#include <iostream> 

int before_main() 
{ 
    std::cout << "before main" << std::endl; 
    return 0; 
} 

static int n = before_main(); 

void main(int argc, char* argv[]) 
{ 
    std::cout << "in main" << std::endl; 
} 
+0

vấn đề có thể phát sinh khi before_main() phụ thuộc vào dữ liệu được tạo trong một hàm khác chạy trước main(). Thứ tự trong những chức năng như vậy chạy không được xác định. Điều này, đặc biệt, có nghĩa là dữ liệu toàn cục before_main() tạo ra có thể được ghi đè bởi các thường trình thời gian chạy khởi tạo dữ liệu toàn cục thành 0s – dmityugov

+6

Đây là C++, không C. – RBerteig

+1

thay thế std :: count << with puts() và nó sẽ là C – dmityugov

1

Ngay cả trong C, cần phải nhập một số mã trước khi nhập main(), nếu chỉ để chuyển dòng lệnh thành quy ước gọi điện C. Trong thực tế, thư viện chuẩn cần một số khởi tạo, và các nhu cầu chính xác có thể khác nhau từ biên dịch để biên dịch.

Điểm vào chương trình thực được đặt tại thời gian liên kết và thường nằm trong mô-đun có tên là crt0 vì lý do lịch sử. Như bạn đã tìm thấy, nguồn của module đó có sẵn trong các nguồn crt.

Để hỗ trợ khởi tạo được phát hiện tại thời gian liên kết, một đoạn đặc biệt được sử dụng. Cấu trúc của nó là một danh sách các con trỏ hàm của chữ ký cố định, sẽ được lặp lại sớm trong crt0 và mỗi hàm được gọi. Cùng một mảng (hoặc rất giống với nó) của các con trỏ hàm được sử dụng trong một liên kết C++ để giữ con trỏ tới các hàm tạo của các đối tượng chung.

Mảng được điền bởi trình liên kết bằng cách cho phép mọi mô-đun được liên kết bao gồm dữ liệu trong đó, tất cả được nối với nhau để tạo thành phân khúc trong tệp thực thi đã hoàn thành. Ý nghĩa duy nhất đối với biến số pinit là nó được khai báo (bởi macro _CRTALLOC()) được đặt trong phân khúc đó và được khởi tạo đến địa chỉ của hàm được gọi trong khi khởi động C.

Rõ ràng, chi tiết về điều này cực kỳ cụ thể cho từng nền tảng. Đối với lập trình nói chung, bạn có lẽ phục vụ tốt hơn bằng cách gói khởi tạo của bạn và chính hiện tại của bạn bên trong một mới main():

int main(int argc, char **argv) { 
    early_init(); 
    init_that_modifies_argv(&argc, &argv); 
    // other pre-main initializations... 
    return real_main(argc,argv); 
} 

Đối với mục đích đặc biệt, sửa đổi các mô-đun crt0 bản thân hoặc làm thủ thuật biên dịch cụ thể để có được thêm chức năng khởi đầu được gọi là câu trả lời hay nhất. Ví dụ, khi xây dựng các hệ thống nhúng chạy từ ROM mà không có trình tải hệ điều hành, thường cần phải tùy chỉnh hành vi của mô-đun crt0 để có một ngăn xếp ở đó để đẩy các tham số đến main(). Trong trường hợp đó, có thể không có giải pháp nào tốt hơn là sửa đổi crt0 để khởi tạo phần cứng bộ nhớ cho phù hợp với nhu cầu của bạn.

2

Tôi đã viết một giải thưởng article về giải thưởng này trên CodeGuru một thời gian trước đây.