C++入门:一、变量和数据类型

这是我的C++学习笔记第一篇,同所有的程序语言学习路径一样,首先学习的是变量和数据类型。

我的学习路径如下:

1. 变量和数据类型
2. 流程控制
3. 函数声明和调用
4. 面向对象
5. 标准库

这一章,学习的是变量和数据类型,需要了解的有:

1. 变量怎么定义?
2. 常见的都有哪些数据类型,占用的内存是多少?
3. 变量的作用域都有哪些?
4. 变量的生命周期是什么?

了解这些,对于变量基本就够了。

Hello world

在开始之前,先写一个hello world来熟悉一下程序的主要结构以及如何打印一个变量。

#include <iostream>

int main () {
    std::cout << "hello world" << std::endl;
    return 0;
}

iostream提供标准输入输出的头文件,程序以main函数问入口,std为标准库的命名空间,“<<” 为输出操作符,std::cout为标准输出,std::endl为结束符,表示将等待输出的内容从内存传送到标准输出。

使用gcc编译,假设上面的文件为hello.cpp

g++ hello.cpp

mac上将会产出a.out文件,通过 ./a.out 就可以执行。

变量的声明和定义

C++变量定义为

类型 变量名;

声明一个变量

extern int i;

变量不初始化即为声明,初始化之后即为定义。变量可以被声明无数次,但是只能被定义一次。

定义一个整型变量:

int i;
i = 1; // 给i赋值

还可以同时定义多个变量,比如

int i=0, j, k=1
// i,j,k 都为int类型,j没有初始化,初值为0

定义变量可以带有一些限定符,比如可以在定义变量前加static,const等。

变量的基本类型

类型名称
bool布尔
char字符
int整型
float浮点型
double双精度浮点
void无类型

char, int, float, double 可以通过 signed, unsigned, short, long这些类型修饰符来修饰。

注:变量所占内存的大小和系统的编译器有关。

const 限定符

在定义变量的前面加一个const,表示这个变量的值不能被改变,默认状态下,const定义一个变量的时候,这个变量只在该文件内有效

const int i = 1;
i = 2; // 错误,修改了这个变量

如果在多个文件中需要共享这个变量,那么需要加extern关键词。

// a文件定义
extern const i = 1;

// b文件使用,此处i为1
extern const i;

当const和指针在一起的时候,问题就变得比较复杂,学习的时候也会比较头痛(我就是这样)。const和指针在一起有两种情况,一种是指向常量的指针,另一种是常量指针。指向常量的指针用 const int *ptr 形式定义,常量指针用 int *const ptr 定义。

指向常量的指针想要表示的是这个指针指向了一个常量,我们不能通过这个指针来更改所指向的值(但其实指向常量的指针我们可以将它指向一个非常量),通过下面的例子可以演示指向常量的指针是什么样子的:

const int i = 1;
const int *ptri = &i;
std::cout << *ptri << std::endl;
// 尝试修改,就会出错,编译不会通过
*ptri = 2;

// 实际上我们还可以将指向非常量,一点也不会出错
int j = 2;
ptri = &j;
std::cout << *ptri << std::endl; // 这时候打印的是j
// 虽然j不是常量,但是ptri以为自己指向了一个常量,会禁止修改,下面的语句依然会出错
*ptri = 2;

常量指针想要表示的是这个指针本身是一个常量,也就说说这个指针是不能被修改的。看下面的代码

int j = 0;
// 常量指针 ptr 将一直指向j
int *const ptr = &j;
// 尝试修改指向将会出错,但是可以修改指针的值

// ptr1是一个指向常量的常量指针
const int k = 1;
const int *const ptr1 = &k
// 尝试修改指针指向和修改指针的值都会出错

变量的初始化

C++中,变量的初始化是一个异常复杂的问题。(看到各种初始化的方式,不用说也能感觉到非常复杂,至于内部为什么如此复杂,暂时不研究)。

当我们语句

int age = getAge();

的时候,我们用的是getAge返回值去初始化age这个变量。咋一看,用等于号似乎是在给age赋值,实际上这是初始化。在很多语言中这两者都差不多,但在C++中,初始化和赋值是两个完全不同的操作

初始化不是赋值,初始化的含义是创建变量时候赋予一个初始值,而赋值的含义是把对象的当前值擦除,以一个新的值替代。

初始化方式

下面四种语句都可以将age初始化为20。

int age = 20;
int age = {20};
int age{20};
int age(20);

使用花括号的初始化方式称为列表初始化,已经称为C++11标准的一部分。用花括号初始化这种方式有一个特点:初始值存在丢失的风险,编译器将会报错,比如:

long double ld = 3.1415926;
int a{ld}, b = {ld}; //错误,转换未执行,存在丢失信息的风险
int a(ld), b = (ld); //正确,转换执行,确实丢失了部分值

变量的作用域

C++中绝大多数的作用域都以花括号分隔。其中有全局作用域,块作用域。

全局作用域:在所有花括号外面定义的变量

块作用域:在某个花括号内部定义的变量

#include <iostream>

int main() {
    int i = 0;
    for (int i=0; i<5; i++) {
        // i为块作用域,只在花括号内部生效
        std::cout << i << std::endl;
    }

    // 外部的i仍为0
    std::cout << i << std::endl; 
}

// 输出
// 0
// 1
// 2
// 3
// 4
// 0

作用域可以嵌套。

变量的生命周期

全局变量:作用域为全局的生命周期为整个程序的生命周期,伴随着程序的启动而存在,程序的停止而消亡。

局部变量:作用域为局部的变量生命周期为某局部运行时间,程序运行出局部该变量即被销毁。变量存储在栈区。

const限定的变量:生命周期在整个程序运行的时候都存在,和全局变量一致

复合类型

复合类型是基于其他类型定义的类型,C++中有两种比较重要的复合类型,引用和指针。

引用:引用实际上是一个别名,只是为一个已经存在的对象所起的另外一个名字。引用声明方式为 类型 &标识符, 例如:

#include <iostream>

int main() {
    int i = 1;
    int &ref = i;
    std::cout << i << std::endl; 

    // 给引用赋值,实际上就是给i赋值
    ref = 2;
    std::cout << i << std::endl; 

    return 0;
}

定义引用的时候会把引用和初始值绑定在一起,而不是赋值值,引用无法重新绑定另一个对象。引用不是对象,无法定义引用的引用。

指针

指针是指向另外一种类型的复合类型,保存的是变量的地址,定义方式是

类型 *变量
#include <iostream>

int main() {
    int i = 1;
    int *p = &i;
    std::cout << *p << std::endl;
    return 0;
}

使用解引用符(符号*)来访问指针指向的对象。上面的std::cout << *p << std::endl语句输出的就是i的值,也就是1。

字符串

在C里面,字符串是一个char数组,比如

char str[] = "hello"
std::cout << str << std::endl
// 将会直接输出hello

不过,C++标准库中提供了一个string类来让字符串的使用更加方便。

#include <iostream>
#include <string>

using namespace std;

int main () {
    string str1 = "hello";
    string str2 = "world";
    cout << str1 + str2 << endl;
}

string字符串类可以很方便地进行连接,替换操作。

结构体

结构体可以让我们自定义我们想要的数据结构,定义结构体的方法是

struct 结构体名称 {
    成员类型 成员名;
    ...
}

比如我们定义人这个结构体

struct Human {
    string name;
    int age;
    int sex;
}

结构体通过 "." 访问结构体成员,比如下面的程序

#include <iostream>
#include <string>

using namespace std;

int main() {
    struct Hunam {
        string name;
        int age;
        int sex;
    };
    Hunam cmhc;
    cmhc.name = "hello";
    cout << cmhc.name << endl;
}

容易混淆的点

由于C++的语法比较复杂,很多地方容易混淆。

*和&

*和&可以在表达式里面左运算符,也可以作为声明的一部分,符号的上下文决定了决定了符号的意义。整理如下

int i = 42;
int &r = i; // 这里的&出现在声明部分,表示r是一个引用。
int *p; // *出现在声明部分,表示p是一个int指针
p = &i; // 这里的&出现在表达式里面,表示去地址符
*p = i; // 这里的*出现在表达式中,表示解引用
int &r = *p; // &出现在声明部分,表示r是一个引用,*出现在表达式部分,表示解引用

声明语句中*和&是什么

*和&是类型修饰符,修饰的是定义的那个变量,但是和类型挨着或者和变量挨着都是合法的(这具有迷惑性)

// 这两条语句是等价的,*挨着谁都无所谓
int *i;
int* i;

把*靠近类型的写法具有一些迷惑性,比如

int* i,j;

看起来i和j似乎都是指向int指针,但是实际上j不是,还是上面说的,*修饰的是变量,而不是类型。在上面的语句,*仅仅修饰了i而没有修饰j.

你可能还喜欢下面这些文章

shell 变量的定义

shell可以自定义变量,这为shell的编写带来很多方便定义变量定义变量时,变量名不加美元符号($),如:variableName="value"注意:变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样同时,变量名的命名须遵循如下规则。首个字符必须为字母(a-z,A-Z)。中间不能有空格,可以使用下划线(_)。不能使用标点符号。不能使用bash里的关键字(可用help命令查看保留关键字)。变量定义举例:myUrl="http://imhuchao.com/tag/bash"myNum=100使用变量使用一个定义过的变量,只要在变量名前面加美元符号($)即可,如:your_nam

linux shell 入门

从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁。用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。深入地了解和熟练地掌握Shell编程,是每一个Linux用户的必修 功课之一。Linux的Shell种类众多,常见的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shel

Go入门:一、变量和数据类型

这是学习Go语言的第一篇笔记,主要学习的是变量和基本数据类型。如果您也在开始学习Go语言,那么这篇笔记一定能帮助您学习的更快!我的语言学习过程一般分为下面几个:1. 变量和数据类型2. 流程控制方法3. 函数声明和调用4. 面向对象5. 语言特性6. 标准库变量声明Go语言的变量声明有三种第一种,var identifier type// 先声明后赋值var identifier typeidentifier = value// 声明并且赋值var identifier type = value开始实战一下!比如声明一个int类型变量var i inti = 1// 或者var i int =

php的empty,isset,is_null与!

来说说php的empty,isset,is_null 与!,这几个都是if语句中比较常见的判断逻辑。但是有时候用的很纠结,甚至看别人写的程序里面也很纠结。特地梳理梳理,避免踩坑先来定义一些东西<?php$a;$b=0;$c=array();$d='';$e=null;empty,用了会上瘾这是一个用了会上瘾的语言结构!多好,empty可接受的参数是一个变量,任意类型,哪怕是变量不存在,只要变量被boolean转换之后是false(参考:php的boolean都有哪些),那么empty返回的就是false,并且不会出现警告!等价于不过注意的是,empty里面不能使用表达式(在php<

C++动态内存管理

C++中,动态内存管理是通过一对运算符来完成:new 和 delete。new操作符在内存中为对象分配空间并返回一个指向该对象的指针,delete接收一个动态对象的指针,销毁该对象,并释放与之相关的内存。手动管理内存看起来只有这两个操作,似乎很轻松,但实际上这是一件非常繁琐的事情,分配了内存但没有释放内存的场景发生的概率太大了!回想一下,你有多少次打开抽屉却没关上,拿出来的护肤品擦完脸之后却忘了放回去,吃完饭却忘了洗碗。类似这种没有收尾的事情我做的太多了。(以上这些都是在实际生活中我爱人批评我的点)我连这种明面上的事情都能忘记收尾,何况分配内存!所以为了世界和平,我放弃了手动管理内存。好在C+

Go语言的 make 和 new

new 和 make 是两个内置函数,主要用来创建并分配类型的内存。在我们定义变量的时候,可能会觉得有点迷惑,不知道应该使用哪个函数来声明变量,其实他们的规则很简单,new 只分配内存,make 只能用于 slice、map 和 channel 的初始化。下面我们就来具体介绍一下new在Go语言中,new 函数描述如下:从上面的代码可以看出,new 函数只接受一个参数,这个参数是一个类型,并且返回一个指向该类型内存地址的指针。同时 new 函数会把分配的内存置为零,也就是类型的零值。【示例】使用 new 函数为变量分配内存空间。当然,new 函数不仅仅能够为系统默认的数据类型,分配空间,自定义

C++入门:三、函数

这是我学习C++的第三篇笔记,函数。我的学习路径是现在学习的是函数的声明、定义、调用等相关知识。函数声明和定义函数的声明包含返回类型,函数名字,0个或者多个形参,无函数体,通常在头文件中对函数进行声明。函数的定义包含返回类型,函数名字,0个或多个形参,以及函数体。比如写一个求阶乘的函数,可以写成下面这样写一些简单的函数大多数语言都差不多,不过可惜每种语言或多或少都有自己的特色,这是比较令人头秃的地方。函数的参数函数可以带有0或多个参数,每个参数都需要声明类型。参数传递可以传值和传引用。如果形参是引用类型,那么它将绑定到对应的实参中,我们成为传引用。否则,将会把实参的值拷贝后赋值给形参,我们成为

C++ 判断 char* 是否相等

在C++中, 是一个指向字符的指针,通常用于表示C风格的字符串。判断两个  指针是否相等,需考虑两个方面:判断指针本身的地址是否相等:可以通过直接使用  或  操作符来实现。 和  指向不同的地址(尽管它们的内容相同),而  和  指向相同的地址。2. 判断指针指向的字符串内容是否相等:需要使用  函数,它是C标准库中的一部分,在C++中可用。 函数用于比较 、 和  的内容。如果内容相同, 返回

python教程(一):变量和数据类型

python的基本数据类型有整型,浮点型,字符串,布尔,列表,元组,字典,集合。它们占用内存和定义方式如下表:类型占用内存如何定义整型(int)动态长度>=24字节a = 1浮点型(float)动态长度>=24字节a = 1.0字符串(string)动态长度,>=37字节,增加一个字符加1字节a = 'str'布尔(boolean)24字节a = True列表(list)动态长度,>=72字节a = []元组(tupe)动态长度,>=56字节a = ()集合(set)动态长度,>=232字节a = set([])占用的内存通过sys.getsizeof()获

linux awk命令分析你的文本或日志,awk命令用法

简介awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您

赞赏

微信赞赏支付宝赞赏

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注