当前位置:文档之家› C与C++中const关键字详解

C与C++中const关键字详解

C与C++中const关键字详解
C与C++中const关键字详解

C/C++中const 关键字详解

为什么使用const?采用符号常量写出的代码更容易维护;指针常常是边读边移动,而不是边写边移动;许多函数参数是只读不写的。const 最常见用途是作为数组的界和switch 分情况标号(也可以用枚举符代替),分类如下:

常变量:const 类型说明符变量名

常引用:const 类型说明符&引用名

常对象:类名const对象名

常数组:类型说明符const 数组名[大小]

常成员函数:类名::fun(形参) const

常指针:const 类型说明符* 指针名,类型说明符* const 指针名

首先提示的是:在常变量(const 类型说明符变量名)、常引用(const 类型说明符&引用名)、常对象(类名const 对象名)、常数组(类型说明符const 数组名[大小]),“const”与“类型说明符”或“类名”(其实类名是一种自定义的类型说明符)的位置可以互换。如:

const int a=5; 与int const a=5; 等同

类名const 对象名与const 类名对象名等同

用法1:常量

取代了C 中的宏定义,声明时必须进行初始化(!c++类中则不然)。const限制了常量的使用方式,并没有描述常量应该如何分配。如果编译器知道了某const 的所有使用,它甚至可以不为该const 分配空间。最简单的常见情况就是常量的值在编译时已知,而且不需要分配存储。―《C++ Program Language》

用const 声明的变量虽然增加了分配空间,但是可以保证类型安全。

C 标准中,const 定义的常量是全局的,C++中视声明位置而定。

用法2:指针和常量

使用指针时涉及到两个对象:该指针本身和被它所指的对象。将一个指针的声明用const “预先固定”将使那个对象而不是使这个指针成为常量。要将指针本身而不是被指对象声明为常量,必须使用声明运算符*const。

所以出现在* 之前的const 是作为基础类型的一部分:

char *const cp; //到char 的const 指针

char const *pc1; //到const char 的指针

const char *pc2; //到const char 的指针(后两个声明是等同的,见于string库函数)

从右向左读的记忆方式:

cp is a const pointer to char. 故pc 不能指向别的字符串,但可以修改其指向的字符串的内容pc2 is a pointer to const char. 故*pc2 的内容不可以改变,但pc2 可以指向别的字符串且注意:允许把非const 对象的地址赋给指向const 对象的指针,不允许把一个const 对象的地址赋给一个普通的、非const 对象的指针。

用法3:const 修饰函数传入参数

将函数传入参数声明为const,以指明使用这种参数仅仅是为了效率的原因,而不是想让调用函数能够修改对象的值。同理,将指针参数声明为const,函数将不修改由这个参数所指的对象。

通常修饰指针参数和引用参数:

void Fun(const A *in); //修饰指针型传入参数

void Fun(const A &in); //修饰引用型传入参数

用法4:修饰函数返回值

可以阻止用户修改返回值。返回值也要相应的付给一个常量或常指针。

用法5:const 修饰成员函数(c++特性)

const 对象只能访问const 成员函数,而非const 对象可以访问任意的成员函数,包括const 成员函数;const 对象的成员是不能修改的,而通过指针维护的对象确实可以修改的;const 成员函数不可以修改对象的数据,不管对象是否具有const 性质。编译时以是否修改成员数据为依据进行检查。

具体展开来讲:

(一). 常量与指针

常量与指针放在一起很容易让人迷糊。对于常量指针和指针常量也不是所有的学习C/C++的人都能说清除。例如:

const int *m1 = new int(10);

int* const m2 = new int(20);

在上面的两个表达式中,最容易让人迷惑的是const 到底是修饰指针还是指针指向的内存区域?其实,只要知道:const 只对它左边的东西起作用,唯一的例外就是const 本身就是最左边的修饰符,那么它才会对右边的东西起作用。根据这个规则来判断,m1 应该是常量指针(即,不能通过m1 来修改它所指向的内容。);而m2 应该是指针常量(即,不能让m2 指向其他的内存模块)。

由此可见:

1. 对于常量指针,不能通过该指针来改变所指的内容。即,下面的操作是错误的:

int i = 10;

const int *pi = &i;

*pi = 100;

因为你在试图通过pi 改变它所指向的内容。但是,并不是说该内存块中的内容不能被修改。我们仍然可以通过其他方式去修改其中的值。例如:

// 1: 通过i直接修改。

i = 100;

// 2: 使用另外一个指针来修改。

int *p = (int*)pi;//必须强制类型转换,否则const 无法赋值给非const

*p = 100;

实际上,在将程序载入内存的时候,会有专门的一块内存区域来存放常量。但是,上面的i本身不是常量,是存放在栈或者堆中的。我们仍然可以修改它的值。而pi 不能修改指向的值应该说是编译器的一个限制。

2. 根据上面const 的规则,const int *m1 = new int(10);我们也可写作:int const *m1 = new int(10); 这是,理由就不须作过多说明了。

3. 在函数参数中指针常量时表示不允许将该指针指向其他内容。

void func_02(int* const p)

{

int *pi = new int(100);

p = pi; //错误!P 是指针常量。不能对它赋值。

}

int main()

{

int* p = new int(10);

func_02(p);

delete p;

return 0;

}

4. 在函数参数中使用常量指针时表示在函数中不能改变指针所指向的内容。

void func(const int *pi)

{

//错误!不能通过pi 去改变pi 所指向的内容!

*pi = 100;

}

int main()

{

int* p = new int(10);

func(p);

delete p;

return 0;

}

我们可以使用这样的方法来防止函数调用者改变参数的值。但是,这样的限制是有限的,作为参数调用者,我们也不要试图去改变参数中的值。因此,下面的操作是在语法上是正确的,但是可能破还参数的值:

#include

# include

using namespace std;

void func(const int *pi)

{

//这里相当于重新构建了一个指针,指向相同的内存区域。当然就可以通过该指针修改内存中的值了。只是不能通过常量指针修改而已。

int* pp = (int*)pi;

*pp = 100;(牛逼)

}

int main()

{

int* p = new int(10);

cout << "*p = " << *p << endl;

func(p);

cout << "*p = " << *p << endl;

delete p;

return 0;

}

(二):常量与引用

常量与引用的关系稍微简单一点。因为引用就是另一个变量的别名,它本身就是一个常量。也就是说不能再让一个引用成为另外一个变量的别名, 那么他们只剩下代表的内存区域是否可变。即:

int i = 10;

const int& ri = i; // 正确:表示不能通过该引用去修改对应的内存的内容。

int& const rci = i; // 错误!不能这样写。

由此可见,如果我们不希望函数的调用者改变参数的值。最可靠的方法应该是使用引用。下面的操作会存在编译错误:

void func(const int& i)

{

i = 100; // 错误!不能通过i去改变它所代表的内存区域。

}

int main()

{

int i = 10;

func(i);

return 0;

}

这里已经明白了常量与指针以及常量与引用的关系。但是,有必要深入的说明以下。在

系统加载程序的时候,系统会将内存分为4个区域:堆区、栈区、全局区(静态)和代码区。从这里可以看出,对于常量来说,系统没有划定专门的区域来保护其中的数据不能被更改。也就是说,使用常量的方式对数据进行保护是通过编译器作语法限制来实现的。我们仍然可以绕过编译器的限制去修改被定义为“常量”的内存区域。看下面的代码:

const int i = 10; // 这里i 已经被定义为常量,但是我们仍然可以通过另外的方式去修改它的值。

int *pi = (int*) &i; // 这说明把i定义为常量,实际上是防止通过i去修改所代表的内存。(牛逼)

(三):常量函数

常量函数是C++对常量的一个扩展,它很好的确保了C++中类的封装性。在C++中,为了防止类的数据成员被非法访问,将类的成员函数分成了两类,一类是常量成员函数(也被称为观察着);另一类是非常量成员函数(也被成为变异者)。在一个函数的签名后面加上关键字const 后该函数就成了常量函数。对于常量函数,最关键的不同是编译器不允许其修改类的数据成员。

例如:

class Test

{

public:

void func() const;

private:

int intValue;

};

void Test::func() const

{

intValue = 100;

}

上面的代码中,常量函数func 函数内试图去改变数据成员intValue的值,因此将在编译的时候引发异常。

当然,对于非常量的成员函数,我们可以根据需要读取或修改数据成员的值。但是,这要依赖调用函数的对象是否是常量。通常,如果我们把一个类定义为常量,我们的本意是希望他的状态(数据成员)不会被改变。那么,如果一个常量的对象调用它的非常量函数会产生什么后果呢?看下面的代码:

class Fred{

public:

void inspect() const;

void mutate();

};

void UserCode(Fred& changeable, const Fred& unChangeable)

{

changeable.inspect(); // 正确,非常量对象可以调用常量函数。

changeable.mutate(); // 正确,非常量对象也允许修改调用非常量成员函数修改数据成员。

unChangeable.inspect(); // 正确,常量对象只能调用常理函数。因为不希望修改对象状态。

unChangeable.mutate(); // 错误!常量对象的状态不能被修改,而非常量函数存在修改对象状态的可能

}

从上面的代码可以看出,由于常量对象的状态不允许被修改,因此,通过常量对象调用非常量函数时将会产生语法错误。实际上,我们知道每个成员函数都有一个隐含的指向对象本身的this 指针。而常量函数则包含一个this 的常量指针。如下:

void inspect(const Fred* this) const;

void mutate(Fred* this);

也就是说对于常量函数,我们不能通过this 指针去修改对象对应的内存块。但是,在上面我们已经知道,这仅仅是编译器的限制,我们仍然可以绕过编译器的限制,去改变对象的状态。看下面的代码:

class Fred{

public:

void inspect() const;

private:

int intValue;

};

void Fred::inspect() const

{

cout << "At the beginning. intValue = "<< intValue << endl;

// 这里,我们根据this 指针重新定义了一个指向同一块内存地址的指针。

// 通过这个新定义的指针,我们仍然可以修改对象的状态。

Fred* pFred = (Fred*)this;

pFred->intValue = 50;

cout << "Fred::inspect() called. intValue = "<< intValue << endl;

}

int main()

{

Fred fred;

fred.inspect();

return 0;

}

上面的代码说明,只要我们愿意,我们还是可以通过常量函数修改对象的状态。同理,对于常量对象,我们也可以构造另外一个指向同一块内存的指针去修改它的状态。这里就不作过多描述了。

另外,也有这样的情况,虽然我们可以绕过编译器的错误去修改类的数据成员。但是C++也允许我们在数据成员的定义前面加上mutable,以允许该成员可以在常量函数中被修改。例如:

public:

void inspect() const;

private:

mutable int intValue;

};

void Fred::inspect() const

{

intValue = 100;

}

但是,并不是所有的编译器都支持mutable 关键字。这个时候我们上面的歪门邪道就有用了。

关于常量函数,还有一个问题是重载。

#include

#include

using namespace std;

class Fred{

public:

void func() const;

void func();

};

void Fred::func() const

{

cout << "const function is called."<< endl;

}

void Fred::func()

{

cout << "non-const function is called."<< endl;

}

void UserCode(Fred& fred, const Fred& cFred)

{

cout << "fred is non-const object, and the result of fred.func() is:" << endl;

fred.func();

cout << "cFred is const object, and the result of cFred.func() is:" << endl;

cFred.func();

}

int main()

{

UserCode(fred, fred);

return 0;

}

输出结果为:

fred is non-const object, and the result of fred.func() is:

non-const function is called.

cFred is const object, and the result of cFred.func() is:

const function is called.

从上面的输出结果,我们可以看出。当存在同名同参数和返回值的常量函数和非常量函数时,具体调用哪个函数是根据调用对象是常量对像还是非常量对象来决定的。常量对象调用常量成员;非常量对象调用非常量的成员。

总之,我们需要明白常量函数是为了最大程度的保证对象的安全。通过使用常量函数,我们可以只允许必要的操作去改变对象的状态,从而防止误操作对对象状态的破坏。但是,就像上面看见的一样,这样的保护其实是有限的。关键还是在于我们开发人员要严格的遵守使用规则。另外需要注意的是常量对象不允许调用非常量的函数。这样的规定虽然很武断,但如果我们都根据原则去编写或使用类的话这样的规定也就完全可以理解了。

(四):常量返回值

很多时候,我们的函数中会返回一个地址或者引用。调用这得到这个返回的地址或者引用后就可以修改所指向或者代表的对象。这个时候如果我们不希望这个函数的调用这修改这个返回的内容,就应该返回一个常量。这应

该很好理解,大家可以去试试。

+++++++++++++++++++++++++++++++++++++++

c++ 中const

+++++++++++++++++++++++++++++++++++++++

1. const 常量,如const int max = 100;

优点:const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误(边际效应)。

2. const 修饰类的数据成员。如:

class A

{

const int size;

}

const 数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const 数据成员的值可以不同。所以不能在类声明中初始化const 数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。

如:

class A

{

const int size = 100; //错误

int array[size]; //错误,未知的size

}

const 数据成员的初始化只能在类的构造函数的初始化表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现。如

class A

{…

enum {size1=100, size2 = 200 };

int array1[size1];

int array2[size2];

}

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

3. const 修饰指针的情况,见下式:

int b = 500;

const int* a = &b [1]

int const *a = &b [2]

int* const a = &b [3]

const int* const a = &b [4]

如果你能区分出上述四种情况,那么,恭喜你,你已经迈出了可喜的一步。不知道,也没关系,我们可以参考《Effectivec++》Item21上的做法,如果const 位于星号的左侧,则const 就是用来修饰指针所指向的变量,即指针指向为常量;如果const 位于星号的右侧,const 就是修饰指针本身,即指针本身是常量。因此,[1]和[2]的情况相同,都是指针所指向的内容为常量(const 放在变量声明符的位置无关),这种情况下不允许对内容进行更改操作,如不能*a = 3;[3]为指针本身是常量,而指针所指向的内容不是常量,这种情况下不能对指针本身进行更改操作,如a++是错误的;[4]为指针本身和指向的内容均为常量。

4. const 的初始化

先看一下const 变量初始化的情况:

1) 非指针const 常量初始化的情况:

A b;

const A a = b;

2) 指针const 常量初始化的情况:

A* d = new A();

const A* c = d;

或者:const A* c = new A();

3)引用const 常量初始化的情况:

A f;

const A& e = f ; // 这样作e 只能访问声明为const 的函数,而不能访问一般的成员函数;

[思考1]:以下的这种赋值方法正确吗?

const A* c=new A();

A* e = c; //错误:无法从const常量变为非const常量,需要强制类型转化:正解为A*e=(A*)c

[思考2]:以下的这种赋值方法正确吗?

A* const c = new A();

A* b = c; //正确

5. 另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中,const 可以修饰函数的返回值,或某个参数;对于成员函数,还可以修饰是整个函数。有如下几种情况,以下会逐渐的说明用法:

A&operator=(const A& a);

void fun0(const A* a );

void fun1( ) const; // fun1( ) 为类成员函数

const A fun2( );

1)修饰参数的const,如void fun0(const A* a ); void fun1(const A& a);

调用函数的时候,用相应的变量初始化const 常量,则在函数体中,按照const 所修饰的部分进行常量化,如形参为const A*a,则不能对传递进来的指针的内容进行改变,保护了原指针所指向的内容;如形参为const A&a,则不能对传递进来的引用对象进行改变,保护了原对象的属性。

[注意]:参数const 通常用于参数为指针或引用的情况,且只能修饰输入参数; 若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用const 修饰。

[总结]对于非内部数据类型的输入参数,因该将“值传递”的方式改为“const引用传递”,目的是为了提高效率。例如,将void Func(A a)改为void Func(const A &a)。对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如

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

2)修饰返回值的const,如const A fun2( ); const A* fun3( );

这样声明了返回值后,const 按照"修饰原则"进行修饰,起到相应的保护作用。

const Rational operator*(const Rational& lhs, const Rational& rhs)

{

return Rational(lhs.numerator() * rhs.numerator(),

lhs.denominator() * rhs.denominator());

}

返回值用const 修饰可以防止允许这样的操作发生:

Rational a,b;

Radional c;

(a*b) = c;

一般用const 修饰返回值为对象本身(非引用和指针)的情况多用于二目操作符重载函数并产生新对象的时候。

[总结]

1.一般情况下,函数的返回值为某个对象时,如果将其声明为const 时,多用于操作符的重载。通常,不建议用const 修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下:如果返回值为某个对象为const(const A test = A 实例)或某个对象的引用为const (const A& test = A 实例),则返回值具有const 属性,则返回实例只能访问类A 中的公有(保护)数据成员和const 成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到。

2.如果给采用“指针传递”方式的函数返回值加const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。如:const char * GetString(void);

如下语句将出现编译错误:

char *str=GetString();

正确的用法是:

const char *str=GetString();

3.函数返回值采用“引用传递”的场合不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。如:

class A

{

A &operate = (const A &other); //赋值函数

}

A a,b,c; //a,b,c 为A 的对象

a=b=c; //正常

(a=b)=c; //不正常,但是合法

若赋值函数的返回值加const 修饰,那么该返回值的内容不允许修改,上例中a=b=c 依然正确。(a=b)=c就不正确了。

[思考3]:这样定义赋值操作符重载函数可以吗?

const A& operator=(const A& a);

6.类成员函数中const 的使用

一般放在函数体后,形如:void fun() const; 任何不会修改数据成员的函数都因该声明为const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其他非const 成员函数,编译器将报错,这大大提高了程序的健壮性。如:

class Stack

{

public:

void Push(int elem);

int Pop(void);

int GetCount(void) const; //const 成员函数

private:

int m_num;

int m_data[100];

};

int Stack::GetCount(void) const

{

++m_num; //编译错误,企图修改数据成员m_num

Pop(); //编译错误,企图调用非const 函数

Return m_num;

}

7. 使用const 的一些建议

1) 要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;

2) 要避免最一般的赋值操作错误,如将const 变量赋值,具体可见思考题;

3) 在参数中使用const 应该使用引用或指针,而不是一般的对象实例,原因同上;

4) const 在成员函数中的三种用法(参数、返回值、函数)要很好的使用;

5) 不要轻易的将函数的返回值类型定为const;

6) 除了重载操作符外一般不要将返回值类型定为对某个对象的const 引用;

[思考题答案]

1) 这种方法不正确,因为声明指针的目的是为了对其指向的内容进行改变,而声明的指针e 指向的是一个常量,所以不正确;

2) 这种方法正确,因为声明指针所指向的内容可变;

3) 这种做法不正确;在const A::operator=(const A& a)中,参数列表中的const 的用法正确,而当这样连续赋值的时侯,问题就出现了:

A a,b,c:

(a=b)=c;

因为a.operator=(b)的返回值是对a 的const 引用,不能再将c 赋值给const

常量。

++++++++++++++++++++++++++++++++++++++++

const 在c 和c++中的区别

++++++++++++++++++++++++++++++++++++++++

1. C++中的const 正常情况下是看成编译期的常量,编译器并不为const 分配空间,只是在编译的时候将值保存在名字表中,并在适当的时候折合在代码中.所以,以下代码:

using namespace std;

int main()

{

const int a = 1;

const int b = 2;

int array[ a + b ] = {0};

for (int i = 0; i < sizeof array / sizeof *array; i++)

{

cout << array << endl;

}

}

在可以通过编译,并且正常运行.但稍加修改后,放在C 编译器中,便会出现错误:

int main()

{

int i;

const int a = 1;

const int b = 2;

int array[ a + b ] = {0};

for (i = 0; i < sizeof array / sizeof *array; i++)

{

printf("%d",array);

}

}

错误消息:

c:\test1\te.c(8): error C2057: 应输入常数表达式

c:\test1\te.c(8): error C2466: 不能分配常数大小为0 的数组

出现这种情况的原因是:在C 中const 是一个不能被改变的普通变量,既然是变量,就要占用存储空间,所以编译器不知道编译时的值.而且,数组定义时的下标必须为常量。

2. 在C 语言中: const int size; 这个语句是正确的,因为它被C 编译器看作一个声明,指明在别的地方分配存储空间.但在C++中这样写是不正确的.C++中const 默认是内部连接,如果想在C++中达到以上的效果,必须要用extern关键字.即C++中,const 默认使用内部连接.而C 中使用外部连接。

(1) 内连接:编译器只对正被编译的文件创建存储空间,别的文件可以使用相同的表示符或全局变量.C/C++中内连接使用static 关键字指定.

(2) 外连接:所有被编译过的文件创建一片单独存储空间.一旦空间被创建,连接器必须解决对这片存储空间的引用.全局变量和函数使用外部连接.通过extern 关键字声明,可以从其他文件访问相应的变量和函数.

/* C++代码header.h */

const int test = 1;

/* C++代码test1.cpp */

#include "header.h"

using namespace std;

int main() { cout << "in test1 :" << test << endl; }

/* C++代码test2.cpp */

#include "header.h"

using namespace std;

void print() { cout << "in test2:" << test << endl;}

以上代码编译连接完全不会出问题,但如果把header.h 改为:

extern const int test = 1;

在连接的时候,便会出现以下错误信息:

test2 error LNK2005: "int const test" (?test@@3HB) 已经在test1.obj 中定义因为extern 关键字告诉C++编译器test 会在其他地方引用,所以,C++编译器就会为test 创建存储空间,不再是简单的存储在名字表里面.所以,当两个文件同时包含header.h 的时候,会发生名字上的冲突. 此种情况和C 中const 含义相似:

/* C 代码header.h */

const int test = 1;

/* C 代码test1.c */

#include "header.h"

int main() { printf("in test1:%d\n",test); }

/* C 代码test2.c */

#include "header.h"

void print() { printf("in test2:%d\n",test); }

错误消息:

test3 fatal error LNK1169: 找到一个或多个多重定义的符号

test3 error LNK2005: _test 已经在test1.obj 中定义

也就是说:在c++ 中const 对象默认为文件的局部变量。与其他变量不同,除非特别说明,在全局作用域声明的const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。通过指定const 变更为extern,就可以在整个程序中访问const 对象:

// file_https://www.doczj.com/doc/632377398.html,

// defines and initializes a const that is accessible to other files

extern const int bufSize = fcn();

// file_https://www.doczj.com/doc/632377398.html,

extern const int bufSize; // uses bufSize from file_1

// uses bufSize defined in file_1

for (int index = 0; index != bufSize; ++index)

// ...

3. C++中,是否为const 分配空间要看具体情况.如果加上关键字extern 或者取const 变量地址,则编译器就要为const 分配存储空间.

4. C++中定义常量的时候不再采用define,因为define 只做简单的宏替换,并不提供类型检查.

Const 在C/C++中的区别

在C 语言中,被const 修饰的就是常量吗?真的能不动如山吗?编译如下代码,你觉得如何?

const int a = 10;

int *pa = &a;

*pa = 20;

printf("%d\n",*pa);

编译器并不会像你想的那样会报错,有时候会有一个警告,有什么可能连警告都没有。你运行时才发现,a 的值已经被改掉了。

关键字const 并不能把变量变成常量!在一个符号前面加上限定符const 只是表示不能被赋值,换句话说,它的值对这个符号来说是只读的,但是并不能防止通过程序的内部(或者说外部)的方法来修改这个值。

所在C 语言中被const 修饰的变量只是一个只读的变量而已。如:const int n = 100; int a[n]; (C 不提倡这样写,可以用宏代替)但是在C++中不是这样的,在C++中被const 的修饰毫无疑问是常量,它的值在编译时就被确定了。

const int a = 10;

int *pa = &a;

*pa = 20;

printf("%d\n",*pa);

上面代码在C++中是错误的,肯定会出现编译错误。

而下面代码在C++中是很普遍的。

const int n = 100;

int a[n];

而且这样的表达是很普遍的很值得提倡的表示法(C++中不提倡使用宏,宏可以被常量、内联函数替代)。

在C++中的const 属性是可以被去掉的,通过const_cast 就可以强转掉。

你试过这样的代码吗?

#include

using namespace std;

int main()

{

const int a = 10;

const int *pa = &a;

int* pb = const_cast(pa);

*pb = 20;

cout << *pa << endl;

cout << *pb << endl;

cout << a << endl;

return 0;

}

Typedef 让人晕有如下的代码:(注意)

typedef char * pStr;

char string[4] = "abc";

const char *p1 = string;

const pStr p2 = string;

p1++;

p2++;

你觉得上面代码会正确运行吗?答案是不会。P2 是一个常指针,并不是你想的那样是一个指向常量的指针。为什么呢?

const char a; a 不变; const char *a; *a 不变(当然a 是可变的)。Const newType a; a 不变,而上面的typedef 定义了一种新类型,所以pStr 是一种新类型,因此const 是修饰p2,p2 是不可变的,p2 是有错误的。

const 修饰什么?

1、const 在前面

const int nValue;//nValue 是const

const char *pContent; //*pContent 是const, pContent 可变

const (char *) pContent; //pContent 是const,*pContent 可变

char* const pContent; //pContent 是const,*pContent 可变

const char* const pContent; //pContent 和*pContent 都是const

2 、const 在后面

int const nValue;// nValue 是const

char const * pContent;// *pContent 是const, pContent 可变

(char *) const pContent;//pContent 是const,*pContent 可变

char* const pContent;// pContent 是const,*pContent 可变

char const* const pContent;// pContent 和*pContent 都是const

怎么判断const 修饰什么呢,很简单,沿着星号画条线,const 在前面,那就是修饰类型了,const 在后面就修饰变量名了。

需要注意:对于const (char *) ; 因为char *是一个整体,相当于一个类型(如char),因此,这是限定变量名是const 。

C语言中的32个关键字及其意思

由ANSI标准定义的C语言关键字共32个: auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if while static 一、数据类型关键字(12个): 1、char [t?ɑ:]:声明字符型变量或函数 (1)主要内容字符:容纳单字符的一种基本数据类型;(2)n.炭;女清洁工 vt. 烧焦; (3)字符类型:字符型(Char) c、字符串型(String) s 、二进制型(Binary) bn、布尔型(Boolean) b 、日期时间型(DateTime) d 、数组型(Array) a、象型(Object) o 、循环控制变量通常使用单一的字符; 2、double [?d?b?l] :声明双精度变量或函数 (1)n. 两倍;(2)a. 两倍的,双重的;(3)v. 加倍的,快步走,加倍努力 3、enum :声明枚举类型 (1)枚举:枚举是一个被命名的整型常数的;(2)枚举类型;(3)列举型; (4)列举enumerate [i?nju:m?reit] 4、float [fl?ut] :声明浮点型变量或函数 (1)浮点数、(2)浮点型、(3)漂浮、(4)浮动 5、int[int]:声明整型变量或函数 (1)符号整数、(2)取整、(3)Int是 integer ['intid??] 的简写 int 声明一个变量为整型。占2个字节,最大表示范围:-32768到32767(十进制)。 long 声明一个变量为长整型。长整型变量占4个字节,最大表示范围: -2147483648(十进制)到2147483647(十进制)。 6、long [l??] :声明长整型变量或函数 (1)长整型(2)a./ ad.长(期)的(地)(3) n.长时间(4)vi.渴望 7、short [??:t] :声明短整型变量或函数 (1)a. 短的,矮的、(2)n. 短裤、(3)adv. 短暂地;突然地,急地 8、signed:声明有符号类型变量或函数 (1)有符号的、(2)带正负号、(3)sign [sain] n.标记,符号;招牌;迹象 v.签(署) 9、struct:声明结构体变量或函数 (1)n.结构(2)结构体(4)创建构架数组(3)structural[?str?kt??r?l]a. 结构的 10、union [?ju:ni?n]:声明共用体(联合)数据类型 (1)联合、(2)n.工会,联盟、(3)合并、(4)团结 11、unsigned [?n'saind]:声明无符号类型变量或函数 (1)无符号的 (1)无符号的 12、void [v?id] :声明函数无返回值或无参数,声明无类型指针(基本上就 这三个作用) (1)a.无效的、(2)没有的、(3)vt.使无效、(4)n.空虚感 二、控制语句关键字(12个):

C语言的标识符和关键字

1、标识符 标识符使用来标识源程序中的某个对象的名字的,这些对象可以是语句、数据类型、函数、变量、常量、数组等。 C语言规定:一个标识符由字母、数字和下划线组成,第一个字符必须是字母或下划线,通常以下划线开头的标识符是编译系统专用的,所以在编写C语言程序时,最好不要使用以下划线开头的标识符。但是下划线可以用在第一个字符以后的任何位置。 标识符的长度不要超过32个字符,尽管C语言规定标识符的长度最大可达255个字符,但是在实际编译时,只有前面32个字符能够被正确识别。对于一般的应用程序来说,32个字符的标示符长度足够用了。 C语言对大小写字符敏感,所以在编写程序时要注意大小写字符的区分。例如:对于sec和SEC这两个标识符来说,C语言会认为这是两个完全不同的标识符。 C语言程序中的标识符命名应做到简洁明了、含义清晰。这样便于程序的阅读和维护。例如在比较最大值时,最好使用max来定义该标识符。 2、关键字 在c语言编程中,为了定义变量、表达语句功能和对一些文件进行预处理,还必须用到一些具有特殊意义的字符,这就是关键字。 关键字已被编译系统本身使用,所以用户编写程序时不能够使用这些关键字来作为标识符。C语言中关键字主要有以下三类: ?类型说明符:用来定义变量、函数或其他数据结构的类型,如unsigned char,int,long等 ?语句定义符:用来标示一个语句的功能,如if,for等 ?预处理命令字:用来表示预处理命令的关键字,如include,define等

标准C语言有32个关键字,c51编译器又扩充了一些关键字,这些关键字在编程时需要注意,绝对不能使用这些关键字来定义标识符。

const int

const int* a = &b 和const* int a = &b的区别收藏 如果const关键字不涉及到指针,我们很好理解,下面是涉及到指针的情况: int b = 500; const int* a = &b; [1] int const *a = &b; [2] int* const a = &b; [3] const int* const a = &b; [4] 如果你能区分出上述四种情况,那么,恭喜你,你已经迈出了可喜的一步。不知道,也没关系,我们可以参考《Effective c++》Item21上的做法,如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。因此,[1]和[2]的情况相同,都是指针所指向的内容为常量(const放在变量声明符的位置无关),这种情况下不允许对内容进行更改操作,如不能*a = 3 ;[3]为指针本身是常量,而指针所指向的内容不是常量,这种情况下不能对指针本身进行更改操作,如a++是错误的;[4]为指针本身和指向的内容均为常量。另外const 的一些强大的功能在于它在函数声明中的应用。在一个函数声明中,const 可以修饰函数的返回值,或某个参数;对于成员函数,还可以修饰是整个函数。有如下几种情况,以下会逐渐的说明用法: A& operator=(const A& a); void fun0(const A* a ); void fun1( ) const; // fun1( ) 为类成员函数 const A fun2( ); --------------------------------------------------------------------------------------------------------------------------------------------- const int * pi 、int const * pi与int * const pi及其操作 (本贴已经做了重大修改) 1 从const int i 说起

const,static,extern用法总结

--------------------------CONST--------------------------------------- const应用: 一、对于基本声明 const int r=100;//标准const变量声明加初始化,编译器经过类型检查后直接用100在编译时替换。 二、对于指针 1. int x=10; const int *r=&x; //指针指向的内容是常量,r指向的内容不能够通过r改变,但如果是非const,内容可以通过自己改变,而且r指针可以改变,可以指向其它的整形. //*r=*r+1;NO //x++;YES //r=&y;YES 2. int const *r=&x; 与1完全相同 3. int * const r=&x; //指针指向是常量,不能修改去指向其它内容,但指向的内容可以修改 //r=&y;NO //*r=*r+1;YES //x++;YES 4.const int * const r=&x; //综合1、3用法,r是一个指向常量的常量型指针,指针指向不能改变,指针内容不能改变,内容可以自身改变 //r=&y;NO //*r=*r+1;NO //x++;YES 三、对于类型检查 可以把非const对象赋予const指针,这样就不能改变.但是不能把const赋给非const,除非先强制转换 const int x=100; int *p=(int*)&x; *p++; 四、对于函数 1.void Fuction1(const int r); //此处为参数传递const值,意义是变量初值不能被函数改变 2.const int Fuction1 (int); //此处返回const值,意思指返回的原函数里的变量的初值不能被修改,但是函数按值返回的这个变量被制成副本,能不能被修改就没有了意义,它可以被赋给任何的const或非const类型变量,完全不需要加上这个const关键字。 3.Class CX; //内部有构造函数,声明如CX(int r =0) CX Fuction1 () { return CX(); } const CX Fuction2 () { return CX(); } Fuction1() = CX(1); //没有问题,可以作为左值调用 Fuction2() = CX(1); //编译错误,const返回值禁止作为左值调用。 4.函数中指针的const传递和返回: int F1 (const char *pstr); //作为传递的时候使用const修饰可以保证不会通过这个指针来修改传递参数的初值 const char *F2();//意义是函数返回的指针指向的对象是一个const对象,它必须赋给一个同样是指向const对象的指针 const char * const F3(); //比上面多了一个const,这个const的意义只是在他被用作左值时有效,它表明了这个指针除了指向const对象外,它本身也不能被修改,所以就不能当作左值来处理。 五、对于类 1.首先,对于const的成员变量,只能在构造函数里使用初始化成员列表来初始化,试图在构造函数体内进行初始化const成员变量会引起编译错误。初始化成员列表形如:X:: X ( int ir ): r(ir) {} //假设r是类X的const成员变量 注意:类的构造和析构函数都不能是const函数。 2.建立了一个const成员函数,但仍然想用这个函数改变对象内部的数据。(函数不能修改类的数据成员)

.net高级工程师面试题

.net软件工程师面试题 基础题: 1.简述string[]、ArrayList、List的区别。 数组: 优点: o数组在内存中是连续存储的,索引速度非常快; o赋值和修改元素也很简单; 不足: o两个数据之间插入数据比较麻烦; o声明数组的时候,必须指明数组的长度,数组长度过长会造成内存浪费,数组的长度过短,会造成数据溢出错误。 ArrayList: 优点: https://www.doczj.com/doc/632377398.html, framework 提供的用于数据存储和检索的专用类 o大小依据存储的数据来动态扩展和收缩 o继承IList,可以方便的进行数据的添加、插入和删除 缺点: o允许插入不同类型的数据,都当做object类型处理 o数据处理时可能出现类型不匹配的错误

o存在装箱(值=》引用)拆箱的操作,会带来很大的性能损耗 List: 优点: o声明List集合时,需要声明集合内数据的对象类型 o避免了类型安全问题和装箱拆箱的性能问题 2.简述装箱和拆箱操作中的性能损耗是如何产生的。 3.简述对https://www.doczj.com/doc/632377398.html,中的事件机制的理解。 4.在一个https://www.doczj.com/doc/632377398.html,的三层结构系统中,以登录操作为例,简述在各层中如何组织代码。 5.简述相比DATASET,实体类在WEB项目中的优点与缺点。 6.简述GC是如何工作的。 Java采用VM(Virtual Machine)机制,由VM来管理程序的运行当然也包括对GC管理。90年代末期.NET出现了,.NET采用了和Java类似的方法由CLR(Common Language Runtime)来管理。 Garbage Collector(垃圾收集器,在不至于混淆的情况下也成为GC)以应用程序的root 为基础,遍历应用程序在Heap上动态分配的所有对象[2],通过识别它们是否被引用来确定哪些对象是已经死亡的、哪些仍需要被使用。已经不再被应用程序的root或者别的对象所引用的对象就是已经死亡的对象,即所谓的垃圾,需要被回收。这就是GC工作的原理。为了实现这个原理,GC有多种算法。比较常见的算法有Reference Counting,Mark Sweep,Copy Collection等等。目前主流的虚拟系统.NET CLR,Java VM和Rotor都是采用的Mark Sweep算法。

C语言 个关键字九种控制语句 种运算符

总结归纳了C语言的32个关键字 第一个关键字:auto 用来声明自动变量。可以显式的声明变量为自动变量。只要不是声明在所有函数之前的变量,即使没加auto关键字,也默认为自动变量。并且只在声明它的函数内有效。而且当使用完毕后,它的值会自动还原为最初所赋的值。自动变量使用时要先赋值,因为其中包含的是未知的值。 例:auto int name=1; 第二个关键字:static 用来声明静态变量。可以显式的声明变量为静态变量。也为局部变量。只在声明它的函数内有效。它的生命周期从程序开始起一直到程序结束。而且即使使用完毕后,它的值仍旧不还原。即使没有给静态变量赋值,它也会自动初始化为0. 例:static int name=1. 第三个关键字:extern 用来声明全局变量。同时声明在main函数之前的变量也叫全局变量。它可以在程序的任何地方使用。程序运行期间它是一直存在的。全局变量也会初始化为0. 例:extern int name; 第四个关键字:register 用来声明为寄存器变量。也为局部变量,只在声明它的函数内有效。它是保存在寄存器之中的。速度要快很多。对于需要频繁使用的变量使用它来声明会提高程序运行速度。 例:register int name=1; 第五个关键字:int 用来声明变量的类型。int为整型。注意在16位和32位系统中它的范围是不同的。16位中占用2个字节。32位中占用4个字节。还可以显式的声明为无符号或有符号: unsigned int或signed int .有符号和无符号的区别就是把符号位也当作数字位来存储。也可用short和long来声明为短整型,或长整行。 例:int num; 第六个关键字:float 用来声明变量的类型。float为浮点型,也叫实型。它的范围固定为4个字节。其中6位为小数位。其他为整数位。 例:float name;

C++中const用法详解

const是一个C语言的关键字,它限定一个变量不允许被改变。使用const在一定程度上可以提高程序的安全性和可靠性。类型声明中const用来修饰一个常量,有如下两种写法,那么,请问,下面分别用const限定不可变的内容是什么? 1)、const在前面 const int nValue;//nValue是const const char *pContent; //*pContent是const, pContent可变 const (char *) pContent;//pContent是const,*pContent可变 char* const pContent; //pContent是const,*pContent可变 const char* const pContent; //pContent和*pContent都是const 2)、const在后面,与上面的声明对等 int const nValue;// nValue是const char const * pContent;// *pContent是const, pContent可变 (char *) const pContent;//pContent是const,*pContent可变 char* const pContent;// pContent是const,*pContent可变 char const* const pContent;// pContent和*pContent都是const 答案与分析: const和指针一起使用是C语言中一个很常见的困惑之处,在实际开发中,特别是在看别人代码的时候,常常会因为这样而不好判断作者的意图,下面讲一下我的判断原则:当const所在代码段中不包含括号时,沿着*号划一条线,如果const 位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。你可以根据这个规则来看上面声明的实际意义,相信定会一目了然。

c语言关键字的用法详解

1. Static用法 1.1 static声明的变量在C语言中有两方面的特征: 1)、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。 2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。 1.2 特点 A.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度; B.若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度; C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题; D.如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带“内部存储器”功能的的函数) E.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static 的局部变量的地址作为返回值,若为auto类型,则返回为错指针。 函数前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。使用内部函数的好处是:不同的人编写不同的函数时,不用担心自己定义的函数,是否会与其它文件中的函数同名。 扩展分析:术语static有着不寻常的历史.起初,在C中引入关键字static是为了表示退出一个块后仍然存在的局部变量。随后,static在C中有了第二种含义:用来表示不能被其它文件访问的全局变量和函数。为了避免引入新的关键字,所以仍使用static关键字来表示这第二种含义。最后,C++重用了这个关键字,并赋予它与前面不同的第三种含义:表示属于一个类而不是属于此类的任何特定对象的变量和函数(与Java中此关键字的含义相同)。 1.3 关键字static的作用是什么? 这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:

const和readonly区别

我们都知道,const和static readonly的确很像:通过类名而不是对象名进行访问,在程序中只读等等。在多数情况下可以混用。二者本质的区别在于,const的值是在编译期间确定的,因此只能在声明时通过常量表达式指定其值。而static readonly是在运行时计算出其值的,所以还可以通过静态构造函数来赋值。明白了这个本质区别,我们就不难看出下面的语句中static readonly和const能否互换了: 1. static readonly MyClass myins = new MyClass(); 2. static readonly MyClass myins = null; 3. static readonly B = 10; static readonly A = B * 20; 4. static readonly int [] constIntArray = new int[] {1, 2, 3}; 5. void SomeFunction() { const int a = 10; ... } 6.private static string astr="abcd"; private const string str = astr+"efg"; 1:不可以换成const。new操作符是需要执行构造函数的,所以无法在编译期间确定 2:可以换成const。我们也看到,Reference类型的常量(除了String)只能是Null。 3:可以换成const。我们可以在编译期间很明确的说,A等于200。 4:不可以换成const。道理和1是一样的,虽然看起来1,2,3的数组的确就是一个常量。5:不可以换成readonly,readonly只能用来修饰类的field,不能修饰局部变量,也不能修饰property等其他类成员。 6.错误:如果在astr前加上const或者const改为readonly即可; 总结:1.const、readonly和static readonly定义的常量,指定初始值后(包括在构造函数内指定的初始值) 将不可更改,可读不可写; 2.const定义时必须指定初始值,而readonly定义时可以不进行初始化(MS建议在定义时初始值),同时也可以在构造函数内指定初始值, 并以构造函数内指定的值为准; 3.const和static readonly定义的常量是静态的,只能由类直接访问;而readonly定义的常量是非静态的,只能由实例对象访问; 4.static readonly常量,如果在构造函数内指定初始值,则必须是静态无参构造函数; 5.const是编译时常量,readonly是运行时常量;cosnt较高效,readonly较灵活。在应用上以static readonly代替const,以平衡const在灵活性上的不足, 同时克服编译器优化cosnt性能,所带来的程序集引用不一致问题; 文章2:

c语言数据类型关键字

1 数据类型关键字(12个): (1) char :声明字符型变量或函数 (2) double :声明双精度变量或函数 (3) enum :声明枚举类型 (4) float:声明浮点型变量或函数 (5) int:声明整型变量或函数 (6) long :声明长整型变量或函数 (7) short :声明短整型变量或函数 (8) signed:声明有符号类型变量或函数 (9) struct:声明结构体变量或函数 (10) union:声明联合数据类型 (11) unsigned:声明无符号类型变量或函数 (12) void :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)(2)控制语句关键字(12个): A循环语句 (1) for:一种循环语句(可意会不可言传) (2) do :循环语句的循环体 (3) while :循环语句的循环条件 (4) break:跳出当前循环 (5) continue:结束当前循环,开始下一轮循环 B条件语句 (1)if: 条件语句 (2)else :条件语句否定分支(与if 连用) (3)goto:无条件跳转语句 C开关语句 (1)switch :用于开关语句 (2)case:开关语句分支 (3)default:开关语句中的“其他”分支 D return :子程序返回语句(可以带参数,也看不带参数) 3 存储类型关键字(4个) (1)auto :声明自动变量一般不使用 (2)extern:声明变量是在其他文件正声明(也可以看做是引用变量) (3)register:声明积存器变量 (4)static :声明静态变量 4 其它关键字(4个): (1)const :声明只读变量 (2)sizeof:计算数据类型长度 (3)typedef:用以给数据类型取别名(当然还有其他作用 (4)volatile:说明变量在程序执行中可被隐含地改变

C基础知识25个常见问题

C# 基础知识 25个常见问题(1) 2007-04-12 16:53 当初学 C# 时是找个人大概问了一下数据类型和分支语句就开始做项目了。这两天又全面的看了一下相关的基础知识(学而时习之嘛),总结了25个问题: 1.静态变量和非静态变量的区别? 2.const 和 static readonly 区别? 3.extern 是什么意思? 4.abstract 是什么意思? 5.internal 修饰符起什么作用? 6.sealed 修饰符是干什么的? 7.override 和 overload 的区别? 8.什么是索引指示器? 9.new 修饰符是起什么作用? 10.this 关键字的含义? 11.可以使用抽象函数重写基类中的虚函数吗? 12.密封类可以有虚函数吗? 13.如果基类中的虚属性只有一个属性访问器,那么继承类重写该属性后可以有几个属性访问器?如果基类中有 get 和 set 两个呢? 14.abstract 可以和 virtual 一起使用吗?可以和 override 一起使用吗? 15.接口可以包含哪些成员? 16.类和结构的区别? 17.接口的多继承会带来哪些问题? 18.抽象类和接口的区别? 19.别名指示符是什么? 20.如何释放非托管资源? 21.P/Invoke是什么? 22.StringBuilder 和 String 的区别? 23.explicit 和 implicit 的含义? 24.params 有什么用? 25.什么是反射? 以下是我做的一份参考答案(C# 语言范畴之内),如果有不准确、不全面的,欢迎各位朋友指正! 1.静态变量和非静态变量的区别? 答:静态变量: 静态变量使用 static 修饰符进行声明 在所属类被装载时创建 通过类进行访问

const变量使用总结

或许还有不少人对于const修饰符理解的并不深刻,都只是停留在一个比较浅的层面上,仅仅是在读别人代码的时候看到了const修饰符的使用,自己的写代码的过中从未使用过,所以自然对于const修饰符比较陌生。那么到底什么是const 修饰符,我们在自己编写C语言代码的过程中又该如何有效的使用const修饰符呢,现在让我们来学习下const修饰符的使用。 const在C语言中算是一个比较新的描述符,我们称之为常量修饰符,即就是说其所修饰的对象为常量。当你代码中想要设法阻止一个变量被改变,那么这个时候可以选择使用const关键字。在你给一个变量加上const修饰符的同时,通常需要对它进行初始化,在之后的程序中就不能再去改变它。 可能有的人会有一个疑问,我们不是有在C中有预处理指令#define VariableNameVariableValue可以很方便地进行值替代,干嘛还要引入const修饰符呢?!这是因为预处理语句虽然可以很方便的进行值得替代,但它有个比较致命的缺点,即预处理语句仅仅只是简单值替代,缺乏类型的检测机制。这样预处理语句就不能享受C编译器严格类型检查的好处,正是由于这样,使得它的使用存在着一系列的隐患和局限性。 在讲解const修饰符之前,我们在此首先给出const修饰符的几个典型作用: 1. const类型定义:指明变量或对象的值是不能被更新,引入目的是为了取代预编译指令 2. 可以保护被修饰的东西,防止意外的修改,增强程序的健壮性; 3. 编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。 4. 可以节省空间,避免不必要的内存分配。 接下来看看具体的使用。 一、const修饰符在函数体内修饰局部变量。 constint n=5; 和 intconst n=5;

C++ const 精髓

1.const的用法: 看到const 关键字,C++程序员首先想到的可能是const 常量。这可不是良好的条件反射。如果只知道用const 定义常量,那么相当于把火药仅用于制作鞭炮。const 更大的魅力是它可以修饰函数的参数、返回值,甚至函数的定义体。 const 是constant 的缩写,“恒定不变”的意思。被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。所以很多C++程序设计书籍建议:“Use const whenever you need”。 1.用const 修饰函数的参数 如果参数作输出用,不论它是什么数据类型,也不论它采用“指针传递”还是“引用传递”,都不能加const 修饰,否则该参数将失去输出功能。const 只能修饰输入参数: 如果输入参数采用“指针传递”,那么加const 修饰可以防止意外地改动该指针,起到保护作用。 例如StringCopy 函数: void StringCopy(char *strDestination, const char *strSource); 其中strSource 是输入参数,strDestination 是输出参数。给strSource 加上const修饰后,如果函数体内的语句试图改动strSource 的内容,编译器将指出错误。 如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加const 修饰。 例如不要将函数void Func1(int x) 写成void Func1(const int x)。 同理不要将函数void Func2(A a) 写成void Func2(const A a)。其中A 为用户自定义的数据类型。 对于非内部数据类型的参数而言,象void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产生A 类型的临时对象用于复制参数a,而临时对象的构造、复制、析构过程都将消耗时间。 为了提高效率,可以将函数声明改为void Func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。但是函数void Func(A & a) 存在一个缺点: “引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为void Func(const A &a)。 以此类推,是否应将void Func(int x) 改写为void Func(const int &x),以便提高效率?完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。 问题是如此的缠绵,我只好将“const &”修饰输入参数的用法总结一下。 对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。 对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。

C语言32个关键字

一、数据类型关键字(12个): 1、char [t?ɑ:]:声明字符型变量或函数 (1)主要内容字符:容纳单字符的一种基本数据类型;(2)n.炭;女清洁工vt.烧焦;(3)字符类型:字符型(Char) c、字符串型(String) s 、二进制型(Binary) bn、布尔型(Boolean) b 、日期时间型(DateTime) d 、数组型(Array) a、象型(Object) o 、循环控制变量通常使用单一的字符; 2、double [?d?b?l] :声明双精度变量或函数 (1)n. 两倍;(2)a. 两倍的,双重的;(3)v. 加倍的,快步走,加倍努力 3、enum :声明枚举类型 (1)枚举:枚举是一个被命名的整型常数的;(2)枚举类型;(3)列举型; (4)列举enumerate [i?nju:m?reit] 4、float [fl?ut] :声明浮点型变量或函数 (1)浮点数、(2)浮点型、(3)漂浮、(4)浮动 5、int[int]:声明整型变量或函数 (1)符号整数、(2)取整、(3)Int是integer ['intid??] 的简写 6、long [l??] :声明长整型变量或函数 (1)长整型(2)a./ ad.长(期)的(地)(3)n.长时间(4)vi.渴望 7、short [??:t] :声明短整型变量或函数 (1)a. 短的,矮的、(2)n. 短裤、(3)adv. 短暂地;突然地,急地 8、signed:声明有符号类型变量或函数 (1)有符号的、(2)带正负号、(3)sign [sain] n.标记,符号;招牌;迹象v.签(署) 9、struct:声明结构体变量或函数 (1)n.结构(2)结构体(4)创建构架数组(3)structural[?str?kt??r?l]a.结构的 10、union [?ju:ni?n]:声明共用体(联合)数据类型 (1)联合、(2)n.工会,联盟、(3)合并、(4)团结 11、unsigned [?n'saind]:声明无符号类型变量或函数 (1)无符号的 12、void [v?id] :声明函数无返回值或无参数,声明无类型指针(基本上就这三个作用)(1)a.无效的、(2)没有的、(3)vt.使无效、(4)n.空虚感 二、控制语句关键字(12个): A循环语句 1、for [f?, f?:]:一种循环语句(可意会不可言传) 2、do [du, du:] :循环语句的循环体 3、while [wail] :循环语句的循环条件 (1)conj.当…的时;(2)而;(3)虽然n.一会儿vt.消磨 4、break [breik]:跳出当前循环 (1)中断、(2)断开、(3)n.休息vt.打破 5、continue[k?n?tinju:]:结束当前循环,开始下一轮循环 (1)v.继续,延续,延伸 B条件语句 1、if [if]: 条件语句 (1)条件函数、(2)conj.如果,假如、(3)是否、(4)即使、(5)无论何时 2、else [els] :条件语句否定分支(与if 连用) (1)a. 别的(2)ad. 其他,另外

const的所有用法——Dan+Saks

In my last column, I discussed one of the reasons why the rules by which a compiler can place data into ROM are a bit more complicated in C++ than they are in C.1I have more to say about that subject, but before I do, I’d like to reply to the following query I received through e-mail from Phil Baurer at Komatsu Mining Systems: “We’re having an interesting prob-lem using const with a typedef. I hoped you could comment on this sit-uation. I am wondering if we are bumping into some unknown (by us) rule of the C language. “We are using the Hitachi C com-piler for the Hitachi SH-2 32-bit RISC microcontroller. We thought the fol-lowing code: t y p e d e f v o i d*V P; c o ns t V P v e c t o r T a b l e[] ={....};(1) should be identical to: c o ns t v o i d*v e c t o r T a b l e[] ={....};(2)“However, the linker places v e c t o r T a b l e in (1) into the C O N S T A N T section, but it places v e c t o r T a b l e in (2) into the D A T A section. “Is this the proper behavior or a bug in the compiler?” This is proper behavior; it is not a bug. You are indeed bumping into some rules of the C language that you apparently don’t know about. Don’t feel bad; you’re not alone. I believe many other C and C++ pro- grammers are confused about these rules, which is why I’m answering this in my column. I presented some of these rules in an earlier column.2However, in look- ing back at that column, I don’t think I emphasized strongly enough the points which seem to be the source of your confusion. So let me try again. Declarat ors Here’s the first insight: Every declaration in C and C++ has two principal parts: a sequence of zero or more declaration specifiers, and a sequence of one or more declarators, separated by commas. For example: A declarator is the name being declared, possibly surrounded by operators such as *, [], (), and (in the case of C++) &. As you already know, the symbol *in a declarator means “pointer to” and []means “array of.” Thus, *x[N]is a declarator indicating that x is an “array of N elements of pointer to ...” something, where that something is the type specified in the declaration specifiers. For example, s t a t i c u ns i g ne d l o ng i nt*x[N]; declares x as an object of type “array of N elements of pointer to unsigned long int.” (As explained later, the key- word s t a t i c does not contribute to the type.) How did I know that *x[N]is an “array of ... pointer to ...” rather than a “pointer to an array of ...?” It follows from this rule: The operators in a declarator group accord- ing to the same precedence as they do when they appear in an expression. For example, if you check the near- est precedence chart for either C or C++, you’ll see that []has higher precedence than *. Thus the declara- tor *x[N]means that x is an array before it’s a pointer. Parentheses serve two roles in declarators: first, as the function call operator, and second, as grouping. As the function call operator, ()have the same precedence as []. As grouping, ()have the highest precedence of all. Embedded Systems Programming FEBRUARY 1999 13 Dan Saks const T vs.T const Although C and C++ read mostly from top-to- bottom and left-to-right, pointer declarations read, in a sense, backwards.

C语言32以及C 63个关键字及其含义

C语言32个关键字及其含义 auto:自动变量用关键字auto作存储类别的声明。(可以省略,不写则隐含确定为“自动存储类别”) break:不能用于循环语句和switch语句之外的任何其他语句中。作用为结束循环。 case:情况之一 char:字符型 const:常量 continue:作用结束本次循环,不是终止整个循环。 default:默认结束 do:做(先做后判断) double:双精度 else:别的 enum:枚举类型, extern:外部变量声明 float:浮点型 for:循环语句, goto:标记。作用是从内层循环跳到外层循环。 if:如果,条件语句

int:整型 long:长整型 register:寄存器标识符return:返回值 short:短整型 signed:有符号型sizeof:大小,长度static:静态的 struct:结构体switch:交换 typedef:起别名 union:共用体unsigned:无符号型void:无返回

C++66个关键字的中文含义 1.asm(汇编),用法如下: asm(指令字符串); 允许在C++程序中嵌入汇编代码。 2.auto(自动,automatic)是存储类型标识符,表明变量“自动”具有本地范围,块范围的变量声明(如for循环体内的变量声明)默认为auto存储类型。 3.bool(布尔)类型,C++中的基本数据结构,其值可选为true(真)或者false(假)。C++中的bool类型可以和int混用,具体来说就是0代表false,非0代表true。bool类型常用于条件判断和函数返回值。 4.break(中断、跳出),用在switch语句或者循环语句中。程序遇到break后,即跳过该程序段,继续后面的语句执行。 5.case用于switch语句中,用于判断不同的条件类型。 6.catch catch和try语句一起用于异常处理。

相关主题
文本预览
相关文档 最新文档