const¶
不能改变内容的常量,C++用的多。
C和C++¶
C语言中¶
头文件里声明:extern const int BUFSIZE;
外部常量声明
源文件里定义:const int BUFSIZE = 100;
引入头文件,替换的时候都是声明。
在C语言中默认是外部链接性¶
在C语言会出现重复定义 因为C语言const默认外部链接性,其它文件引用包含const的头文件的时候,预处理 相当于把头文件换成了const的定义 所以会有多处的重复定义。
C语言使用const必须写一个源文件,默认外部链接性,extern做一个声明(引用)。
C语言里 总是分配内存空间¶
C++¶
C++中不会每次都把引用的头文件替换成对应的常量定义,那样多个文件引入头文件,就会有多处替换,重复,效率低。
define就是文本替换,const是常量折叠,保存在符号表里,在常量表中去查找替换(如同使用#define一样)。¶
C++中并没有定义一个常量分配内存,而是做了一个常量折叠。
在C++中默认是内部链接性¶
因为在C++中const默认的链接性是内部链接性。即使头文件被包含,头文件替换对应的定义,但是是内部链接性,所以不会出错。
C++可以直接在.h
头文件使用。
C++里通常不会分配内存空间,有时会分配内存空间¶
- 使用外部链接性
在源文件中使用extern const int BUFSIZE = 100;
这样C++就会给BUFSIZE分配内存。(这是定义)
在头文件中声明extern const int BUFSIZE;。
(这是声明)
这样的就是外部链接性 分配内存。
-
取地址
const int i = 100;//不分配内存 const int j = i + 10; long address = (long)&j;//这里如果不取地址j不分配内存,一取地址就分配了内存 char bufs[j + 10];//这里做常量折叠(查找替换)
-
用于集合(数组,struct,类)
普通的可以放到符号表里,所以不分配内存,而数组值太复杂了,不能放符号表里。
分配内存 只有在运行的时候才有值 在编译的时候值是未知的¶
-
#define
在预处理的时候做一个文本查找替换,无法检查类型。 -
const
编译器会把BUFSIZE
放到符号表里,没有分配内存。编译的时候都把BUFSIZE换成100。
编译器做的常量折叠,首先会检查类型。通过安全之后才替换。
#include <iostream>
const int bs = 100;
char buf[bs];
上面代码在编译的时候
C语言不知道bs值是多少,在运行的时候才知道是100,并且不能改。所以C语言中编译的时候会出错。
但是在C++中可以,并不给bs分配内存,只做常量折叠(查找替换),把bs放符号表里,编译的时候查找,知道bs是100。
const指针¶
- 指向const常量的指针
- const常量指针
- 赋值和类型检查
- 可以把非const对象的地址赋值给一个指向const指针
- 不能把const对象的地址赋值给一个指向非const指针
指向const的指针¶
const int* u;
指针u必须指向常量。
u是变量 是指针 指向const int
( 常量整型),是指向常整型的指针。
const int e = 2;
u = &e;
u可以指向常量。
因为u是常量 所以不可以通过指针修改
*u = 300
这句是错误的。
u是指针 u指向的是常量 不能被修改。
可以把非const对象的地址赋值给一个指向const指针¶
int d = 1;
u = &d;
只能使指针u去读取数据 例:输出*u。
不能通过指针修改,例:*u = 300;。
定义const指针¶
int* const w = &d;
w是const指针,常指针,指向整型。
w指向&d,就永远指向&d,不允许修改指向。但是这个数是可以被修改的,*w = 2;
常指针 指向常整型¶
const int e = 100;
const int* const x = &e;
x常指针 指向常整型
int d = 1;
const int* const x2 = &d;
下面这两种写法一样:const都在*
左边
const都是修饰int整型,指针指向const整型。
const int*u;
int const*v;
第二种:const在*右边
const用来修饰指针,const指针。
int* const w = &d;
下面的e是const 但是指针n不是const,所以赋值错误。
const int e = 100;
int* n = &e; //(这种错误)
可以把非const赋值非const,const指针保证不会对它修改。即使它可以修改,也不修改。这是安全的。
e是const,把它赋值给非const。通过指针可以对它修改,不是安全的。所以通不过。但是可以做一个强制转换:
int *k = (int *)&e;
默认不行,但是强制了也可以这样做,只是很危险。尽量不要这样转换。不能修改,只能读取。
*k = 111; //错误的
cout << *k << endl; //可以
- 不能修改值。
非常指针const int*u
- 不能修改指针指向。
常指针int* const w
总结¶
const修饰什么,什么就不可以修改。
int* const w = &d;//w不可以修改
const int* u;//*u不可以修改
字符串¶
const char* cp = "howdy";
应该有const。
C语言早期是下面这种:
char* cp = "howdy";
这种有问题。
cp不能被修改。
cp[2] = 'a' //错误
*(cp + 2) = 'a' //错误
如果要修改 就要用字符数组
char cp2[] = "howdy";
cp2[2] = 'a';
const修饰函数参数和返回值¶
const函数参数¶
传参数 参数也可以是指针(引用也可以)
- 传普通参数
- 利用指针方式传参数
- 利用引用方式传参数
参数传递的过程中 可以加上const,实际根据需要来确定要不要加const。
void f4(const int i)
{
}
void f5(const int* pi)
{
}
void f6(const int& ri)
{
}
- 普通传递
按值传递
传递的是拷贝 改的是拷贝复制品。不是原来的数据并没有改变本身 所以是不会变的
如果传递的参数是一个很大的参数class。函数传递参数class。传递的是复制 复制多占用内存 复制需要时间,效率不高。
传比较大的对象,可以传指针,也可以传引用。
地址复制了一份。地址没那么大,速度会快。
- 利用传指针的方式
参数可以很大可以很小,都没关系。传的是指针。
可以通过指针对原来的数据进行修改。因为有地址,所以可以对原来的数据进行修改。
- 引用和指针完全一样
也会发生修改。
指针和引用都是地址
两种情况用指针和引用¶
- 想对原始数据进行修改。
- 参数数据对象很大(clase,数组) 用指针。速度快。
指针引用或者普通传参都可以加const
参数有const和没有const的不同¶
-
有const 传进来的参数不允许在内部修改。传递进来的数据不许被修改。加了const 就加了保护。
-
没有const 传进来的参数可以修改。
const函数返回值¶
返回C++内置类型(int)自己写的类型(class)
返回指针
返回引用。
int g1()
{
return 1;
}
int* g2(int* x)
{
(*x)++;
return x;
}
int& g3(int& x)
{
x++;
return x;
}
这三个函数 前面都可以加const
加const的不可以被修改,不加const的可以被修改。
- 函数返回值
函数内部的这个返回值是局部的 函数结束之后就没了。返回之后就没了。外面接收的也是一个复制品,和函数普通参数传递一样。按值传递。
- 返回指针
复制的是地址,得到地址的复制,对原来的数据进行操作修改。
不想修改则加上const。
- 引用 同理。和地址一样。可以修改,不想修改则可以加上const。
需要对返回的数据进行保护就加上const。
void u(const int* cip)
{
//*cip = 2;//不可以
int i = *cip;//可以
//int* ip2 = cip;//不可以
}
返回值是一个指向const整型的const指针
const int* const w()
{
static int i;
return &i;
}
//这两种接收都可以,只要第一个是const就行
const int* const ccip = w();
const int* cip2 = w();
- 非const可以赋值给一个const。
- const不能赋值给一个非const。
总结:
这都是一种保护措施。
普通的那种很少传const,本身就是复制品,不怎么需要。
多数传指针 传引用都是用const。指针和引用可以修改数据 不允许的就需要加const。