最新更新:09-05-2011 =
======================
----------------位域问题----------------------
1 #include <stdio.h>
2
3 struct test {
4 unsigned char data:2;
5 unsigned char data1:3;
6 unsigned char data2:3;
7 };
8
9 int main(void)
10 {
11 struct test t = {
12 .data = 1,
13 };
14
15 t.data = 7;
16 printf("sizeof(t)=%d\n", sizeof(t));
17 // printf("sizeof(t.data)=%d\n", sizeof(t.data)); //compile error
18 printf("t.data = %d\n", t.data);
19
20 return 0;
21 }
结果是:
sizeof(t)=1
t.data = 3
----------------------------------------------
* 用双引号引起的字符串,代表的是一个指向无名数组起始字符的指针,
该数组被双引号之间的字符以及一个额外的二进制值为零的字符'\0'
初始化。
* strlen(str) 返回的是字符串的不包括结尾的'\0'在内的字符的个数。
下面分析一个例子:
char * r, *malloc();
r = malloc(strlen(s) + strlen(t));
strcpy(r, s);
strcat(r, t);
错误分析:
1. malloc可能无法提供请求的内存,所以要检查malloc的返回值。
2. r 作为局部变量会被自动释放,就会造成内存泄漏,所以必须显式调用free()
3. malloc并没有分配到足够的内存。因为strlen(s)的值为n,那么需要占用n+1个
字符空间,多出一个给'\0'。
* 用单引号引起的一个字符实际上代表一个整数。
* 任何C变量的声明都由两部分组成:类型和一组类似表达式的声明符。
对该表达式求值应该返回一个声明中给定的类型的结果。
比如 float * p(), 那么*p() 应该返回一个 float 类型的值,所以
p 是一个返回 float * 类型值的指针。
* 类型转换符: 把声明中的变量名和末尾的分号去掉,将剩余部分用
圆括号括起来即可。
* 如果 fp 是个指向函数的指针,调用时:(* fp)();
------> 例子1
void (*signal(int, void (*) (int))) (int);
<===> typedef void (*HANDLER) (int);
HANDLER signal(int, HANDLER);
-----> 例子2
(* (void (*) () ) 0) ();
注意: (*0) (); 并不能调用地址0处的函数,因为 * 需要一个指针做操作数。
------------------------------------我的积累----------------
#include <stdio.h>
int f1(int a)
{
return ++ a;
}
int f2(int a)
{
return 5;
}
int (*fun(int a, int (*f)(int)))(int)
{
printf("f returned: %d\n", (*f)(a));
return &f2;
}
int main(void)
{
printf("In main:%d\n", (*fun(1, f1))(2));
return 0;
}
=======================================
==== 错误例子
struct a{
...
}
main(void){
...
}
表示 main的返回类型为 struct a 类型。
==== swich 语句
每个 case 结尾容易忘记break;
如果程序真不需要break,那么最好在结尾注明,这是个好的编程习惯。
===== else问题
* else始终与同一对花括号内最近未匹配的if结合。
- 例子
if (x == 0)
if(y == 0) error();
else{
z = x + y;
}
实际上被解释为:
if (x == 0) {
if (y == 0) error();
else {
z = x + y;
}
}
* 建议:良好的编程习惯是:即使是一条语句也加上{}.
比如:
if (x > 0) {
x = 1;
} else {
x = -1;
}
==============指针和数组
* c语言中只有一位数组,而且数组的大小必须在编译时就作为一个常数确定下来。
数组的元素可以是任何类型,也可是另一个数组。
* int calendar[12][31];这个语句声明了calendar是一个数组,该数组拥有12哥数组类型的元素,
其中每一个元素都是一个拥有31个整型元素的数组(而不是 一个拥有31个数组类型的元素的数组,
其中每一个元素 又是一个拥有12个整型元素的数组)。
为什么呢?
*
a[i] <===> *(a+i) <===> *(i+a) <===> i[a] //不提倡
*
a[4][7] <===> *(*(a+4)+7)
*
============== 08_08_2011
-- 复制指针并不复制指针所指向的数据。
-- 任何时候,常数0 被转化为指针使用时,这个指针绝对不能被解除引用。
if (p == (char*) 0) ... // 正确
if (strcmp(p, (char*) == 0) ... //错误
-- 运算符的求值顺序:
&&, || 首先对左侧操作数求值,只在需要时才对右侧操作数求值(对于&&是左侧为真,对于||是左侧为假时)。
a?b:c中,首先对a求值。根据a的值再求b或者c的值。
逗号运算符,首先对左侧操作数求值,然后该值被“丢弃”,再对右侧操作数求值。
-- 可能出现错误:
i = 0;
while (i < n)
y[i] = x[i++];
这段代码假设了y[i]的地址在i 的自增操作执行之前被求值,这一点是没有任何保证的。
最好换成如下的写法:
i = 0;
while ( i < n) {
y[i] = x[i];
i ++;
}
==== 整数的溢出
C语言中存在两种整数算术运算,有符号运算和无符号运算。在无符号运算中没有“溢出”一说:所有无符号运算都是以
2的n次方为模,n为结果的位数。如果参与运算的一个是有符号数,一个是无符号数,那么有符号整数会被转化为
无符号整数,“溢出”也不会发生。
* 当两个操作数都是有符号数时,“溢出“就有可能发生,而且"溢出"的结果是未定义的。
例子:
假定 a和b是两个非负整型变。那么检查是否会溢出的方法可以如下:
if ((unsigned) a + (unsigned) b > INT_MAX)
complain();
此处 INT_MAX代表最大的整数值。ANSI C在limits.h中定义了INT_MAX.
另一个方法是:
if (a > INT_MAX - b)
complain();
更多相关的介绍: http://blog.csdn.net/duduhaha/article/details/624123
=== main函数的返回值问题
大多数C语言实现都通过main函数的返回值告知操作系统该函数的执行是成功还是失败。典型情况是,
返回值为0代表执行成功,返回值非零表示执行失败。
=== 连接器
连接器的输入是一组目标模块和库文件。输出是一个载入模块。对于每个目标模块中的每个外部对象,
连接器都要检查载入模块,看是否有同名的外部对象。如果没有,连接器就将该外部对象加到载入模块中,
如果有,连接器就开始处理命名冲突。
===函数参数相关问题
例子:
#include <stdio.h>
int main(int argc, char* argv[])
{
int i;
char c;
for (i = 0; i < 5; i++) {
scanf("%d", &c);
printf("%d", i);
}
printf("\n");
return 0;
}
在我的系统上运行结果是:
1 2 3 4 5
00000
... 而且一直不会结束,最后只好ctrl + c
原因: c被声明为 char类型,而不是int 类型。程序中scanf函数得到的是一个指向字符的指针,scanf并不能分辨
这种情况,它只是将这个指向字符的指针作为指向整数的指针而接受,并在在指针指向的位置存储一个整数。
因为整数所占的存储空间大于字符所占空间,字符c附近的内存(i的低端部分)将被覆盖。因此,每次读入
一个数值,i的低端都会被覆盖为0,所以循环一直进行。
============外部类型的检查
保证一个特定名称的所有外部声明都有相同的类型,而且是严格意义上的“相同类型”。
如下就是一个错误的例子:
char filename[] = "/etc/passwd";
而在另一个文件中包含声明:
extern char* filename;
第一个声明中, filename 是一个字符数组,第二个,是一个指针。内存布局是不同的。