赞
踩
由于在CSDN上无法看到markdown的目录,有需要额小伙伴可以联系我,附送本文的 .md文件,可以再本地typora上更加方便的学习
这篇文章和 汇编入门基础(点此链接) 为想结合内容,请大家在学习时可以同时参考
1.1.1 C++的源文件拓展名是:cpp(c plus plus的简称)
1.1.2 C++程序的入口是main函数(函数即方法,一个意思)
1.1.3 C++完全兼容C语言的语法,很久以前,C++叫做C with classes
1.1.4 C++发展史
#include <iostream>
using namespace std;
int main() {
cout << "hello world!" << endl;
return 0;
}
1.3.1 C++中常使用cin、cout进行控制台的输入、输出
1.3.2 使用cin cout 需要依赖#include 和using namespace std;
1.3.3 cin用右移运算符>>,cout用的是左移运算符<<
1.3.4 endl 是换行的意思
2.1.1 函数名相同
2.1.2 参数个数不同、参数类型不同、参数顺序不同
#include <iostream>
using namespace std;
int sum(int v1, int v2) {
return v1 + v2;
}
int sum(int v1, int v2, int v3) {
return v1 + v2 + v3;
}
int main() {
cout << sum(10, 20) << endl;
cout << sum(10, 20,30) << endl;
return 0;
}
2.2.1 返回值类型与函数重载无关
2.2.2 调用函数时,实参的隐式类型转换可能会产生二义性
注意: C语言不支持函数重载、C++支持函数重载
采用了name mangling或者叫做name decoration技术
重载时会生成多个不同的函数名,不同编译器(MSVC、g++)有不同的生成规则
通过IDA打开[vs 下 release 禁止优化] 可以看到
release下会进行优化(可以禁止)
debug 下可以查询所有反汇编
规则如下:
#include <iostream>
using namespace std;
int sum(int v1 , int v2 = 6) {
return v1 + v2;
}
int main() {
cout << sum(10) << endl;
cout << sum(10, 20) << endl;
return 0;
}
如下面代码写法错误,v2的赋值不能缺失
int sum(int v1 = 10, int v2 ) { //错!!!!
return v1 + v2;
}
#include <iostream>
using namespace std;
//声明
int sum(int v1, int v2 = 6);
int main() {
cout << sum(10) << endl;
cout << sum(10, 20) << endl;
return 0;
}
//实现
int sum(int v1, int v2) {
return v1 + v2;
}
1) 默认参数值是全局变量
#include <iostream>
using namespace std;
int age = 20;
int sum(int v1, int v2 = age) {
return v1 + v2;
}
int main() {
cout << sum(10) << endl;
return 0;
}
2) 指向函数的指针调用函数
(*p)(int) :(*p)是指向函数的指针 (int)为参数类型
#include <iostream>
using namespace std;
int age = 20;
void test(int a) {
cout << "test(int)" << a << endl;
}
int main() {
void (*p)(int) = test;
(*p)(age);
p(age);
return 0;
}
可见 (*p)(age); 和 p(age); 是一样的
(*p)(age); 00111D0F 8B F4 mov esi,esp 00111D11 A1 00 C0 11 00 mov eax,dword ptr ds:[0011C000h] 00111D16 50 push eax 00111D17 FF 55 F8 call dword ptr [ebp-8] 00111D1A 83 C4 04 add esp,4 00111D1D 3B F4 cmp esi,esp 00111D1F E8 66 F5 FF FF call 0011128A p(age); 00111D24 8B F4 mov esi,esp 00111D26 A1 00 C0 11 00 mov eax,dword ptr ds:[0011C000h] 00111D2B 50 push eax 00111D2C FF 55 F8 call dword ptr [ebp-8] 00111D2F 83 C4 04 add esp,4 00111D32 3B F4 cmp esi,esp 00111D34 E8 51 F5 FF FF call 0011128A
3) 默认参数值是函数名
#include <iostream> using namespace std; int age = 20; void test(int a) { cout << "test(int):" << a << endl; } void func(int v1, void(*p)(int) = test) { p(v1); } int main() { func(30); return 0; }
被 extern “C” 修饰的代码会按照C语言的方式去编译
extern "C" void func() {
}
extern "C" void func(int v) {
}
extern "C" {
void func() {
}
void func(int v) {
}
}
#include <iostream>
using namespace std;
// 函数声明
void func();
extern "C" void func(int v);
int main() {
return 0;
}
// 函数实现
void func() {
}
void func(int v) {
}
C++在调用C语言API 时,需要使用extern “C” 修饰C语言的函数声明
在C++ 中引用C文件,声明时需要加上extern “C”,否则报错
只要在C++文件中,默认定义宏 #define __cplusplus (一般不可见)
可以用这个宏来判断是否为C++文件
1)math.c
#include "math.h"
//include 是为了在.c文件中
//调用自己不报错
int sum(int v1, int v2) {
return v1 + v2;
}
2)main.cpp
#include <iostream>
using namespace std;
#include "math.h"
extern "C" {
void other();
}
int main() {
other();
cout << sum(10, 20) << endl;
return 0;
}
3)math.h
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
int sum(int v1, int v2);
#ifdef __cplusplus
}
#endif // __cplusplus
4)math.h
#include "math.h"
#include <stdio.h>
void other() {
int a = sum(10, 99);
printf("a= %d\n",a);
}
我们经常使用 #ifndef、#define、#endif 来防止头文件的内容被重复包含
#ifndef __MATH_H
#define __MATH_H
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
int sum(int v1, int v2);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // __MATH_H
#pragma once 可以防止整个文件的内容被重复包含
#ifndef、#define、#endif 与 #pragma once 区别 :
1)#ifndef、#define、#endif 受C\C++标准的支持,不收编译器的任何限制
2)有些编译器不支持#pragma once (较老编译器不支持,如GCC 3.4版本之前),兼容性不够好
3)#ifndef、#define、#endif 可以针对一个文件中的部分代码,而#pragma once只能针对整个文件
5.1.1 使用 inline 修饰函数的声明或者实现 ,可以使其变成内联函数
5.1.2 建议声明和实现都增加 inline 修饰
5.2.1 编译器会将函数调用直接展开为函数体代码
#include <iostream>
using namespace std;
inline int sum;
inline int sum(int v1, int v2) {
return v1 + v2;
}
int main() {
int c = sum(10, 20);
// 等价于
//int c = v1 + v2;
int c = 10 + 20;
return 0;
}
5.2.2 如果不使用 inline 函数,被调用 func的函数体只会保留一份 (即:cout << “func()” << endl;)
#include <iostream>
using namespace std;
void func() {
cout << "func()" << endl;
}
int main() {
func();
func();
func();
func();
return 0;
}
5.2.3 可以减少函数调用的开销(不存在函数调用,程序直接运行函数体内容)
5.2.4 会增大代码体积
注意
1)尽量不要内联超过10行代码的函数
2)有些函数即使声明为inline 也不一定会被编译器内联,比如:递归函数
5.3.1 函数代码体积不大
5.3.2 频繁调用的函数
#include <iostream> using namespace std; //==================== 声明 ==================== void func(); inline int sum(int v1, int v2); //==================== 实现 ==================== //开辟 栈空间 void func() { cout << "func()" << endl; } //回收 栈空间 inline int sum(int v1, int v2) { return v1 + v2; } int main() { func(); int c = sum(10, 20); cout << c << endl; getchar(); return 0; }
--- E:\C++-程序\05-内联函数\05-内联函数\main.cpp ----------------------------------------- func(); 00291000 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (029305Ch)] 00291006 push offset std::endl<char,std::char_traits<char> > (0291320h) 0029100B call std::operator<<<std::char_traits<char> > (0291100h) 00291010 mov ecx,eax 00291012 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0293038h)] int c = sum(10, 20); cout << c << endl; 00291018 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (029305Ch)] 0029101E push offset std::endl<char,std::char_traits<char> > (0291320h) 00291023 push 1Eh 00291025 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0293034h)] 0029102B mov ecx,eax 0029102D call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0293038h)] getchar(); 00291033 call dword ptr [__imp__getchar (02930F0h)] return 0; 00291039 xor eax,eax }
func(); 01052078 call func (01051424h) int c = sum(10, 20); 0105207D push 14h 0105207F push 0Ah 01052081 call sum (01051429h) 01052086 add esp,8 01052089 mov dword ptr [c],eax cout << c << endl; 0105208C mov esi,esp 0105208E push offset std::endl<char,std::char_traits<char> > (010512B7h) 01052093 mov edi,esp 01052095 mov eax,dword ptr [c] 01052098 push eax 01052099 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0105D0D8h)] 0105209F call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0105D0A0h)] 010520A5 cmp edi,esp cout << c << endl; 010520A7 call __RTC_CheckEsp (0105128Fh) 010520AC mov ecx,eax 010520AE call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0105D0A4h)] 010520B4 cmp esi,esp 010520B6 call __RTC_CheckEsp (0105128Fh)
而且宏会将普通的运算改变
#include <iostream> using namespace std; inline int sum(int v) { return v + v ; } //定义宏 #define add(v)(v + v) int main() { int a = 10; int b = 10; int c = sum(++a); int d = add(++b); cout << c << endl; cout << d << endl; getchar(); return 0; }
输出结果 :22 24
可以看出宏将++a 操作复杂化了
#include <iostream>
using namespace std;
//表达式
int main() {
int a = 1;
int b = 2;
(a = b) = 4;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
getchar();
return 0;
}
输出结果:a=4
b=12
#include <iostream>
using namespace std;
//表达式
int main() {
int a = 1;
int b = 2;
(a > b ? a : b) = 10;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
getchar();
return 0;
}
输出结果:a=1
b=10
#include <iostream> using namespace std; struct Date { int year; int month; int day; }; int main() { Date d = { 2011,2,1 }; Date d2 = { 2013,9,2 }; //p指针指向d结构体 Date* p = &d; //指向结构体的指针,访问结构体的成员使用 -> (语法糖) p->day = 99; //*p 就是结构体即为d 等价于 d.month =12; (*p).month = 12; //结构体本身,访问结构体的成员使用 . (语法糖) cout << d.day << endl; }
const是常量的意思,被其修饰的变量不可修改
如果修饰的是类、结构体(的指针),其成员不可以更改
#include <iostream>
using namespace std;
struct Date {
int year;
int month;
int day;
};
int main() {
Date d = { 2011,2,1 };
Date d2 = { 2013,9,2 };
d = d2;
d.year = 2022;
}
结构体不能改
结构体成员也不能改
#include <iostream>
using namespace std;
struct Date {
int year;
int month;
int day;
};
int main() {
const Date d = { 2011,2,1 };
Date d2 = { 2013,9,2 };
d = d2; // 报错 结构体不能改->不能将d指向d2
d.year = 2022;// 报错 结构体成员也不能改->不能操作d的成员
}
#include <iostream>
using namespace std;
int main() {
Date d = { 2011,2,1 };
Date d2 = { 2013,9,2 };
//p指针指向d结构体
const Date* p = &d;
p->day = 99; // 报错
(*p).month = 12; // 报错
*p = d2; // 报错
cout << d.day << endl;
}
#include <iostream>
using namespace std;
int main() {
int age = 10;
int height = 20;
const int *p1 = &age;
int const* p2 = &age;
int* const p3 = &age;
const int* const p4 = &age;
int const* const p5 = &age;
}
结论:const修饰的是其右边的内容
#include <iostream>
using namespace std;
int main() {
int age = 10;
int height = 30;
int* const p3 = &age;
// p3所指向的存储空间的值改为20(当前p3指向 age的地址)
*p3 = 20; // age = 20;
// 代码含义: 将height的地址 通过 &height赋值给 p3,此时p3指向height的地址
p3 = &height; //==================此行代码报错,原因 p3是常量
// p3所指向的存储空间的值改为40(当前p3指向 height的地址)
*p3 = 40; // height = 40;
}
#include <iostream> using namespace std; int main() { int age = 10; int height = 20; // p1(指针)不是常量,*p1(指向的存储空间)是常量 // p2(指针)不是常量,*p2(指向的存储空间)是常量 const int *p1 = &age; int const* p2 = &age; // p3(指针)是常量,*p3(指向的存储空间)不是常量 int* const p3 = &age; // p4(指针)是常量,*p4(指向的存储空间)也是常量 // p5(指针)是常量,*p5(指向的存储空间)也是常量 const int* const p4 = &age; int const* const p5 = &age; }
const 修饰的是 *pstu1(指针指向的存储空间),禁止pstu1指针去修改它所指向的存储空间
#include <iostream>
using namespace std;
struct Student { int age; };
int main() {
Student stu1 = { 10 };
Student stu2 = { 20 };
//const 修饰的是* pstu1,禁止pstu1指针去修改它所指向的存储空间
const Student* pstu1 = &stu1;
*pstu1 = stu2; // 报错
(*pstu1).age = 20; // 报错
pstu1->age = 30; // 报错
pstu1 = &stu2; //正确
}
const 修饰的是 pstu2(指针),禁止pstu2指针去重新指向新的地址
#include <iostream>
using namespace std;
struct Student { int age; };
int main() {
Student stu1 = { 10 };
Student stu2 = { 20 };
//const 修饰的是* pstu1,禁止pstu1指针去修改它所指向的存储空间
Student* const pstu2 = &stu2;
*pstu2 = stu2; // 正确
(*pstu2).age = 20; // 正确
pstu2->age = 30; // 正确
pstu2 = &stu2; //报错
}
引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针
#include <iostream> using namespace std; void swap(int v1,int v2) { int tmp = v1; v1 = v2; v2 = tmp; } int main() { int a = 10; int b = 20; swap(a, b); cout << "a="<<a << ",b=" << b << endl; getchar(); return 0; } //输出:a=10,b=20
使用 指针(Pointer) 定义变量v1和v2
#include <iostream> using namespace std; void swap(int * v1,int *v2) { int tmp = *v1; *v1 = *v2; *v2 = tmp; } int main() { int a = 10; int b = 20; swap(&a, &b); cout << "a="<<a << ",b=" << b << endl; getchar(); return 0; } //输出:a=20,b=10
使用 引用(Reference) 定义变量v1和v2
#include <iostream> using namespace std; void swap(int& v1, int& v2) { int tmp = v1; v1 = v2; v2 = tmp; } int main() { int a = 10; int b = 20; swap(a, b); cout << "a=" << a << ",b=" << b << endl; getchar(); return 0; } //输出:a=20,b=10
#include <iostream> using namespace std; void swap(int& v1, int& v2) { int tmp = v1; v1 = v2; v2 = tmp; } int main() { int a = 10; int b = 20; swap(a, b); cout << "a=" << a << ",b=" << b << endl; int c = 30; int d = 40; swap(c, d); cout << "c=" << c << ",d=" <<d << endl; getchar(); return 0; } //输出: a=20,b=10 c=40,d=30
在定义“引用变量”时就赋值-即指向谁(否则 vs 中报错)
int age =10;
int &refAge = age; // 定义时 赋值
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
int& refA = a;
cout << "当前 refA=" << refA << endl;
refA = b; // 实际上是将 b的值20 赋值给引用变量 refA
cout << "当前 refA=" << refA << endl;
getchar();
return 0;
}
#include <iostream>
using namespace std;
int main() {
int age = 10;
int* p = &age;
*p = 5;
return 0;
}
C++ 对应的汇编
5: int age = 10;
008917E2 mov dword ptr [ebp-0Ch],0Ah
6: int* p = &age;
008917E9 lea eax,[ebp-0Ch]
008917EC mov dword ptr [ebp-18h],eax
7: *p = 5;
008917EF mov eax,dword ptr [ebp-18h]
008917F2 mov dword ptr [eax],5
#include <iostream>
using namespace std;
int main() {
int age = 10;
int &ref = age;
ref = 5;
return 0;
}
7: int age = 10;
013E17E2 mov dword ptr [ebp-0Ch],0Ah
8: int &ref = age;
013E17E9 lea eax,[ebp-0Ch]
013E17EC mov dword ptr [ebp-18h],eax
9: ref = 5;
013E17EF mov eax,dword ptr [ebp-18h]
013E17F2 mov dword ptr [eax],5
在C语言中,使用指针(Pointer)可以间接获取、修改某个变量的值 (在C++可用)
在C++语言中,使用引用(Reference)可以起到跟指针类似的功能
1)结构体引用
#include <iostream> using namespace std; struct Date { int year; int month; int day; }; int main() { //结构体引用 Date d = { 2011,2,3 }; Date& ref = d; ref.day = 18; return 0; }
2)指针的引用
#include <iostream>
using namespace std;
int main() {
//指针的引用
int age = 10;
int* p = &age;
int*& ref = p;
* ref = 30;
int height = 30;
ref = &height;
return 0;
}
3)用于指向数组的引用(指针)
#include <iostream>
using namespace std;
int main() {
int array[] = { 1,2,3 };
//指针数组,数组里面可以存放3个int*
int* arr[3];
//用于指向数组的指针
int(*arr2)[3];
int(&refArr)[3] = array;
return 0;
}
引用存在的价值之一:比指针更安全、函数返回值可以被赋值
引用可以被const修饰,这样就无法通过引用修改数据了,可以称为常引用
const必须写在&符号的左边,才能算是常引用
常引用:可以访问,不可以修改
应用场景:
函数 test中p只可以访问外面的值,不可以改变外面的值
#include <iostream>
using namespace std;
void test(const int& p) {
// p = 30; 不可改变
cout << p << endl;
}
int main() {
int age = 20;
test(age)
return 0;
}
等价写法
int age = 10;
int const& r = age;
const int & r = age;
1) ref 不能修改指向,但可以通过ref间接修改所指向的变量
俩种写法一样 ref 本来就不能修改
int& const ref = age;
int& ref = age;
#include <iostream> using namespace std; struct Date { int year; int month; int day; }; void test(const int& p) { p = 30; cout << p << endl; } int main() { int age = 10; //ref 不能修改指向,但可以通过ref间接修改所指向的变量 //引用本身不能修改指向 int& const ref = age; int& ref = age; ref = 30; return 0; }
2)ref 不能修改指向,不可以通过ref间接修改所指向的变量严格限制
int const& ref = age;
1)常量
#include <iostream>
using namespace std;
int main() {
int age = 10;
int& ref =10; //报错 : 不可以,因为会被修改
const int& ref = 10;
return 0;
}
2)表达式
#include <iostream>
using namespace std;
int main() {
int a = 1;
int b = 2;
int& ref = a + b;//报错 : 不可以
const int& ref = a + b;
return 0;
}
2)函数返回值
#include <iostream>
using namespace std;
int func() {
return 0;
}
int main() {
int a = 1;
int b = 2;
const int& ref = func()
int& ref = func() //报错 : 不可以
return 0;
}
#include <iostream>
using namespace std;
int func() {
return 0;
}
int main() {
int a = 1;
const double& ref = a;
return 0;
}
可以接受const和非const实参(非const引用,只能接受非const实参)
#include <iostream>
using namespace std;
//在函数必须使用引用的参数时
int sum(const int& v1, const int& v2) {
return v1 + v2;
}
int main() {
int a = 1;
int b = 1;
sum(a, b);
sum(10, 20);
return 0;
}
可以跟非const引用构成重载
int sum(const int& v1, const int& v2) {
return v1 + v2;
}
int sum( int& v1, int& v2) {
return v1 + v2;
}
可以跟非const引用构成重载(此规则也适用于const指针)
void sum(const int* v1, const int* v2) {
}
void sum( int* v1, int* v2) {
}
#include <iostream>
using namespace std;
int main() {
int age = 10;
const int& ref = age;
age = 30;
cout << "age is " << age << endl;
cout << "ref is " << ref << endl;
return 0;
}
输出结果:
age is 30
ref is 30
#include <iostream>
using namespace std;
int main() {
int age = 10;
const long& ref = age;
age = 30;
cout << "age is " << age << endl;
cout << "ref is " << ref << endl;
return 0;
}
输出结果:
age is 30
ref is 10
原创不易,你的支持是作者更新的动力
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。