[C++ 面试基础知识总结] 变量和基本类型

简介: [C++ 面试基础知识总结] 变量和基本类型参考书籍:《C++ Primer》目录C 面试基础知识总结 变量和基本类型目录10与010的区别变量初始化的4种形式变量的作用域引用与指针const限定符const的引用常量指针和指向常量的指针constexpr类型别名auto类型decltype类型头文件保护符

[C++ 面试基础知识总结] 变量和基本类型

参考书籍:《C++ Primer》

目录

10与010的区别

#include <iostream>
using namespace std;

int main(int argc, const char * argv[]) {
    int x = 010, y = 10;
    cout << "x=" << x << " y=" << y << endl;
    return 0;
}

执行上述代码的结果为:

x=8 y=10

原因是在C++中,以0开头的整数表示八进制数,以0x或0X开头的代表十六进制数,八进制中的10等于十进制中的8。

变量初始化的4种形式

int x = 0;
int x = {0};
int x(0);
int x{0};

以上4条语句都可以将x初始化等于0,其中使用了花括号的两种形式是C++ 11的新特性,被称为列表初始化,在为对象赋新值时也可以采取同样的办法。列表初始化形式的一个重要特点是,初始值存在丢失信息风险的时候,编译器会报错:

long double pi = 3.1415926536
//报错,因为存在丢失信息的风险
int a{pi}, b = {pi};
//可以执行,但确实丢失了部分信息
int c(pi), d = pi;

变量的作用域

#include <iostream>
using namespace std;

int i = 40;
int main(int argc, const char * argv[]) {
    // insert code here...
    int i = 100;
    int j = i;
    int k = ::i;
    cout << j << endl;
    cout << k << endl;
    return 0;
}

执行上述代码结果:

100
40

在main()函数外定义的i为全局变量,拥有全局作用域,声明之后在整个程序范围内可用。而在main()函数中定义的i为局部变量,拥有块作用域,只在main()函数内可用。在内层作用域中新建局部变量i会覆盖全局变量i,所以j的值为局部变量i的值100,而采用::i的方式可以显示地访问全局变量i,所以k的值为全局变量i的值40。

引用与指针

&和*的多重含义:

int i = 0;
// &是声明的一部分,r是一个指向i的引用(改变r的值也会改变i的值)
int &r = i;
// *是声明的一部分,p是一个未初始化的指针
int *p;
// &是一个取地址符,将i的地址赋给p,即p为指向i的指针
p = &i;
// *是一个解引用符,将p指向的变量的值赋为1
*p = 1;
// &是声明的一部分,*是解引用符,r2是一个指向指针p指向的变量的引用
int &r2 = *p;

复合类型

// 定义两个int型指针p1,p2
int *p1, *p2;
// 定义int型指针p1,int型变量p2
int* p1, p2;

int i = 0;
// p是一个指向int型数的指针
int *p = &i;
// pp是一个指向int型指针的指针
int **pp = &p;
// r是一个指向int型指针的引用
int *&r = p

const限定符

const的引用

const定义的对象为常量,任何试图对常量进行赋值的行为都会报错,由于const对象定义后就不能修改,所以必须初始化。

int i = 0;
const int j = 0;

// 正确,引用和其指向的对象都为常量
const int &r1 = j;
// 错误,r1是对常量的引用,不能赋值
r1 = 2;
// 错误,不能用非常量引用指向一个常量对象
int &r2 = j;
// 正确,可以用常量引用指向一个非常量对象,但不能通过修改r3的值来修改i的值
const int &r3 = i;
// 正确,常量引用可以绑定到一个普通int对象上
const int &r4 = 0; 
// 错误,非常量引用不能绑定到一个普通int对象上
int &r5 = 0;

常量指针和指向常量的指针

int i = 0;
const int j = 0// p1为常量指针,一直指向i,可以修改i的值
int *const p1 = &i;
// p2为指向常量的指针,可以修改p2的指向,但不能修改指向对象的值
const int *p2 = &j;
// p3为指向常量的常量指针,一直指向j,且不能用p3去修改j的值
const int *const p3 = &j;

修饰指针的const为顶层const,而修饰指针或引用指向的对象的const为底层const

int i = 0;
const int j = 0// 顶层const
int *const p1 = &i;
// 底层const
const int *p2 = &j;
// 前一个为底层const,后一个为顶层const
const int *const p3 = &j;

当执行对象的拷贝操作时,顶层const不受影响,而底层const却有限制,只能允许将非常量转换为常量。

constexpr

constexpr是C++ 11中的新类型,以便由编译器来验证变量值是否是一个常量表达式(数据类型和初始值均为常量)。

// 不是常量表达式,x类型不常量
int x = 0;
// 不是常量表达式,初始值x不是常量
const int i = x;
// 是常量表达式,j类型和初始值均为常量
const int j = 0;

// 正确,等价于 const int k = 0;
constexpr int k = 0;
// 编译器报错,x不是常量,不是常量表达式
constexpr int l = x;

用constexpr定义指针时,初始值必须是nullptr,0或存储于某个固定地址的对象。且修饰符只对指针有效,与所指对象无关。

// 指向常量的指针
const int *p = nullptr;
// 常量指针 等价于 int *const p = nullptr;
constexpr int *q = nullptr;

类型别名

用typedef可自定义类型名

// ptr是char*的同义词
typedef char *ptr;
// p是常量指针,等价于char *const p = 0,而不是const char *p = 0;
const ptr p = 0;

在C++ 11中,有一种新方法自定义类型名:

using ptr = char*;

auto类型

auto类型是C++ 11引入的新类型说明符,由编译器去分析表达式所属的类型(类似Swift中的let,var),为了让编译器能通过初始值推算变量的类型,auto定义的变量必须有初始值。
使用一条auto语句声明多个变量时必须保证所有变量的基本数据类型相同。

//正确,i为整数,p为整型指针,基本类型相同
auto i = 0, *p = &i;
//错误,a为整数,b为浮点数,基本类型不同
auto a = 1, b = 2.5

auto声明指针或引用时一般会忽略顶层const,只保留底层const,如希望推断类型是一个顶层const,需要明确指出。

const int i = 0;
//b是一个整数变量(i的顶层const被忽略)
auto b = i;
//c是一个指向整数常量的引用(对常量取地址是一种底层const)
auto c = &i;
//d是一个整数常量
const auto d = i;

decltype类型

decltype的作用是选择并返回操作数的数据类型(包括顶层const和引用)。

const int i = 0, &r = i, *p = &i;
// x类型为const int
decltype(i) x = 0;
// y类型为const int& ,y绑定到x上
decltype(r) y = x;
// 错误,z类型为const int&,引用必须初始化
decltype(r) z;
// 错误,解引用指针得到的类型是引用类型,所以这里m的类型是int&,而不是int,必须初始化
decltype(*p) m;
// 错误,decltype用双层括号时得到的结果永远是引用,所以这里n的类型是int&,而不是int,必须初始化
decltype((i)) n;

头文件保护符

“#define”指令把一个名字设定为预处理变量。
“#ifdef”当且仅当变量已定义时为真,”#ifndef”当且仅当变量未定义时为真,一旦检查结果为真,则执行后续操作直至遇到”#endif”指令为止。

#ifndef PEOPLE_H
#define PEOPLE_H
struct People {
    std::string name;
    bool male;
};
#endif

用这些功能就可以防止重复包含发生。
注意:预处理变量包括头文件保护符必须唯一,为了避免与程序中其他实体发生名字冲突,一般全部大写。

目录
相关文章
|
23天前
|
C语言 C++ 开发者
C++基础知识(一:命名空间的各种使用方法)
C++在C的基础上引入了更多的元素,例如类,类的私密性要比C中的结构体更加优秀,引用,重载,命名空间,以及STL库,模板编程和更多的函数,在面向对象的编程上更加高效。C语言的优势则是更加底层,编译速度会更快,在编写内核时大多数都是C语言去写。 在C++中,命名空间(Namespace)是一种组织代码的方式,主要用于解决全局变量、函数或类的命名冲突问题。命名空间提供了一种封装机制,允许开发者将相关的类、函数、变量等放在一个逻辑上封闭的区域中,这样相同的名字在不同的命名空间中可以共存,而不会相互干扰。
|
16天前
|
存储 设计模式 监控
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
Java面试题:如何在不牺牲性能的前提下,实现一个线程安全的单例模式?如何在生产者-消费者模式中平衡生产和消费的速度?Java内存模型规定了变量在内存中的存储和线程间的交互规则
25 0
|
23天前
|
C++
C++基础知识(二:引用和new delete)
引用是C++中的一种复合类型,它是某个已存在变量的别名,也就是说引用不是独立的实体,它只是为已存在的变量取了一个新名字。一旦引用被初始化为某个变量,就不能改变引用到另一个变量。引用的主要用途包括函数参数传递、操作符重载等,它可以避免复制大对象的开销,并且使得代码更加直观易读。
|
23天前
|
算法 编译器 C++
C++基础知识(三:哑元和内联函数和函数重载)
在C++编程中,"哑元"这个术语虽然不常用,但可以理解为在函数定义或调用中使用的没有实际功能、仅作为占位符的参数。这种做法多见于模板编程或者为了匹配函数签名等场景。例如,在实现某些通用算法时,可能需要一个特定数量的参数来满足编译器要求,即使在特定情况下某些参数并不参与计算,这些参数就可以被视为哑元。
|
23天前
|
C++
C++基础知识(四:类的学习)
类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。
|
23天前
|
自然语言处理 程序员 C++
C++基础知识(五:运算符重载)
运算符重载是C++中的一项强大特性,它允许程序员为自定义类型(如类或结构体)重新定义标准运算符的行为,使得这些运算符能够适用于自定义类型的操作。这样做可以增强代码的可读性和表达力,使得代码更接近自然语言,同时保持了面向对象编程的封装性。
|
23天前
|
存储 编译器 C++
C++基础知识(六:继承)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
23天前
|
存储 编译器 C++
C++基础知识(七:多态)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
23天前
|
存储 算法 程序员
C++基础知识(八:STL标准库(Vectors和list))
C++ STL (Standard Template Library标准模板库) 是通用类模板和算法的集合,它提供给程序员一些标准的数据结构的实现如 queues(队列), lists(链表), 和 stacks(栈)等. STL容器的提供是为了让开发者可以更高效率的去开发,同时我们应该也需要知道他们的底层实现,这样在出现错误的时候我们才知道一些原因,才可以更好的去解决问题。
|
23天前
|
算法 前端开发 C++
C++基础知识(八:STL标准库 deque )
deque在C++的STL(Standard Template Library)中是一个非常强大的容器,它的全称是“Double-Ended Queue”,即双端队列。deque结合了数组和链表的优点,提供了在两端进行高效插入和删除操作的能力,同时保持了随机访问的特性。