指针、常量和类型别名
1.使用 typedef char *pstring; const pstring cats = 0;
时:
- 首先,
typedef char *pstring;
定义了pstring
是char *
的别名,即pstring
表示一个指向char
的指针类型。 - 然后,
const pstring cats = 0;
意味着cats
是一个pstring
类型的常量,即cats
是一个指针,这个指针本身是常量,不允许修改cats
指针的值,使其指向其他地方,但可以通过cats
指针修改它所指向的字符内容。例如: -
#include <iostream> typedef char *pstring; int main() {const pstring cats = 0;char str[] = "hello";// 错误:cats 是常量指针,不能修改其指向// cats = str; // 正确:可以修改 cats 指向的内容if (cats!= 0) {*cats = 'H'; }return 0; }
- 这里,如果尝试修改
cats
指针的值(如cats = str;
),编译器会报错,因为cats
是一个常量指针,但如果cats
不为NULL
,可以修改cats
所指向的内容(如*cats = 'H';
)。
2.当我们使用 const char *cats = 0;
时:
-
- 这里
cats
是一个指向const char
的指针,意味着cats
可以指向不同的位置,但不能通过cats
指针修改它所指向的字符内容。例如:
- 这里
#include <iostream>
int main() {const char *cats = 0;char str[] = "hello";// 正确:可以修改 cats 的指向cats = str; // 错误:不能修改 cats 所指向的内容if (cats!= 0) {// *cats = 'H'; }return 0;
}
- 这里可以将
cats
指针指向不同的字符串(如cats = str;
),但如果尝试修改cats
所指向的内容(如*cats = 'H';
),编译器会报错,因为cats
指向的是const char
,其指向的内容是不允许修改的。
总结:
typedef char *pstring; const pstring cats = 0;
中cats
是一个常量指针,其指向可以修改,但不能修改cats
本身的值。const char *cats = 0;
中cats
是一个指向常量的指针,其指向可以修改,但不能修改其指向的内容。
3.对 const pstring *ps;
的解释:
- 首先回顾
typedef char *pstring;
,这里pstring
是char *
的别名,即pstring
表示一个指向char
的指针类型。 - 对于
const pstring *ps;
:ps
是一个指针,这个指针指向的是pstring
类型的数据。- 由于
pstring
本身是char *
的别名,所以ps
实际上是一个指针,它指向的是一个指向char
的指针。 - 关键在于
const
的位置,这里const
修饰的是pstring
,即ps
所指向的pstring
类型的数据是常量。 - 具体来说,这意味着
*ps
是一个pstring
类型的常量,也就是*ps
是一个指向char
的指针,且这个指针是常量。不能通过ps
修改它所指向的pstring
指针的值。
#include <iostream>
typedef char *pstring;
int main() {char str1[] = "hello";char str2[] = "world";pstring p1 = str1;pstring p2 = str2;const pstring *ps = &p1;// 错误:*ps 是常量指针,不能修改其指向// (*ps) = p2; return 0;
}
总结:
const pstring *ps;
中ps
是一个指针,它指向的是一个指向char
的指针(pstring
),并且*ps
所指向的pstring
指针是常量,不能通过ps
来修改它所指向的pstring
指针的值。
c++11中constexpr、auto、decltype
constexpr
- 用途:用于在编译期计算表达式的值。这意味着可以用它来创建常量表达式,提高程序性能。例如,可以用
constexpr
定义常量和常量函数。
constexpr int square(int x) {return x * x;
}
constexpr int num = square(5); // num的值在编译期就确定为25
- 注意点:
constexpr
函数必须足够简单,其内部只能包含少量的操作,如算术运算、类型转换等,并且不能有副作用(如修改全局变量)。
auto
- 用途:让编译器自动推导变量的类型。这样可以减少代码编写的繁琐程度,特别是在处理复杂的类型,如迭代器、模板类型等。
std::vector<int> v = {1, 2, 3};
auto it = v.begin(); // it的类型被自动推导为std::vector<int>::iterator
- 注意点:
auto
会根据初始化表达式尽可能地推导出合适的类型,但有时候推导出来的类型可能不是你期望的,比如引用折叠等情况需要注意。
decltype
- 用途:用于查询表达式的类型。这在编写模板代码或者需要根据已有变量类型来定义新变量时非常有用。
int a = 10;
decltype(a) b = 20; // b的类型和a一样,是int
- 注意点:
decltype
在处理一些复杂的表达式(如函数调用、带有括号的表达式等)时,推导规则比较复杂,需要特别注意其推导出来的类型是否符合预期。
decltype
和auto
在 C++ 11 中有一些相同点和不同点
相同点
- 类型推导目的:
decltype
和auto
都是用于类型推导的工具。它们在一定程度上都能帮助程序员减少代码中冗长的类型声明,使代码更加简洁。例如,在处理复杂的模板类型或者迭代器类型时,它们都可以用来避免手动书写复杂的类型。
不同点
- 推导时机和方式
- auto:
auto
是根据变量的初始化表达式来推导类型,它主要关注的是变量初始化的值。例如,auto x = 5;
,这里auto
会根据5
这个整数值推导x
为int
类型。而且auto
在推导时通常会忽略顶层const
和引用(除非初始化表达式本身就是引用类型)。比如const int a = 10; auto b = a;
,b
的类型是int
,而不是const int
。 - decltype:
decltype
是根据表达式本身的类型来推导,而不考虑表达式是否初始化或者初始化的值是什么。例如,int a = 10; decltype(a) b;
,这里b
的类型就是int
,和a
的类型一样。decltype
在推导时会完整地保留类型的所有属性,包括const
、引用等。例如,const int& c = a; decltype(c) d;
,d
的类型是const int&
。
- auto:
- 使用场景
- auto:通常用于声明变量时,自动获取变量的类型,尤其是在迭代器遍历容器、lambda 表达式捕获变量等场景下非常方便。比如,
for (auto it = container.begin(); it!= container.end(); ++it)
,auto
可以自动推导迭代器的类型。 - decltype:更多地用于在需要获取某个表达式的确切类型来声明其他变量或者函数返回类型等情况。例如,在模板编程中,当你想根据一个模板参数的表达式类型来定义另一个变量类型时,
decltype
就很有用。比如,template<typename T> void func(T& t) { decltype(t) another_variable; }
。
- auto:通常用于声明变量时,自动获取变量的类型,尤其是在迭代器遍历容器、lambda 表达式捕获变量等场景下非常方便。比如,