当然下面列出来的几点都是C的基础用法,只不过是这些用法可能平时不会被注意。所以很多东西第一次看到的时候,可能会觉得很怪异,但是细细想想就能很好的理解,也就能更好的清楚C语言的一些特性。但是在具体的编码过程当中,我还是希望都能老老实实规规矩矩的。因为程序员不需要太多棱角,把代码写得规范整洁比耍小聪明要重要得多。下面我列举了5个例子说明一些问题,如果你是老手看到这些就一笑而过吧,如果是新手,我相信还是会得到一些启发的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include <stdio.h> #define _mir(a) #a int
main()
{ char
* s = _mir(
struct
_st{
int
a;
int
b;
int
c;
};
);
printf ( "%s\n" , s );
return
0;
} |
输出:
1
|
struct
_st{ int
a; int
b; int
c; };
|
说明:
A) 预编译中#是将右边的参数转成一个字符串,##是将左右两边的参数连接成一个字符串。例子是#的用法。
B)
宏当中的参数其实是以逗号(,)分隔的,其他的字符其实都被看成同一个参数,但是换行和空白其实被处理过了,使参数在同一个行中。有兴趣的自己多做些测试吧,这个用法可以用于要写包含特殊字符的字符串,免得要写很多的转义字符(\),但是中间不能有逗号,呵呵~
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h> struct
_st{
int
a;
int
b;
int
c;
}; void
main()
{ printf ( "%d\n" , &(( struct
_st*)0)->b );
} |
输出:
1
|
4 |
说明:
A) &((struct _st*)0)->b
的作用是输出_st结构体中b的偏移。为什么用0当成指针呢,其实很好理解:如果一个_st结构体的地址是0,那么b的地址其实就是b在结构体中的偏移。
B)
其实按理,如果先做((struct _st*)0)->b运算,那么程序肯定异常,所以编译器还是做了优化的,具体编译器怎么做的,我也没深究。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <stdio.h> struct
_st{
int
a : 1;
int
b : 1;
int
c : 1;
}s = { .c = 1,
.b = 0,
.a = 0
}; void
main()
{ printf ( "%d %d %d %d\n" , sizeof (s), s.a, s.b, s.c );
} |
输出:
1
|
4 0 0 -1 |
说明:
A) 在结构体的初始化时,可以指定域进行初始化,如例子中的.c =
1,顺序可以颠倒,这样做的好处就是可读性较强,对于大结构的初始化,在阅读时很方便。缺点就是低版本的编译器可能不支持。
B)
在结构体的声明中,可以指定域的大小,如例子中的int a : 1; 说明a只暂用一个bit,充分展示了C对二进制处理反面的亲和力。
C)
为什么s.c输出是-1,而不是1,其实很简单,因为0xFFFFFFFF表示的是-1,那么一个1bit大小的变量,所有位上面都是1,那么它也表示-1。所以编码的过程中,有符号和无符号混用其实是很危险的一件事情。
4. 例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <stdio.h> void
main()
{ int
i;
char
a[10]= "hello" ;
0[a] = ‘x‘ ;
printf ( "%s\n" , a );
for ( i=0; i<10; i++ )
printf ( "%c" , ( rand ()%10)[ "0123456789" ] );
printf ( "\n"
);
} |
输出:
1
2
|
xello 1740948824 |
说明:
A) 0[a] =
‘x‘;是什么玩意儿?如果写成a[0]=‘x‘;其实你就明白是什么意思了,但是说白了,a[0]和0[a]在编译器看来是一样的。因为数组在做[]运行时,其实是做指针的加法运行:a[0]等价于*(a+0)。所以0[a]也就等价于*(0+a)是完全正确的。
B)
循环中功能是输出一个10位的随机数。其实也等价于"0123456789"[rand()%10]。这里"0123456789"的类型是char*,所以指针也支持[]运算,因为[]运算其实就是加法运算。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <stdio.h> void
func( char
a[10] )
{ printf ( "%d %d\n" , &a, &a[0] );
} void
main()
{ char
a[10];
printf ( "%d %d\n" , &a, &a[0] );
func( a );
} |
输出:
1
2
|
1638208 1638208 1638192 1638208 |
说明:
A)
为什么两行的结果会不一样?在一般情况下,按我的理解,一个数组a,&a和&a[0]的值是一样的。但是当a在形参当中时就不一样了。例子中,func函数中的a,其实a变量是在func函数的栈当中,在func内部,a其实已经被转化成char
*a,所以&a是表示指针变量a在栈中的地址,而&a[0]表示的是指针指向的内存空间的第一个元素的地址,其实也就是调用者传入的数组的第一个元素的地址。不知道我说明白了没有!!
B)
这个可能比较难理解,关键是明白一点,在数组作为形参时,是被转换成指针看待的。