数组

一维数组

初始化数组

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

    • 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. #错排,组合计数#洛谷 4071 [SDOI2016]排列计数

    题目 多组询问长度为\(n\)的排列中恰好有\(m\)个数对号入座的排列数 分析 首先\(n\)个数中选择\(m\)个数放入那\(m\)个位置显然是\(C(n,m)\) 剩下就是错排\(D(n)=(n ...

  2. 直播预告丨OpenHarmony标准系统多媒体子系统之音频解读

    今晚19点,OpenHarmony开源开发者成长计划知识赋能第五期"掌握OpenHarmony多媒体的框架原理"的第四节直播课,即将开播! 深开鸿资深技术专家苑春鸽老师,将在Ope ...

  3. 深入了解 Java 方法和参数的使用方法

    Java 方法 简介 方法是一块仅在调用时运行的代码.您可以将数据(称为参数)传递到方法中.方法用于执行特定的操作,它们也被称为函数. 使用方法的原因 重用代码:定义一次代码,多次使用. 提高代码的结 ...

  4. SQL CREATE INDEX 语句- 提高数据库检索效率的关键步骤

    SQL CREATE INDEX 语句 SQL CREATE INDEX 语句用于在表中创建索引. 索引用于比其他方式更快地从数据库中检索数据.用户无法看到索引,它们只是用于加速搜索/查询. 注意: ...

  5. C# Dictionary(数据字典)的基本用法

    C# Dictionary(数据字典)的基本用法 - Mr.Cat~ - 博客园 (cnblogs.com)   通常情况下,我们可以通过 int 类型的索引来从数组或者 List 集合中查询所需的数 ...

  6. 将 Github Pages 个人博客录入搜索引擎(以 Bing 为例)

    目录 关于 Bing Webmaster Tools 步骤一:登录 步骤二:添加网站 步骤三:验证网站 步骤四:添加网站地图 验证 & 总结 相关链接 笔者最近准备将 Gitee Pages ...

  7. Python 爬虫进阶五之多线程的用法

    Python 爬虫进阶五之多线程的用法 作者 崔庆才   发表于 2016-11-03   分类于 Python   阅读次数: 60553   本文字数: 7.5k   阅读时长 ≈ 7 分钟 前言 ...

  8. UML 哲学之道——类图[三]

    前言 简单整理一些uml中的类图. 正文 类的基本表示法: 名称.属性(类型.可见性).方法(参数.返回值.可见性) 想上面这样,第一行是名称,第二行是属性,第三行是方法 可见性: 表示public ...

  9. MySQL组合索引

    MySQL组引合索优化SQL 我的场景 200w左右的数据,后面会更多 使用定时任务爬取数据插入到自己的数据库.要保证数据的唯一性,所以我用了组合唯一索引. 表结构 最初的组合索引 SQL执行和exp ...

  10. WPF开发随笔收录-操作注册表

    一.前言 在windows平台软件开发过程中,注册表的操作是经常会遇到的一个场景.今天记录一下在操作注册表时遇到的一些坑: 二.正文 1.操作注册表,于是直接从网上找了一段代码来用 /// <s ...