2011-10-04 29 views
19

Xin vui lòng xem mã của tôi:Dấu hiệu macro cộng bí ẩn này trong stdint.h là gì?

#include <stdint.h> 

int main(int argc, char *argv[]) 
{ 
unsigned char s = 0xffU; 
char ch = 0xff; 
int val = 78; 
((int8_t) + (78)); /*what does this mean*/ 

INT8_C(val); /*equivalent to above*/ 

signed char + 78; /*not allowed*/ 

return 0; 
} 

Tôi thấy rằng định nghĩa vĩ mô trong <stdint.h> là:

#define INT8_C(val) ((int8_t) + (val)) 

ý nghĩa hoặc nghĩa của điều này cộng với ký là gì?

+1

Cần lưu ý rằng định nghĩa này của 'INT8_C' là sai: 7.18.4 đoạn 3: "Mỗi lời gọi của một trong các macro này (CHÚ Ý: tham chiếu đến' INTN_C') sẽ mở rộng thành một biểu thức hằng số nguyên ** thích hợp để sử dụng trong các chỉ thị tiền xử lý '# if'. **" trong một chỉ thị tiền xử lý, vì các kiểu (và do đó phôi) không tồn tại trong quá trình tiền xử lý. Trình biên dịch/nền tảng nào bạn đang sử dụng? Bạn nên xem xét việc nộp báo cáo lỗi về vấn đề này. –

+4

@ChrisLutz: Nó hợp lệ trong C99; nó đã trở thành không hợp lệ trong Corrigendum kỹ thuật 1, được kết hợp vào N1256. Điều này đã được đáp ứng với [DR # 209] (http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_209.htm). –

+0

@KeithThompson - Ah. Tôi có một bản sao của TC2, và cho đến gần đây gần đây đã không thực sự nhận thức được về Chương trình kỹ thuật. –

Trả lời

27

Đoạn:

((int8_t) + (78)); 

là một ngôn luận, một trong đó có giá trị 78, áp dụng unary +, sau đó phôi đó để một kiểu int8_t, trước khi ném nó đi. Không có gì khác biệt với các biểu thức pháp lý:

42; 
a + 1; 

cũng đánh giá các biểu thức sau đó sẽ loại bỏ kết quả nếu trình biên dịch có thể không có tác dụng phụ).

Các biểu thức "khỏa thân" này hoàn toàn hợp lệ trong C và thường hữu ích chỉ khi chúng có tác dụng phụ, chẳng hạn như với i++, tính toán i và ném nó đi với tác dụng phụ là nó tăng giá trị.

Cách bạn nên được sử dụng vĩ mô đó là hơn dọc theo dòng:

int8_t varname = INT8_C (somevalue); 

Lý do cho sự + hành unary dường như không cần thiết có thể được tìm thấy trong tiêu chuẩn. Trích dẫn C99 6.5.3.3 Unary arithmetic operators /1:

Toán hạng của toán tử đơn hoặc + phải có loại số học;

Và, trong 6.2.5 Types, /18:

Integer và loại nổi được gọi chung là các loại số học.

Nói cách khác, đơn nhất + ngăn bạn sử dụng tất cả các loại dữ liệu khác trong macro, chẳng hạn như con trỏ, số phức hoặc cấu trúc.

Và, cuối cùng, lý do của bạn:

signed char + 78; 

đoạn không hoạt động là bởi vì nó không phải là điều tương tự. Điều này đang bắt đầu tuyên bố một biến loại signed char nhưng chokes khi nó được + vì đó không phải là một tên biến pháp lý. Để làm cho nó tương đương với đoạn làm việc của bạn, bạn sẽ sử dụng:

(signed char) + 78; 

đó là đúc giá trị +78signed char.

Và, theo C99 7.8.14 Macros for integer constants /2, bạn cũng nên cẩn thận với việc sử dụng phi hằng trong những macro, họ đang không được bảo đảm để làm việc:

Đối số trong bất kỳ trường hợp các macro sẽ là một hằng số nguyên không được trộn lẫn (như được định nghĩa trong 6.4.4.1) với một giá trị không vượt quá giới hạn cho loại tương ứng.

6.4.4.1 chỉ đơn giản là xác định các định dạng số nguyên khác nhau (thập phân/bát phân/hex) với hậu tố khác nhau (U, UL, ULL, L, LL và chữ thường tương đương, tùy thuộc vào loại). Điểm mấu chốt là chúng phải là các hằng số thay vì các biến.

Ví dụ, glibc có:

# define INT8_C(c)  c 
# define INT16_C(c)  c 
# define INT32_C(c)  c 
# if __WORDSIZE == 64 
# define INT64_C(c) C## L 
# else 
# define INT64_C(c) C## LL 
# endif 

mà sẽ cho phép bạn INT8_C vĩ mô để làm việc tốt nhưng văn bản INT64_C(val) sẽ được pre-chế biến thành một trong hai valL hoặc valLL, không phải trong đó bạn muốn.

+0

Thực ra, định nghĩa OP cho 'INT8_C' vi phạm tiêu chuẩn, bằng cách đọc của tôi. 7.18.4 đoạn 3: "Mỗi lệnh gọi một trong các macro này sẽ mở rộng thành một biểu thức hằng số nguyên ** thích hợp để sử dụng trong các chỉ thị tiền xử lý' # if'. ** "Không thể sử dụng định nghĩa gốc của' INT8_C' một '# if' chỉ thị, và do đó là không chính xác. –

+3

@Chris: Dấu + làm cho nó hợp lệ trong các chỉ thị tiền xử lý. 'int8_t' mở rộng thành 0, và' (0) + (val) 'chỉ đơn giản là' val'. –

+0

Làm cách nào để 'int8_t' mở rộng thành 0? Tôi nghĩ đó là một typedef? o_O –

10

Đó là một toán tử độc lập +. Nó mang lại giá trị của toán hạng của nó, nhưng nó chỉ có thể được áp dụng cho các kiểu số học. Mục đích ở đây là để ngăn chặn việc sử dụng INT8_C trên một biểu thức của kiểu con trỏ.

Nhưng tuyên bố của bạn biểu hiện

INT8_C(val); 

đã không xác định hành vi. Đối số cho INT8_C() là bắt buộc phải là "hằng số nguyên không có xáo trộn ... với giá trị không vượt quá giới hạn cho loại tương ứng" (N1256 7.18.4). Mục đích của các macro này là cho phép bạn viết một cái gì đó như INT64_C(42) và mở rộng lên 42L nếu int64_tlong hoặc đến 42LL nếu int64_t là `dài và lâu hơn.

Nhưng văn bản hoặc

((int8_t) + (78)); 

hoặc

((int8_t) + (val)); 

trong mã của riêng bạn là hoàn toàn hợp pháp.

EDIT:

Định nghĩa cho INT8_C() trong câu hỏi:

#define INT8_C(val) ((int8_t) + (val)) 

là hợp lệ trong C99, nhưng là không phù hợp theo quy định của dự thảo N1256 sau, như là kết quả của một sự thay đổi trong một trong các chương trình kỹ thuật.

Chuẩn C99 ban đầu, phần 7.18.4.1p2, nói:

vĩ mô INT *** N * _C ** (giá trị) sẽ mở rộng sang một số nguyên ký thường xuyên với các Speci fi giá trị ed và gõ int_least *** N * _t **.

N1256 thay đổi này để (đoạn 3):

Mỗi gọi của một trong các macro sẽ mở rộng sang một số nguyên biểu thức hằng số thích hợp để sử dụng trong # nếu chỉ thị tiền xử lý. Loại biểu thức phải có cùng loại như biểu thức của loại tương ứng được chuyển đổi theo các quảng cáo số số nguyên. Giá trị của biểu thức phải là giá trị của đối số .

Thay đổi được thực hiện để phản hồi lại Defect Report #209.

EDIT2: Nhưng hãy xem câu trả lời của R ..; nó thực sự hợp lệ trong N1256, vì những lý do khá mơ hồ.

+1

Không chỉ là mã của OP sai, bản sao của OP của 'stdint.h' là sai (nếu đó thực sự là định nghĩa của mình về' INT8_C'). –

+0

@ChrisLutz: Có gì sai? –

+0

Nó vi phạm 7.18.4 đoạn 3. Xem câu trả lời của tôi dưới đây để tôi không tiếp tục gửi spam đoạn đó khắp nơi. –

0

Đó là một cộng thêm đơn nhất.

Chỉ có hai thứ là có thể đang hoạt động.

  1. Ứng dụng này được hiểu là số chuyển đổi số học thông thường thông thường cho toán hạng. Điều này thiết lập một loại loại thực phổ biến cho kết quả. Tôi không thể nghĩ ra bất kỳ lý do nào để ép buộc điều này nếu kết quả sắp được đưa vào một cái gì đó cụ thể.

  2. Nó hạn chế toán hạng thành các loại toán tử số học được xác định. Điều này có vẻ giống như lý do nhiều khả năng.

18

Dường như mọi người đã bỏ lỡ điểm của toán tử cộng đơn ở đây, để làm cho kết quả có hiệu lực trong #if chỉ thị tiền xử lý. Đưa ra:

#define INT8_C(val) ((int8_t) + (val)) 

các chỉ thị:

#define FOO 1 
#if FOO == INT8_C(1) 

mở rộng như:

#if 1 == ((0) + (1)) 

và do đó làm việc như mong đợi. Điều này là do thực tế rằng bất kỳ biểu tượng nào có biểu tượng #define d trong chỉ thị #if sẽ mở rộng đến 0.

Nếu không có cộng thêm đơn nhất (trở thành dấu cộng nhị phân trong chỉ thị #if), cụm từ sẽ không hợp lệ trong chỉ thị #if.

+0

Điều đó thực sự kỳ lạ và không may là công trình. Điều này có được tính là mã C bị xáo trộn không? Tôi nghĩ rằng có '+' là một toán tử đơn nhất trong một tình huống và toán tử nhị phân trong một trường hợp khác, dựa vào '(int8_t)' được hiểu là một diễn viên hoặc như một biểu tượng không xác định, đang giáp với obfuscation ... –

+0

Nếu bạn nghĩ điều này bị quấy rầy, không đọc tgmath.h .... –

+1

Điều đó thật đẹp và xấu xí. –

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