C99标准详解
变量类型
在C89里新加了一些变量类型:
void
void*
signed char
unsigned char
unsigned short
unsigned long
long double
以及已有类型的新名称:
signed short表示short
signed int表示int
signed long表示long
C9X也加入了新的变量类型:
_Bool
long long
unsigned long long
float _Imaginary
float _Complex
double _Imaginary
double _Complex
long dobule _Imaginary
long double _Complex
C9X也允许扩展的整型类型,它们定义在<inttypes.h>和<stdint.h>里,比如int64_t、uint32_t等等。还有定义在<stdbool.h>里的布尔类型bool,它作如下定义:
#define bool _Bool
#define true 1
#define false 0
bool虽然看上去像int,但还是有稍许不同。比如:
bool b = 0.5;
int n = 0.5;
printf ("b: %d, n: %d\n", b, n);
结果为b: 1, n: 0
整型与浮点型类型汇总(linux3.0.0-17-generic x86)
类型 | sizeof | 格式化字符串 | 常量举例 |
char | 1 | %hhd | 123 |
unsigned char | 1 | %hhu | 234U |
short | 2 | %hd | 12345 |
unsigned short | 2 | %hu | 12345U |
int | 4 | %d | 1234567898 |
unsigned int | 4 | %u | 1234567898U |
long | 4 | %ld | 1234567898L |
unsigned long | 4 | %lu | 1234567898UL |
long long | 8 | %lld | 12345678987654321LL |
unsigned long long | 8 | %llu | 12345678987654321ULL |
float | 4 | %f | 1234.5678F |
double | 8 | %lf | 123456789.987654 |
long double | 12 | %Lf | 12345678912345.987654321L |
一些浮点数的操作可能会导致非正常的值,例如:
-1的根号值为NaN。(Not a Number)
1除以0和log(0)的结果都是无穷大。
C99在<math.h>里定义了fpclassify来提供对浮点数的类别判断,它是一个宏:
Macro: int fpclassify (float-type x),返回值为以下的某值:
FP_NAN:浮点数x不是一个数。
FP_INFINITE:x的值为正负无穷大。
FP_ZERO:x的值为正负0。
FP_SUBNORMAL:x的绝对值过小,以至于无法以正常的形式表示,所以用更低精度、但离0更接近的形式表示。
FP_NORMAL:x为除以上情况之外的正常的值。
除了fpclassify,还有另外几个类似的宏:
Macro: int isfinite (float-type x):等价于(fpclassify (x) != FP_NAN && fpclassify (x) != FP_INFINITE)
Macro: int isnormal (float-type x):等价于(fpclassify (x) == FP_NORMAL)
Macro: int isnan (float-type x):等价于(fpclassify (x) == FP_NAN)
Ubuntu使用fpclassify时可能会出现错误:
undefined reference to `__fpclassify'
这是一个已知的bug,更新gtk-gnutella包可以解决。当然,你还要确保gcc使用了-lm选项来引入math库。
复数类型:
使用<complex.h>里的_Complex类型,或它的别名complex可以操作复数。它的用法为:
complex c = 35 + 78i; //与35 + I*78等价
complex a = c * 2;
printf("%f+%fi", creal(c), cimag(c));
printf("%f+%fi", creal(a), cimag(a));
其中creal和cimag分别得到复数的实数和虚数部分。
_Imagianry表示一个虚数,gcc没有支持这个关键字。
类型 | sizeof | 常量举例 |
float complex | 8 | 12.3f+I*45.6f |
double complex | 16 | 1.23+4.56i |
long double complex | 24 | 1.23l+4.56l |
结构体
里的所有对象都必须表示成连续的字节序列,且每个都至少有8位的宽度。在一个36位字长的机器上,一个字节可以定义成9、12、18、或36位,对于占用一个字节的字符类型来说,它的位数也不会小于8。sizeof返回的“字节”数也是与机器无关的,它可能是8位、9位或其它的位数。
因为这种宽度限制,所以空结构体的对象也要占用至少一字节的空间。比如:
struct Empty
{
};
printf("%d, ", sizeof(struct Empty));
struct Empty e1, e2;
printf("%p, %p\n", &e1, &e2);
输出为:0, 0xbf9ccf8e, 0xbf9ccf8f
再讨论下包含空结构体对象的结构体:
struct EmptyGroup
{
struct Empty e1;
struct Empty e2;
struct Empty e3;
};
int main()
{
struct EmptyGroup eg1, eg2;
printf("%p, %p\n", &eg1, &eg2);
printf("%p, %p, %p\n", &(eg1.e1), &(eg1.e2), &(eg1.e3));
return 0;
}
输出为:
0xbff7965e, 0xbff7965f
0xbff7965e, 0xbff7965e, 0xbff7965e
可以看到各成员共用一个地址。
注意,如果是C++,它的输出会有不同:
0xbfca68d8, 0xbfca68db
0xbfca68d8, 0xbfca68d9, 0xbfca68da
每个成员对象都占用了一个字节,这种特性在C++中保证了多重继承的可能性。
结构体的内存布局有一些限制:
1、结构体开头不能有空洞(hole);
2、成员会增加存储空间;
3、在结构体末,需要的时候可能会放置一个空洞来使结构体足够大,以便在数组里紧密排列并有恰当的内存对齐。
可变长度的结构体
在C89,有一种称为“struct hack”的方法来得到可变数组。
struct s
{
int n_items;
int items[1];
};
struct s *p = malloc(sizeof(struct s) + (n - 1) * sizeof(int));
这种方式可以使得结构体里的数组长度为n。但它被认为多少有些易做图的。C99提供了类似的但合法的“struct”机制:
struct OK
{
int n;
int a[];
};
struct OK *p = (struct OK *)malloc(sizeof(struct OK) + 10 * sizeof(int));
p->a[9] = 9;
注意只有结构体内最后的数组才可以有可变长度,且它不能是唯一的成员。
结构体的初始化与赋值
C89的扩展和C++都支持“Compound Literal”的概念。例如:
struct R
{
int c;
double d;
};
struct S
{
int a;
float b;
struct R r;
char *s;
};
结构体的初始化和赋值可以很简明:
struct S s = { 1, 3.4, {6, 7.8}, "hello" };
s = (struct S){ 1, 3.4, {6, 7.8}, "hello" };
s.r = (struct R){6, 7.8};
C99提供了“Designated Initializers”的机制,它可以以任意顺序为结构体赋值:
s.r = (struct R) { .c = 8, .d = 9.0 };
或 s.r = (struct R) { .d = 8.0, .c = 9 };
对于union,只能用Designated Initializers,比如:
u = { .a = 4 };
虽然Compound Literal也可以使用,但它的效果仅等同于cast,结果可能会不正确。
数组
C9X的可变长度数组是码农的福音。
int n;
scanf("%d", &n);
int a[n];
printf("%d", sizeof(a)/sizeof(a[0]));
由于它的存在,alloca这种东西就out了。使用alloca会得到编译警告。
如果说数组的Compound Literal带来很多便利的话:
int a[
补充:软件开发 , 其他 ,