c++编程基础

编程基础
第一个c++程序
#include //输入输出流头文件

int main()
{
std::cout << “Hello world!” << std::endl; //<<流操作符(字节的流向)
return 0;
}

流的概念及用途:
c++的I/O是以字节流的形式实现的
输入:磁盘->内存。
输出:内存->磁盘。
输入输出都是相对于内存而言的。
这里“流”试图说明字符随着时间顺序生成或消耗的.
输人/输出系统的任务实际上就是以一种稳定、可靠的方式在设备与内存之间传输数据
C++并没有直接定义进行输入输出的任何语句,这些功能是由标准IO库中的 来完成的
#include
iostream library
标准库定义了4个IO对象:
cin, cout, cerr, clog。
命名空间
 命名空间:实际上就是一个由程序设计者命名的内存区域,程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其他全局实体分隔开来。
 命名空间是ANSIC++引入的可以由用户命名的作用域,用来处理程序中常见的同名冲突。
std::cout std::cin std::endl
 使用命名空间:
using std::cout;
using namespace std;
using namespace std;

int a = 100;

int main(void)
{
int a = 5;
cin >> a;
cout << ::a << endl; //输出的是全局变量的a std是作用域 ::是作用符
return 0;
} //endl表示这一行结束,相当于c中的’\n’。 >>是流入运算符。

c++程序的执行过程
c++主要有三个编译阶段:预处理、转义成目标代码和链接。
c++预处理命令和c基本相同。

Linux下的编译c++程序:
g++ hello.cpp
c++类与对象的关系:

c语言:以函数为中心。
c++:以对象为中心。
c/c++的字符串操作比较:
int main()
{
string str = “hello”;
string str2;

  cout << str.length() <<endl;   //求字符串长度

  cout << str.append(" world!") <<endl;  //在字符串后追加字符串

  str2 = str.substr();         //字符串拷贝
  cout << str2 <<endl;

  cout << str2.compare(str) <<endl; //字符串比较相同为0 不同为1

  int where = str2.find("hello"); //差找字符串找到为0 找不到为-1
  cout << where << endl;

}
/*输出结果:
5
hello world!
hello world!
0
0
*/

int main(void)
{

    char str[20] = "hello";
    char str1[20] = "world!";
   
    char str2[20];
    
    int n;
    
    printf("%d\n%s\n",strlen(str),str);  //求字符串长度

    strcat(str, " world!");   //追加字符串
    printf("%d\n%s\n",strlen(str), str);

    n = strcmp(str, str1);   //比较字符串
    printf("%d\n",n);

    strcpy(str2,str1);   //拷贝
    printf("%s\n",str2);
    
    strncpy(str2, str, 6);//拷贝一定长度字节
    printf("%s\n",str2);
    
    return;

}
/*输出结果:
5
hello
12
hello world!
-1
world!
hello
*/

c/c++数据类型与变量
c/c++数据类型:
c++是强类型语言(编译器)【任何一个变量都得有明确的类型】。

bool型:
bool b = true;
cout << b << endl; //真为1
bool c = false;
cout << c << endl; //假为0

c/c++变量
程序运行过程中值能否发生改变分为常量和变量
从变量作用域的大小考虑:全局变量,局部变量
全局变量:定义在所有的函数体之外,它们在程序开始运行时分配存储空间,在程序结束时释放存储空间
函数中定义的变量称为局部变量(Local Variable)
从变量的生命周期考虑: 静态生存周期和动态生存周期
动态存储变量:变量仅在需要的时候分配和占用内存
静态存储变量:变量在程序运行过程中占用固定的内存
从变量的在内存中位置考虑:普通变量与指针变量
c/c++运算符

如果不知道优先级就加括号。
动态内存分布
动态内存分配是指在程序运行期间根据需要随时申请内存,在不需要时释放内存。
malloc/free是C++语言的标准库函数,不是运算符,不在编译器控制权限之内。(编译器检测不到哪里出了问题)(malloc分配空间后会返回空指针类型,没有步长概念。)
new/delete是c++的运算符
用于申请动态内存和释放内存
语法格式:int *p = new int; int *p = new int[30];
delete用来释放申请的空间:delete p; 释放创建的对象数组:delete []p; 若 delete p; 则只释放了第一个元素,会造成内存泄漏。
注意:在释放空间后要让p指向空 p = NULL;否则会变成野指针还是指向原来的空间,一直到p的生命周期结束才会释放!
int *p = new int(5);
char *pr = new char[10];
strcpy(pr,“hello”);
cout << p << endl;
cout << &p << endl;
cout << *p << endl;

   delete p;
   delete []pr;
  cout << "释放后:"<<endl;
    cout << p << endl;
   cout << &p << endl;
   cout << *p << endl;

   p = NULL;
   pr = NULL;
   cout << "p指向空后:"<<endl;
    cout << p << endl;
   cout << &p << endl;
   cout << *p << endl;

/*结果:
0x730d70
0x68fed8
5
释放后:
0x730d70
0x68fed8
7540528
p指向空后:
0
0x68fed8

*/

常见错误:
编译错误:编译器造成的。语法错误。
内存错误:操作系统造成的。
段错误:越界、野指针。
内存泄漏/碎片。
逻辑错误。
杜绝野指针
指针是用来存放某个变量的地址的值的一种变量。
野指针不是NULL指针(0指针),是指向垃圾内存的指针。
野指针的危险之处在于if语句对它不起作用。
任何指针在被创建时不会自动变成NULL指针,他是随机的,所以指针变量在创建时应当被初始化或设置为NULL,要么指向合法内存。
char *p = NULL;
char *str = (char *)malloc(100);

指针p被free或delete之后,应设置为NULL。
指针操作超越了变量的作用范围
指针不能返回指向栈内存的指针或引用。
数组与指针
数组属于一种数据结构。
指针是用来存放某个变量的地址的一种变量。
int a[100]; //a是常量指针不能a++

int *p = a; //p是变量指针可以进行p++

int fun(int a[100])//形参中的数组退化成指针,可以进行a++

任何一个数组的名字是一个常量指针,其值是该数组的首元
素的地址值
在C++中引用一个数组元素有两种等价的方法:
下标法:如a[j]或p[j]形式,比较直观
指针法:如*(a+j)或 *(p+j)形式,比较高效
数组名作为函数形参时,在函数体内,其失去了本身的内涵,
仅仅是一个指针。
指向数组的指针则是另外一种变量类型(在linux或win32平
台下,长度为4),仅仅意味着数组的存放地址;
数组指针与指针数组
数组指针:
int a[100];
int *p = a; //p就是一个数组指针,指向数组的指针。

指针数组:
int *p[100]; //存放指针的数组。

总结:
new运算符根据对象的类型,自动决定其大小,比使用sizeof运算符,而malloc要指定分配存储空间的大小。
new返回指向此类型的指针,不能进行强制类型转换。malloc返回指向void * 类型的指针。
如果在申请内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。
用free或delete释放内存后,没有将指针设置为NULL。导致产生’野指针’。防止使用指针值为NULL的内存。
动态内存的申请与释放必须配对,防止内存泄露。
引用与函数传参
在c++中变量可以没有变量名,可以有一个变量名,也可以有多个变量名。
int(5);
什么时引用
引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
引用中(&)是起标识作用,而不是求地址运算。
cout << “int(5):” <<int(5) <<endl;
int a = 3;
int &m = a;
cout << “m:” << m << endl;
int n = m;
int *p = &m;
cout << “*p:”<<*p<<endl;
cout << “p:” << p<<endl;
m = m + 5;
cout << “a:” << a << endl;
/*输出结果:
int(5):5
m:3
p:3
p:0x68fed0
a:8
/

引用详解:
1.初始化与赋值
定义引用时必须初始化;
可以将一个引用赋予给某个变量;
引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。还可以对引用引用。
2.指针和引用的区别
指针通过地址间接访问某个变量
引用通过别名直接访问某个变量
3.引用一般用作函数的参数或函数的返回值
4.如果既要利用引用提高使用效率,又要保护传递给函数的数据不在函数中被改变,就应当使用常引用。
5.引用没有分配空间,常引用不同类型时会自己创建空间。
引用的用途
C语言中没有引用,C++中才有引用,引用的主要用途就是在函数传参和返回值上。(引用可以避免频繁的申请释放空间,从而提高了效率)
使用引用作为传递函数的参数,在内存中并没有产生实参的副本,他是直接对实参操作。
如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程(临时变量的申请和释放),从而提高效率。
如果既要利用引用调高使用效率,又要保护传递给函数的数据不在函数中被改变,就应当使用常引用。
如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率,有些场合不可以。
常引用
声明一个引用不是新定义一个变量,他只是该引用名是目标变量的一个别名,它本身不是一种数据类型,因此引用不占存储单元。故对引用取地址,就是对目标变量取地址。
非const引用只能绑定到该引用同类型的变量。const引用则可以绑定到不同但相关类型的对象, 或着绑定到右值。
常引用的声明方式:const 类型标示符 &引用名 = 目标变量名;
用这种方式声明的引用,不能通过引用对变量的值进行修改,但目标自身任然能够改变,这是语言的不一致性。
#include
using namespace std;
int main()
{
//测试一:
//int &p1 = 20;//错误,非const引用只能绑定到该引用同类型的对象,而20是常量

//测试二:
const int &p = 30;
cout<<p<<endl; //可以,const引用则可以绑定到不同,但相关的类型的对象或者绑定到右值

//测试三:
//double dvalue=3.14;
//int &pd=dvalue;//error,类型不同
//cout<<pd<<endl;

//测试四:
double dvalue2=3.14;
const int &pp=dvalue2;//ok
dvalue2 = 50.6;
cout<<pp<<","<<dvalue2; //3,50.6,因为转换的时候p引用的是dvaule2的一个副本

//测试五
int a = 20;
const int &pa = a;
//pa = 30;//错误,pa是一个常量引用,不可以通过pa改变目标值

//测试六
int a2 = 20;
const int &pa2 = a2;
a2 = 40; //ok,常量引用对自身没有影响
return 0;
}

函数返回一个类型的引用:
int &fun(const int v)
{
int t = v;
return t;
}

  int main()
  {        
       int n = fun(888);  //m和n是整型有地址,所以输出结果正常
      int m = fun(999);
      cout << "n=" << n <<endl;
      cout << "m=" << m <<endl;
  }
  /*
    888
    999
  */

int &fun(const int v)
{
int t = v;
return t;
}

int main()
{
int &n = fun(888);//m和n是引用没有地址,所以输出结果不确定
int &m = fun(999);
cout << “n=” << n <<endl;
cout << “m=” << m <<endl;
}
/*输出结果:
n=999
m=4199040
*/

总结
int *p = new int(5)

const int p = new int(5)//p是指向常量的指针,不能通过p修改右边的值
//但是可以指向其他变量

int * const p = new int(5)//p是指针常量,不可以指向别人,但可以改变指向的值。

如果参数是指针,且仅做输入用,则应在类型前加const,以防止该指针在函数体内被意外修改。
如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中改变就应用常引用。
如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率,有些场合不可以(局部变量)。
危险的引用方法:
返回一个局部变量的引用。
return语句不可以返回指向栈内存的指针,或者引用,因为该内存在函数体结束时被自动销毁
c/c++函数说明
C/C++中的函数说明:
四个组成部分:返回类型、函数名、形式参数表、函数体
函数的申明与实现,函数的调用与传参(形参与实参)
函数与指针:指针函数与函数指针

C++增强了函数类型:
基础函数:内联函数,函数重载,模板函数(泛型编程)
成员函数:构造/析构函数,常成员函数,静态成员函数,虚函数
指针函数和函数指针:
指针函数是返回指针的函数。
函数指针是指向函数的指针。
函数指针的例子:
typedef void(*ptr)(int);//void代表函数返回值是空,int代表形参是int型

void fun(int a)
{
printf("%d\n",a);
}
int main()
{
ptr p = fun;
p(1);
}

内联函数
内联(内嵌)函数,主要解决的是程序的运行效率问题。
解决手段,在编译阶段,编译器就会把每次调用内联函数的地方都替换为该函数体内的代码。
引入原因:解决程序中一些函数体代码不是很大,却有被频繁调用的函数的函数调用效率问题。
解决方法: 以目标代码的增加为代价来换取时间上的节约。

当在一个函数定义或声明前加上关键字inline,则把该函数定义为内联函数。
内联函数和带参宏
带参宏:
#include
using namespace std;
#define f(x) xx //x是没有类型的
//#define f(x) (x)
(x) //这样会输出正确结果
int main()
{
int x(2);
cout<<f(x)<<endl;
cout<<f(x+1)<<endl;
}
程序运行结果:
4
5
原因:
f(x) 替换为 22
f(x+1)替换为2+1
2+1

内联函数:
#include
using namespace std;
inline int f(int x)
{
return xx;
}
int main()
{
int x(2);
cout<<f(x)<<endl;
cout<<f(x+1)<<endl;
}
程序运行结果:
4
9
原因:
f(x)替换为2
2
f(x+1)替换为3*3

使用内联函数的注意事项
内联函数的定义必须出现在该函数第一次被调用前;
内联函数不能有复杂的控制语句,如switch,goto和while。
递归函数不能是内联函数。类结构中所有的在类说明内部定义的函数都是内联函数。
内联函数具有与带参的宏定义#define相同的作用和相似的机理,但内联函数具有宏定义所没有的优点而没有缺点,它消除了宏定义的不安全性。
#define MAX 100; //MAX没有类型使用时要注意
const int MAX = 100; //c++中一般这样使用。

函数重载
#include

using namespace std;

int add(int,int);
double add(double,double);

int main()
{
cout<<add(5,10)<<endl;
cout<<add(5.0,10.5)<<endl;
}
int add(int x,int y)
{
cout<<“int”<<endl;
return x+y;
}
double add(double x,double y)
{
cout<<“double”<<endl;
return x+y;
}

函数重载又称为函数的多态性
指同一个函数名对应着多个不同的函数。
“不同”是指这些函数的形参表必须互不相同,或者是形参的个数不同,或者是形参是类型不同,或者是两者都不相同,否则将无法实现函数重载。
下面是合法的重载函数:
int funcl (int ,int);
int funcl(int );
double funcl(int,long);
double funcl(long);

重载函数的类型,即函数的返回类型可以相同,也可以不同。但如果仅仅是返回类型不同而函数名相同、形参表也相同,则是不合法的,编译器会报“语法错误”。如:
int funcl (int a,int b);
double funcl (int a,int b);

除形参名外都相同的情况,编译器不认为是重载函数,只认为是对同一个函数原型的多次声明。
在调用一个重载函数funcl()时,编译器必须判断函数名funcl到底是指哪个函数。它是通过编译器,根据实参的个数和类型对所有funcl()函数的形参一一进行比较,从而调用一个最匹配的函数。
带默认参数值的函数
在C++语言中调用函数时,通常要为函数的每个形参给定对应的实参。若没有给出实参,则按指定的默认值进行工作。
当一个函数既有定义又有声明时,形参的默认值必须在声明中指定,而不能放在定义中指定。只有当函数没有声明时,才可以在函数定义中指定形参的默认值。
默认值的定义必须遵守从右到左的顺序,如果某个形参没有默认值,则它左边的参数就不能有默认值。如:
void funcl(int a,double b=4.5,int c=3);//合法
void funcl(int a=1,double b,int c=3);//不合法

在进行函数调用时,实参与形参按从左到右的顺序进行匹配,当实参的数目少于形参时,如果对应位置形参又没有设定默认值,就会产生编译错误;如果设定了默认值,编译器将为那些没有对应实参的形参取默认值。
注意
形参的默认值可以是全局常量、全局变量、表达式、函数
调用,但不能为局部变量。例如,下例不合法:
void funcl()
{
int k;
void g(int x=k);//k为局部变量
}

使用缺省参数时,主要满足函数重载条件;
void fun (int x, int y=0);
void fun(int x);

此时函数fun不能进行重载,因为编译器不能唯一确定调用哪个函数(fun(3)或fun(3,0)均可)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值