数组

一维数组

初始化数组

  • 第一种:数组定义空间大小但不手动赋值(默认初始化)

    • 1.1
      不初始化,只定义,后面不赋值(没有意义)
      这种直接开辟空间的是没有意义的,如果这样初始化后,必须记得后面要进行赋值操作,下面的代码打印出来就是一个没有意义的数值。
    	int nums[5];
    int len = sizeof(nums) / sizeof(int);
    for (int i = 0; i < len; i++) {
    cout << nums[i];
    }
    • 1.2
      另一种是定义,并 默认初始化,给一个大括号就是默认初始化 ,不同类型会产生不同的默认值。
      数值类型(int,float,double…):默认值是0
      字符类型(char):默认值是空字符
      长度是根据你给出的数组大小决定的,
      不会因为没有赋值而长度变成0。
    	int nums[5]={};
    int len = sizeof(nums) / sizeof(int);
    for (int i = 0; i < len; i++) {
    cout << nums[i];
    }
    cout << len << endl;
    //长度是5,不会因为没有赋值长度变成0
  • 第二种:数组定义空间大小并赋值

    • 定义数组并给初始化的空间全部手动赋值
      直接给数组的空间赋值,并且每一个数值的类型必须都是一样。(五个int空间就赋值5个数值)
    	int nums[5]= { 1,2,3,4,5 };
    int len = sizeof(nums) / sizeof(int);
    for (int i = 0; i < len; i++) {
    cout << nums[i];
    }
    • 定义数组并给初始化的空间部分手动赋值,后面的默认初始化
      数组是5个空间,但是只初始化了4个数值,那么剩下的就会用0默认赋值。
      数值类型(int,float,double…):默认值是0
      字符类型(char):默认值是空字符
    	int nums[5]= { 1,2,3,4};
    int len = sizeof(nums) / sizeof(int);
    for (int i = 0; i < len; i++) {
    cout << nums[i];
    }
  • 第三种:数组不定义空间大小,但需手动赋值。
    中括号内不显示的定义多少个空间,那么就必须在花括号内赋值,赋值后会自动帮你计算出来你赋值了多少个数值,然后你的数组空间大小就是多少。
    不可以即不显示写出空间的大小也不进行花括号赋值初始化,这样一般的代码编辑器不用等你编译后都会显示的告诉你代码错误
    错误示范→:nums[]={}

    	int nums[]= { 1,2,3,4,5};
    int len = sizeof(nums) / sizeof(int);
    for (int i = 0; i < len; i++) {
    cout << nums[i];
    }

数组名

  • 数组名的含义

    • 直接打印数组名即数组的地址
    • &数组名,即数组地址
    • 数组第一个空间的地址(首地址,&数组名[0])
	//以下输出的三个都是地址,并且都是同一个地址
cout << nums << endl;
cout << &nums << endl;
cout << &nums[0] << endl;
  • 取出的地址进行sizeof量长度大小的话

    • x64(64位编译环境下)
      地址长度为8字节
    • x86(32位编译环境下)
      地址长度为4字节

二维数组

二维数组其实本质还是一维数组,只不过可以用二维坐标的方式取出数据而已,其实你还是可以只使用一个行就能取出所有元素。
比如nums[0][0~end]可以只使用第0行就能取出后面所有的元素,因为二维数组的元素还是一串连续的地址空间,因此即使你使用超过该行的列数,那么他二维数组会把你该列模该行的列数的结果就是下一行的元素列下标(当然是不超过下标的情况)。
二维数组定义的四种方式

  • 1:数据类型 数组名 [ 行数 ][ 列数 ];
    初始化,同一维数组一样,如果后面不进行赋值的话就没有意义。

  • 2:数据类型 数组名 [ 行数 ][ 列数 ] = {{数据1,数据2 }, {数据3,数据4}};
    初始化的空间多少就赋值多少数据。

    	int nums2[2][3] = { {1,2,4},{5,6} };

    这种初始化方式照样很随意,只要你不超过一行规定的列数,所有元素加起来不超过总空间即可,因为二维数组本质就是一维数组。
    同理:在本例子中,第二行的5,6中明显不符合三列的规定,但是在数组中会默认赋值为0,同一维数组一样
    (都说了吧,二维本质就是一维)

  • 3: 数据类型 数组名[行数][列数]={数据1,数据2,数据3,数据4};
    这里是需要你赋值的数据不要超过二维数组的总空间即可
    比如:2行3列,你用这种赋值方式只需要你所有数据个数在2×3=6个内即可。

    int nums2[2][3] = { 1,4,5,6 };

    这种和给每行单独赋值的区别就是:在该方法中,我们不能让计算机给我们的某一行默认赋值0,比如本方法中,他会自动的给145分在同一行,然后6作为第二行,然后后面两列没有赋值的就会自动的填充0。但是我们在用花括号给每一行进行赋值的时候可以手动的控制我们应该赋值的元素,不赋值的就自动让他默认值即可。(各个有各个的好处,看具体情况具体分析)

  • sizeof(nums[0]):这是一行的数据大小

  • sizeof(nums[0][0]):这是一个元素的大小

  • nums[0]这是一行中的首地址,相当于一维数组中的首地址

  • nums这是第一行第一列的地址,也是整个二维数组的地址

总结:二维数组和一维数组一样,二维只是多了一个行的定义,在二维中可以表现的跟一维数组一样,只用一行就可以表示二维内所有的元素(只不过是在改行内作为主位置,相对于其他元素的位置作为列下标)
好比下面的例子:甚至可以用 负号 作为列下标
int nums2[2][3] = { 1,2,5,6,1 };
cout << nums2[1][-1] << endl;

函数

函数声明

  • 函数声明可以有多次,但是函数定义只能有一次(可以选择重载函数)
    函数声明可以单出一个头文件,前提是必须在main函数之前声明,在函数定义之前声明。

函数的分文件

  • 头文件
    用来存放函数的声明和其他。
  • 函数定义文件,也就是CPP文件
    用来存放对于头文件中的函数声明的函数定义。
  • 主文件CPP
    这个就是主要程序的文件了。
  • 注意:不同文件之间相互引用才能使用彼此的代码,比如函数定义里面要使用头文件里面的东西,直接使用include即可引进来(自己编写的文件要用双引号引,不能用尖括号)
  • 主程序中一般引用的是头文件,然后定义函数文件里面也必须要引用头文件。
    解释:我们主程序引用头文件就行是因为我们在一个项目中,因此编译的时候可以通过函数声明找到函数定义的文件然后我们引用函数声明的文件即可。

函数重载

  • 函数重载的条件

    • 函数名必须相同
    • 函数的参数个数或者参数类型不同
      首先这个参数个数必须是没有默认参数的才算一个参数
      下面这两个出现二义性了,就不能构成函数重载了。
      void a(int a,int b = 10);
      void a(int a);
    • 函数使用引用类型进行函数重载也是可以的。
      需要注意的就是我们的引用类型本质是:int* const,因此传参的时候只需要正常传参就行,传入一个普通变量,该函数就会将你的变量转为引用类型到函数内部进行操作。
      因为函数参数就是一个赋值的过程,函数参数 = 调用函数的实参,那么在引用作为参数的时候就直接变成了:int& a = 实参,所以函数调用的过程就是一个参数赋值的过程,然后将你参数的类型转到函数内部进行使用。
      • 还有一个注意事项:const int& a,双重const的时候该变量类型本质就是:const int * const a,所以我们在调用的时候必须要传递一个常量作为参数,所以我们可以直接传define过的常量变量或者直接传递一个数字,数字就是一个常量,变量才不是常量。数值10,。。。这些都是常量,变量则是有变量名和显示指出变量类型才为变量。
  • 返回值不同不能完成重载,因为我们的重载条件仅仅是函数名相同,参数类型和个数不同即可。函数返回值不同不会影响你的函数重载,换句话说不能使用函数返回值不同来完成函数重载。

指针

指针占的内存

  • 在x86,即32位操作系统编译下,一个指针变量占:4位
  • 在x64,即64位操作系统编译下,一个指针变量占:8位
    无论是什么类型的指针都一样,因为存的指针都是进制数。

空指针

空指针是用于防止野指针的出现

  • 空指针不是野指针,空指针用NULL赋值之后是指向0的,就是防止野指针出现。

    • 控制指针不能访问,除非你给他赋值地址了,当然你赋值了就不叫空指针了都。(因此:当他还是一个空指针的时候,空指针是不能进行访问赋值的)

野指针

  • 野指针就是你的指针用完之后没有将置为空指针,那么这个指针的指向是不明确的,或者说你这个地址用完了已经释放了,但是你的指针还在指向这个地址位置,这就是没有权限的访问,越界访问就是野指针。在编写代码中不要出现空指针现象。

const

常量的意思是,被修饰的那个东西不能修改。

  • 常量指针
const int * p

常量指针,直接音译过代码就是const int *,常量const,指针int *,
然后的话常量指针修饰的是类型,所以指针指向的数值不能修改,但是我们的指针的指向的地址是可以修改的。
解释:常量的指针嘛,那你指针的地址指向的东西就是常量,即地址对应的数值不可以修改,但是 地址可以修改。

  • 指针常量
int* const p

指针常量,直接音译过代码就是int *指针,const常量,
然后指针常量说的就是指针的常量,那很明显了我们的指针地址不能修改,也就是说我们指针指向不能修改但是数值是可以修改的。
解释:指针的常量,中文意思已经很明白了,就是指针常量,那么就是指针指向的地址是常量,常量就是不能修改的意思。

  • 双重const修饰
    意思就是既修饰指针变量又修饰指针指向的值,就是上了双重锁,一旦指定一个值,地址不能变,值也不能变。
const int * const p

总结:其实字面意思和代码的书写顺序一样
常量+指针 = const int* : 常量指针
指针+常量 = int* const : 指针常量
如何区别:其实只要记住中文意思即可
常量指针:常量的指针,那就是说我的这个指针指向的值是一个常量,那就是指针指向的值是不能修改的,但是这个地址的值可以随便修改。
指针常量:指针的常量,那就说我们的这个指针变量不能随意修改,那么我们指针变量存的东西就是地址值,那么换句话说就是我们的指针常量就是不允许修改指针地址,但是地址指向的值可以随意修改。
双重const:很明显了 ,就是哪边都不能进行修改操作,直接上了两把锁。

结构体指针

在结构体中使用指针访问内部变量的时候使用->箭头。

指针++

指针++这个就是说对于一个数组指针来说,可以使用指针++的形式进行移位访问。相当于用自增下标访问所有元素。

int nums[5] = {1,2,3,4,5};
int* p = nums;
for(int i = 0; i < 5; i++){
cout << *p++ << endl;
}

指针与函数

地址传递

经典的通过指针地址交换值函数

void swap(int* num1, int* num2){
int temp;
temp = *num1;
*num1 = *num2;
num2* = temp;
}
//调用
int a = 1;
int b = 2;
swap(&a,&b);

C++引用

基本语法

int a = 10;
int& b = a; //这个就是引用类型
  • 引用类型的本质其实就是: int * const p
    就是指针常量,指针指向的地址不等你修改,值可以修改就是引用的本质。

    • 引用必须初始化
    • 引用不可以改变(值可以改变)
      验证:
int a = 10;
int& b = a;
int c = 1;
int& d = c;
//开始验证
//int &e;这样写是错误的
d = b;//记住这个不是改变引用,而是把b的值给了引用d

在尝试改变引用中发现是行不通的,就是说我们完成不了这件事,也就验证了这件事是不可行的,C++开发者已经把引用封装好了,所以刚刚也说引用就是指针常量。
因此记住引用就是封装好的指针常量。

  • 引用也是一个类型,因此我们可以用它作为函数参数传递一个实参
    调用参数的时候可以很方便,在函数中使用引用过来的参数也很方便。
    引用就是给变量起别名,因此调用的时候是直接使用变量的名字,然后函数中直接用&变量名的形式把调用函数的变量名接过来(当真是妙哉妙哉!!)
void swap(int& a,int& b){
int temp;
temp = a;
a = b;
b = a;
}
//函数调用
int a = 10;
int b = 20;
swap(a,b);
//我们可以很方便的在传参过程中就像传递形参一样传递实参进函数里面。
  • 常量引用
    说白了就是双重buff的const,因为引用就是指针常量,
    所以常量引用就是:const int& 变量名别名 = 变量名
    const int&本质就是:const int* const

面向对象易错点

  • this指针使用->箭头访问成员(学过Java的千万别记混了)

  • 在类中又常函数一说,在该函数中只读不可修改任何一个成员函数,
    除非成员函数前有一个关键字mutable

  • 属性名:建议都用m_大驼峰书写方式变量名(每个单词字母大写)

  • 函数名:用大驼峰变量名(每个单词第一个字母大写)

  • 同类之间的访问权限都是public,这一点在面向对象语言中都如出一辙。
    (即:同类但是不同对象之间,只要是在类的内部就可以访问该对象的私有属性。意思是在类中传入同一个类的参数对象,可以在类中直接访问该参数对象的私有属性)

  • 在函数体内部想要返回一个实例化的对象必须要使用new关键字分配出来的空间才可以在执行完函数之后不会将该空间销毁。术话:只有使用new(malloc)在堆区分配出来的空间才是手动开辟手动销毁的。否则,不使用new(malloc)关键字,直接比如:Person p;这样进行实例化的对象返回之后就会将其局部变量销毁掉,因为出了new(malloc)是在堆区申请的空间,剩下的实例化方法在函数体内都是在栈上的空间地址,执行完函数就会将其销毁掉。

  • 拷贝构造与重载等号运算符不一样,拷贝构造是发生在实例化的时候,即空对象复制已存在的对象值而等号是发生在等号两边的对象都存在的时候进行=赋值。虽然本质都是复制,并且都是默认浅拷贝,所以我们要在operator=重载运算符中进行对指针的深拷贝操作,操作与拷贝构造函数一样的思想,就是重新开辟一个新的堆区空间 。
    总结:拷贝构造与重载等号运算符不一样的地方就是一个是构造函数用来空对象=已存在的,而等号则是两个已存在的对象之间进行赋值操作。

  • 一旦一个类中有指针类型,就必须要进行拷贝构造和析构函数书写,对地址空间的进一步控制,防止内存泄漏。

  • 一旦写了拷贝构造函数就要注意是否出现对象运算符等号双方已存在的情况,并且类中存在指针类型属性的时候一定要写等号重载运算符,否则会造成内存重复释放
    因此重载等号运算符的时候一定要注意该事项,且编译不报错,否则很难找出来原因。

  • 等号运算符重载函数与拷贝函数不一样。

    • 等号运算符重载:等号两边的对象必须存在才会发生类中的等号属性进行值拷贝。
    • 拷贝构造函数:在对象实例化的时候才会发生,即:左值为空对象,等号右边为已存在的对象。将右边对象使用拷贝函数复制到空对象中。
  • 子类的静态成员一旦与父类的任何一个成员发生同名的时候,子类访问不到父类的成员,不管是否发生函数名相同参数不同看上去可以通过参数发生重载情况能够通过子类访问到父类的时候,其实子类是完全隐藏了父类的同名的成员,所以我们的子类希望访问父类同名成员的时候必须要通过父类的类名::进行访问。

  • 重写与重载的区别

    • 重写都是发生在父类与子类之间,子类重写父类的函数,且重写满足条件必须是返回值函数名参数个数类型顺序必须一模一样才算是发生函数重写
    • 重载能够发生的条件就是相反的,函数名一样即可,但是是通过参数不同来发生函数重载,即参数个数不同、参数类型不同、参数类型顺序不同也可以发生重载(但是重载不可以用返回值来发生重载)
    • 虚函数必须有实现体
    • 纯虚函数没有实现体
    • 纯虚析构必须要有实现体,但是必须类内声明类外实现。(即类内 = 0,类外使用作用域进行实现函数体)
      否则:编译不报错,运行报错。
  • 函数参数中可以写参数默认值,参数默认值只可以写在参数末尾,比如:fun(int a, int b = 0, int c = 0);,默认只可以写在参数末尾,不可以写在非末尾。

  • 假如构造函数中所有参数都有默认值,那么这个构造函数可以看成一个无参构造函数,即初始化的时候可以不带任何参数。(当然这时候编译器的默认无参构造已经不存在了,走的是自己写的构造)

  • 模板类 的函数声明注意事项:类内的所有函数的默认参数只可以在类内声明的时候写上,类外定义的时候不可以写默认参数,否则直接报错。

  • 模板类 在自己的cpp文件中跟普通类一样包含自己的.h文件声明,!!!但是在被main文件调用的时候不允许包含.h文件,想要调用模板类,在main中只能包含类模板的cpp文件

    • 总结来说就是:写模板类希望分文件写的时候建议在main函数文件中包含的是类模板的.cpp文件

零散知识点

  • cout修改输出数字个数:cout输出数字的时候可以通过cout.precision(数字个数)修改输出位数,比如cout.precision(3)就代表只有三个数字输出而且会做一个四舍五入,比如3.146输出3.15,这个意思不是保留小数后三位,而是加上个位数上的保留几位数字的意思。

    cout.precision(数字个数);
    cout<<num<<endl;
  • cout修改输出小数后面的精度:cout输出数字的时候可以通过cout.precision(保留小数个数);cout.flags(cout.fixed);
    也称作:定法。

    cout.precision(保留小数个数);
    cout.flags(cout.fixed);
    cout<<num<<endl;
  • 当我们不希望cout继续用这些设置几位小数的时候,
    即希望cout取消定点法输出的时候用↓:

    cout.unsetf(cout.fixed);
    • 总结上cout修改输出位数:cout.precision是设置数字位数,如果单单只是设置了cout.precision的个数就代表的是从整数位数起保留几个数,比如cout.precision(4),即保留4位数字,那输出double n = 123.45678的时候只会输出123.4,如果是设置了保留几位数后希望我们保留的这个几位数是保留小数后的数字那就需要设置cout.flags(cout.fixed);,那么当我们设置完cout.precision(4)后再写cout.flags(cout.fixed);的输出就是123.4567,即这个4的参数变成了保留小数后面呢4位
  • Long类型:建议在数字后面加大写的L,如123L
    LongLong类型:建议在数字后面加大写的LL,如123LL
    float类型:希望一个常量小数是float的话,一定要在小数后面加f
    double:希望一个常量小数是double的话,后面不加f的都默认当成double

  • 在C++中的进制如何表示(即cout输出什么格式的时候默认会当成哪个进制)
    16进制:0x为前缀
    8进制:0为前缀(没听错,8进制就是0为前缀,比如:011 = 十进制的9)
    所以:
    当我们cout输出0x某某数字的时候这个就会当成16进制,而我们cout以0开头的数字就会当成八进制数字输出,输出的时候会自动帮我们把你该进制的数字转化成十进制输出来。

日后继续补充细节…


C++:查漏补缺笔记的更多相关文章

  1. Mysql查漏补缺笔记

    目录 查漏补缺笔记2019/05/19 文件格式后缀 丢失修改,脏读,不可重复读 超键,候选键,主键 构S(Stmcture)/完整性I(Integrity)/数据操纵M(Malippulation) ...

  2. 2019Java查漏补缺(一)

    看到一个总结的知识: 感觉很全面的知识梳理,自己在github上总结了计算机网络笔记就很累了,猜想思维导图的方式一定花费了作者很大的精力,特共享出来.原文:java基础思维导图 自己学习的查漏补缺如下 ...

  3. 《CSS权威指南》基础复习+查漏补缺

    前几天被朋友问到几个CSS问题,讲道理么,接触CSS是从大一开始的,也算有3年半了,总是觉得自己对css算是熟悉的了.然而还是被几个问题弄的"一脸懵逼"... 然后又是刚入职新公司 ...

  4. js基础查漏补缺(更新)

    js基础查漏补缺: 1. NaN != NaN: 复制数组可以用slice: 数组的sort.reverse等方法都会改变自身: Map是一组键值对的结构,Set是key的集合: Array.Map. ...

  5. Entity Framework 查漏补缺 (一)

    明确EF建立的数据库和对象之间的关系 EF也是一种ORM技术框架, 将对象模型和关系型数据库的数据结构对应起来,开发人员不在利用sql去操作数据相关结构和数据.以下是EF建立的数据库和对象之间关系 关 ...

  6. 20165223 week1测试查漏补缺

    week1查漏补缺 经过第一周的学习后,在蓝墨云班课上做了一套31道题的小测试,下面是对测试题中遇到的错误的分析和总结: 一.背记题 不属于Java后继技术的是? Ptyhon Java后继技术有? ...

  7. 今天開始慢下脚步,開始ios技术知识的查漏补缺。

    从2014.6.30 開始工作算起. 如今已经是第416天了.不止不觉.时间过的真快. 通过对之前工作的总结.发现,你的知识面.会决定你面对问题时的态度.过程和结果. 简单来讲.知识面拓展了,你才干有 ...

  8. 【spring源码分析】IOC容器初始化——查漏补缺(四)

    前言:在前几篇查漏补缺中,其实我们已经涉及到bean生命周期了,本篇内容进行详细分析. 首先看bean实例化过程: 分析: bean实例化开始后 注入对象属性后(前面IOC初始化十几篇文章). 检查激 ...

  9. Django 查漏补缺

    Django 查漏补缺 Django  内容回顾: 一. Http 请求本质: 网络传输,运用socket Django程序: socket 服务端 a. 服务端监听IP和端口 b. 浏览器发送请求 ...

  10. Mysql查漏补缺

    Mysql查漏补缺 存储引擎 数据库使用存储引擎来进行CRUD的操作,不同的存储引擎提供了不同的功能.Mysql支持的存储引擎有InnoDB.MyISAM.Memory.Merge.Archive.F ...

随机推荐

  1. #分治#洛谷 5502 [JSOI2015]最大公约数

    题目 分析 又是一道思维题,考虑用分治,选取左边或右边的基准尽量扩展长度,时间复杂度\(O(nlog_2n)\) 代码 #include <cstdio> #include <cct ...

  2. html-testRunner中文乱码

    如下图,使用 html-testRunner 这个库生成测试报告后,出现乱码 因为  HTML文件已经写了  文件编码是  utf-8 所以 我怀疑可能是 html-testRunner 这个库文件中 ...

  3. openGauss内存引擎中的索引

    一.索引 索引是一种用于快速查询和检索数据的数据结构.常见的索引结构有: B 树, B+树和 Hash. 索引的作用就相当于目录的作用.打个比方: 我们在查字典的时候,如果没有目录,那我们就只能一页一 ...

  4. MogDB 使用样本数据集Mogila

    MogDB 使用样本数据集 Mogila MogDB 提供了一个样本数据集 Mogila,本数据集借鉴了适用于 MySQL 的Sakila 示例数据库.Sakila最初由 MySQL AB 文档团队的 ...

  5. Native Rawfile开发指导

      场景介绍 开发者可以通过本指导了解在HarmonyOS应用中,如何使用Native Rawfile接口操作Rawfile目录和文件.功能包括遍历.打开.搜索.读取和关闭Rawfile. 接口说明 ...

  6. Native Drawing开发指导,实现HarmonyOS基本图形和字体的绘制

      场景介绍 Native Drawing模块提供了一系列的接口用于基本图形和字体的绘制.常见的应用场景举例: ● 2D图形绘制. ● 文本绘制. 接口说明 接口名 描述 OH_Drawing_Bit ...

  7. 物联网浏览器(IoTBrowser)-Java快速对接施耐德网络IO网关

    前一段时间有个Java技术栈的朋友联系到我,需要快速对接现有的无人值守称重系统,这里的对接是指替代现有系统,而非软件层面的对接,也就是利用现有的硬件开发一套替代现有软件的自动化系统.主要设备包括地磅秤 ...

  8. BI、OLAP、多维分析、CUBE 这几个词是什么关系?

    这些词我们在建设分析型应用时经常会听到,这几个词也经常被弄混,这里来梳理一下. BIBI 是 Business Intelligence(商业智能)的缩写,是指企业利用已有数据进行数据分析从而指导商业 ...

  9. web开发可不可以是这样的?

    service不外乎就是数据校验,调用其它service,调用第三方api,读写数据库,既然这样,那我认为Service也可以做成可配置化的样子,配置项大致有 所需参数配置:参数列表,参数类型,参数长 ...

  10. ES6中数组新增了哪些扩展?

    一.扩展运算符的应用 ES6通过扩展元素符...,好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列 console.log(...[1, 2, 3])// 1 2 3console.l ...