现在的位置: 首页 > 综合 > 正文

《高质量C/C++编程》学习笔记2(函数、指针、引用)

2014年01月28日 ⁄ 综合 ⁄ 共 2569字 ⁄ 字号 评论关闭

const 与 #define 的比较

(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应)。
(2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调
试。

      枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是:它的隐含数据类型是整数,其最大值有限,且不能表示浮点数(PI=3.14159)。

 

函数设计

C 语言中,函数的参数和返回值的传递方式有两种:值传递(pass by value)和指针传递(pass by pointer)

【规则6-1-1】参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字。
如果函数没有参数,则用void 填充。
例如:
void SetValue(int width, int height); // 良好的风格
void SetValue(int, int); // 不良的风格
float GetValue(void); // 良好的风格
float GetValue(); // 不良的风格

一般地,应将目的参数放在前面,源参数放在后面。如  void StringCopy(char *strDestination, char *strSource);

【规则6-1-3】如果参数是指针,且仅作输入用,则应在类型前加const,以防止该指针在函数体内被意外修改。

          void StringCopy(char *strDestination,const char *strSource);

【规则6-2-2】函数名字与返回值类型在语义上不可冲突。
违反这条规则的典型代表是C 标准库函数getchar。
例如:
char c;
c = getchar();
if (c == EOF)

按照getchar 名字的意思,将变量c 声明为char 类型是很自然的事情。但不幸的是
getchar 的确不是char 类型,而是int 类型,其原型如下:
int getchar(void);
由于c 是char 类型,取值范围是[-128,127],如果宏EOF 的值在char 的取值范围
之外,那么if 语句将总是失败,这种“危险”人们一般哪里料得到!导致本例错误的责
任并不在用户,是函数getchar 误导了使用者。

 【规则6-3-1】在函数体的“入口处”,对参数的有效性进行检查。

【规则6-3-2】在函数体的“出口处”,对return 语句的正确性和效率进行检查。

注意:return 语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。例如
char * Func(void)
{
char str[] = “hello world”; // str 的内存位于栈上

return str; // 将导致错误
}

 使用断言

void *memcpy(void *pvTo, const void *pvFrom, size_t size)
{
   assert((pvTo != NULL) && (pvFrom != NULL)); // 使用断言
   byte *pbTo = (byte *) pvTo; // 防止改变pvTo 的地址
   byte *pbFrom = (byte *) pvFrom; // 防止改变pvFrom 的地址
   while(size -- > 0 )
      *pbTo ++ = *pbFrom ++ ;
   return pvTo;
}

【规则6-5-1】使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况
之间的区别,后者是必然存在的并且是一定要作出处理的。


【规则6-5-2】在函数的入口处,使用断言检查参数的有效性(合法性)。

 

指针和引用

(1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化)。
(2)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。
(3)一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象)。

函数的参数和返回值的传递方式有三种:值传递、指针传递和引用传递。

值传递

由于Func1 函数体内的x 是外部变量n 的一份拷贝,改变x 的值不会影响n, 所以n 的值仍然是0。
void Func1(int x)
{
x = x + 10;
}
int n = 0;
Func1(n);
cout << “n = ” << n << endl; // n = 0

 


 

指针传递

由于Func2 函数体内的x 是指向外部变量n 的指针,改变该指针的内容将导致n 的值改变,所以n 的值成为10。
void Func2(int *x)
{
(* x) = (* x) + 10;
}

int n = 0;
Func2(&n);
cout << “n = ” << n << endl; // n = 10

 


 

引用传递

由于Func3 函数体内的x 是外部变量n 的引用,x 和n 是同一个东西,改变x 等于改变n,所以n 的值成为10。
void Func3(int &x)
{
x = x + 10;
}

int n = 0;
Func3(n);
cout << “n = ” << n << endl; // n = 10

 

使用const 提高函数的健壮性

const 是constant 的缩写,“恒定不变”的意思。“Use const whenever you need”。

用const 修饰函数的参数

const 只能修饰输入参数

如果输入参数采用“指针传递”,那么加const 修饰可以防止意外地改动该指针,起到保护作用。

void StringCopy(char *strDestination, const char *strSource);

如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。

例如void Func(int x) 不应该改为void Func(const int &x)。

对于非内部数据类型的参数而言,象void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为void Func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。

用const 修饰函数的返回值

例如函数
const char * GetString(void);
如下语句将出现编译错误:
char *str = GetString();
正确的用法是
const char *str = GetString();

 

抱歉!评论已关闭.