Chinaunix首页 | 论坛 | 博客
  • 博客访问: 311820
  • 博文数量: 41
  • 博客积分: 857
  • 博客等级: 准尉
  • 技术积分: 614
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-11 12:42
文章分类

全部博文(41)

文章存档

2016年(1)

2015年(2)

2012年(6)

2011年(32)

分类: C/C++

2011-06-10 09:35:45

以下基本常识观点,本人均在C-Free软件上运行测试过.
 
1.关于sizeof:
sizeof不是函数,而是关键字。sizeof在计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略。
例:
#include
int main(int argc,char *argv[])
{
 int x=32;
 printf("sizeof(x)=%d\n",sizeof x);
 printf("sizeof(int)=%d\n",sizeof(int));
 return (0);
}
结果:

2.关于static
(1)修饰变量:
静态全局变量,作用域仅限于变量被定义的文件中,其他文件即使用 extern声明也没法使用它。在定义之处前面的那些代码行也不能使用它。想要使用就得在前面再加 extern(未实验成功!)
 
静态局部变量:由于被 static修饰的变量总是存在内存的静态区,所以即使这个函数运行结束,这个静态变量的值还是不会被销毁,函数下次使用时仍然能用到这个值。
例:
(1)加static
#include
void fo(void)
{
 static int x=10;
 printf("x=%d\n",x);
 x=x+1;
}
int main(int argc,char *argv[])
{
    fo();
    fo();
 return (0);
}
结果:
(2)不加static
#include
void fo(void)
{
    int x=10;
 printf("x=%d\n",x);
 x=x+1;
}
int main(int argc,char *argv[])
{
    fo();
    fo();
 return (0);
}
结果:
 
(2)修饰函数:此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。

3.关于const:
精确的说应该是只读的变量。编译器通常不为普通 const只读变量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的值,没有了存储与读内存的操作,使得它的效率也很高。

const定义的只读变量在程序运行过程中只有一份拷贝(因为它是全局的只读变量,存放在静态区),而#define定义的宏常量在内存中有若干个拷贝。
#define宏是在预编译阶段进行替换,而const修饰的只读变量是在编译的时候确定其值。
注意:const修饰的只读变量必须在定义的同时初始化。

const修饰指针:
const int *p; // p 可变,p指向的对象不可变
int const *p; // p可变,p指向的对象不可变
int *const p; //p不可变,p指向的对象可变
const int *const p; //指针p和p指向的对象都不可变

记忆和理解的方法:先忽略类型名(编译器解析的时候也是忽略类型名),看const离谁近。离谁近就修饰谁。如:
const int *p; //const修饰*p,p是指针,*p是指针指向的对象,不可变
int const *p;//const修饰*p,p是指针,*p是指针指向的对象,不可变
int *const p;//const修饰 p,p不可变,p 指向的对象可变
const int *const p; //前一个 const修饰*p,后一个const修饰p,指针p和p指向的对象都不可变

const修饰符也可以修饰函数的参数和返回值,当不希望这个值被函数体内意外改变时使用。
在另一连接文件中引用const只读变量:extern const int i; 

const修饰的只读变量不能用来作为定义数组的维数,也不能放在case关键字后面。
4.关于volatile
例: int i=10;
int j = i;//(1)语句
int k = i;//(2)语句
在(1)语句时从内存中取出i的值赋给j之后,这个值并没有被丢掉,而是在(2)语句时继续用这个值给k赋值。

volatile int i=10;
int j = i;//(3)语句
int k = i;//(4)语句,此时的k不见得就是10
 
volatile关键字告诉编译器i是随时可能发生变化的,每次使用它的时候必须从内存中取出i的值,因而编译器生成的汇编代码会重新从i的地址处读取数据放在k中。

所以说volatile可以保证对特殊地址的稳定访问。
5.关于enum:
例子: enum enum_type_name
{
ENUM_CONST_1,
ENUM_CONST_2,
...
ENUM_CONST_n
}enum_variable_name;
enum_type_name 类型的变量enum_variable_name只能取值为花括号内的任何一个值,如果赋给该类型变量的值不在列表中,则会报错或者警告。这些成员都是常量, 也就是我们平时所说的枚举常量 (常量一般用大写)。如果不赋值则会从被赋初值的那个常量开始依次加1,如果都没有赋值,它们的值从0开始依次递增1。
枚举与#define宏的区别:
#define宏常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。
枚举可以一次定义大量相关的常量,而#define宏一次只能定义一个。

6.关于typedef:
typedef的真正意思是给一个已经存在的数据类型(注意:是类型不是变量)取一个别名,而非定义一个新的数据类型。
易错点:
#define PCHAR char*
PCHAR p3,p4;
这里的p4却不是指针,仅仅是一个char类型的字符。这种错误很容易被忽略。

二.符号

1.关于编译器:
编译器的确会将注释剔除,但不是简单的剔除,而是用空格代替原来的注释。

2.关于注释:
(1)对于全局数据(全局变量、常量定义等)必须要加注释。
(2)注释的位置应与被描述的代码相邻,可以与语句在同一行,也可以在上行,但不可放在下方。
(3)注释的缩进要与代码的缩进一致。
(4)注释代码段时应注重“为何做(why)” ,而不是“怎么做(how)” 。
(5)对于函数的入口出口数据给出注释。

3.关于接续符:
C语言里以反斜杠(\)表示断行。编译器会将反斜杠剔除掉,跟在反斜杠后面的字符自动接续到前一行。但是注意:反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格。即反斜杠作为接续符时,在本行其后面不能再有任何字符,空格都不行。

4.关于左右移:
左移和右移的位数不能大于数据的长度,否则溢出。

5.关于a+++b:
C语言有这样一个规则:每一个符号应该包含尽可能多的字符。也就是说,编译器将程序分解成符号的方法是,从左到右一个一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。

6.关于优先级:
(1).的优先级高于*,->操作符用于消除这个问题
如:*p.f:对 p 取 f 偏移,作为指针,然后进行解除引用操作。*(p.f)
(2)[]高于*
如:int *ap[]:ap 是个元素为 int指针的数组int *(ap[])
(3)函数()高于*
如:int *fp():fp是个函数,返回int *,int *(fp())
(4)== 和!=高于位操作
(5)== 和!=高于赋值符
(6)算术运算符高于位移运算符
(7)逗号运算符在所有运算符中优先级最低

三.预处理

1.关于注释与预处理:
注释先于预处理指令被处理,因此,试图用宏开始或结束一段注释是不行的。

2.关于预处理的括号:
例:#define SQR (x) x * x
假设 x 的值是个表达式10+1,SQR (x)被替换后变成10+1*10+1,就不对了。要搞定它其实很简单,别吝啬括号就行了。
例:
#include
#define sum(x) x*x
int main(int argc,char *argv[])
{
     printf("sum(x)=%d\n",sum(10+1));
 return (0);
}
结果:
例:
#include
#define sum(x) (x)*(x)
int main(int argc,char *argv[])
{
     printf("sum(x)=%d\n",sum(10+1));
 return (0);
}
结果:

 
3.关于预处理的空格:
例:#define SUM (x)(x)+(x)
空格仅仅在定义的时候有效,在使用这个宏函数的时候,空格会被编译器忽略掉。也就是说,上一节定义好的宏函数 SUM(x)在使用的时候在 SUM 和(x)之间留有空格是没问题的。

4.关于#define和#undef:
例:#define PI 3.141592654
。。。。。
#undef PI
也就是说宏的生命周期从#define开始到#undef结束。

5.关于#include:
include支持相对路径,.代表当前目录,..代表上层目录。

6.关于#运算符:
# 和 ## 操作符是和宏使用的. 使用#可以使在#后的首个参数返回为一个带引号的字符串
 
例:使用前
#include
#define SRQ(s) printf("s*s=%d\n",s*s)
int main(int argc,char *argv[])
{   
     int x=10;
     SRQ(x);
  return (0);
}
结果:
例:使用后
#include
#define SRQ(s) printf(""#s"*"#s"=%d\n",s*s)
int main(int argc,char *argv[])
{   
     int x=10;
     SRQ(x);
  return (0);
}
结果:
7.关于##预算符:
##运算符可以用于宏函数的替换部分。这个运算符把两个语言符号组合成单个语言符号。看例子:
#define XNAME(n) x ## n
如果这样使用宏:
XNAME(8)
则会被展开成这样:
x8
##就是个粘合剂,将前后两部分粘合起来。
例:
#include
#define sum(x,y) x ## y
int main(int argc,char *argv[])
{   
  printf("sum(1,2)=%d",sum(1,2));
  return (0);
}
结果:
 

阅读(1764) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~