C++笔记 2
1、程序由函数组成,函数只完成自己特定的功能即可
把函数声明写在头文件里(想使用函数时,可直接导入头文件,调用函数),把函数实现写在".cc"文件中
把多个".cc"文件编译成可执行文件 ->分别编译成".o"文件,再连接到一起
2、值传递
函数中的参数传递是值传递,形参只是实参的一份拷贝数据,在函数中改变形参的值,对实参无影响
3、作业分析:显示层(与用户的交互)
操作数据(完成业务逻辑) biz层
数据(id , password , balance )
Bank实现代码
================================================================
biz.h
================================================================
//operation
/* p : Password of account .
* b : balance of account .
* return : id of account .
*/
long create( int p , double b );
void save( double sum ) ;
/*
* return : 0 success , otherwise -1 returned .
*/
int withdraw( int p , double sum ) ;
double query( int p ) ;
long generateId();
================================================================
biz.cc
================================================================
#include <iostream>
using namespace std;
static long id ;
static int passwd ;
static double balance ;
#include <iostream>
using namespace std;
long generateId(){
static int id = 1 ;
return id++ ;
}
long create( int p , double b ){
id = generateId();
passwd = p ;
balance = b ;
return id ;
}
void save( double sum ){
balance += sum ;
}
int withdraw( int p , double sum ){
if( p != passwd ){
cout<<"invalid password ." << endl;
return -1 ;
}
if( balance < (sum + 10) ){
cout<<"no enough money ." << endl;
return -1 ;
}
balance -= sum ;
return 0 ;
}
double query( int p ){
if( p != passwd ){
cout<<"invalid password " << endl;
return -1 ;
}else{
return balance ;
}
}
================================================================
menu.h
================================================================
int showMenu();
void createMenu();
void saveMenu();
void withdrawMenu();
void queryMenu();
================================================================
menu.cc
================================================================
#include "biz.h"
#include <iostream>
using namespace std;
int showMenu(){
cout<<"create ------> 1 " << endl;
cout<<"save ------> 2 " << endl;
cout<<"withdraw ----> 3 " << endl;
cout<<"query -------> 4 " << endl;
cout<<"exit --------> 0 " << endl;
cout<<"enter your choice >";
int c ;
cin>>c ;
if( !cin ){
return -1 ;
}else{
return c ;
}
}
void createMenu(){
int passwd ;
double balance ;
cout<<"\tenter password >";
cin>>passwd ;
cout<<"\tenter balance >";
cin>>balance ;
long id = create( passwd , balance );
cout<<"======================"<<endl;
cout<<"create account ok , id = " << id <<endl;
cout<<"======================"<<endl;
}
void saveMenu(){
double sum ;
cout<<"\tenter sum >";
cin>>sum ;
save( sum ) ;
cout<<"======================"<<endl;
cout<<"save money ok "<<endl;
cout<<"======================"<<endl;
}
void withdrawMenu(){
int passwd ;
double sum ;
cout<<"\tenter password >";
cin>>passwd ;
cout<<"\tenter sum >";
cin>>sum ;
int ret = withdraw( passwd , sum ) ;
if( ret == 0 ){
cout<<"========================"<<endl;
cout<<"withdraw successful . "<<endl;
cout<<"========================"<<endl;
}
}
void queryMenu(){
int passwd ;
cout<<"\tenter password >";
cin>>passwd ;
double ret = query( passwd ) ;
if( ret != -1 ){
cout<<"============================"<<endl;
cout<<"BLANCE : $ " << ret << endl;
cout<<"============================"<<endl;
}else{
cout<<"============================"<<endl;
cout<<"invalid password "<<endl;
cout<<"============================"<<endl;
}
}
================================================================
main.cc
================================================================
#include <iostream>
#include "menu.h"
using namespace std;
int main(){
int c = 0 ;
do{
c = showMenu();
if( c == -1 ) { break ; }
switch( c ){
case 1 :
createMenu();
break;
case 2 :
saveMenu();
break;
case 3 :
withdrawMenu();
break;
case 4 :
queryMenu();
break;
case 0 :
cout<<"===================="<<endl;
cout<<"Good Bye "<<endl;
cout<<"===================="<<endl;
break;
default :
cout<<"===================="<<endl;
cout<<"invalid option, try again.";
cout<<endl;
cout<<"===================="<<endl;
break;
}
}while( c != 0 );
return 0 ;
}
================================================================
4、数组
(1)声明数组 <元素类型> 数组名[元素个数] int intArray[100]; -->intArray 是个集合,有100个元素,每个元素都是int类型的
(2)初始化数组
(3)使用 通过数组的下标来访问数组中的元素,下标从0开始 intArray[0]=100; -->intArray数组中的第一个元素赋值为100
数组声明时,元素个数必须是常量表达式
数组声明带有初始化,则可直接为数组赋值
在数组声明时,必须指明数组长度,若在声明时候初始化,数组长度可省
int a1[2]={100,200}; 长度2
int a2[] = {5,6,7}; 长度3
对于数组中的元素个数,比声明时的多,则会报错,这样的越界访问,对整个程序来说会有很严重的后果!!!
比声明少,系统会把余下的没有定义数据的元素初始化为0
不初始化的数组,其中的元素数据为随机数
下标工作的原理:
表示编号,还表示当前元素相对于数组起始位置的偏移量
计算机通过偏移量找到元素在内存中的位置
5、数组的排序
选择排序:找出最小的放在第一个位置
数组元素有n个,需要找n-1次,需要比较n-i次(i为开始元素的位置)
6、多维数组
二维数组;一个数组中的每个元素是个数组
声明: int iA[5][10]; -->“5”代表数组有5行,“10”代表数组有10列
声明时,第一维可以省略!
确定一个元素需要2个下标
7、结构
用户自己定义的一组数据类型
声明结构时,编译器不会分配空间,在声明一个结构的变量的时候才会为其分配空间
结构中的成员是多个简单类型组成的
用 “结构名.成员名”来操作其中的成员变量
strcpy(p.name,"huxz");为char数组赋值
结构类型的变量间也可以相互赋值
结构的大小就是所有成员的大小之和(每个成员的大小必须是int类型的整倍数,当不够时,会自动补齐)
Unix上还要求,结构的大小是结构的最大的成员的整倍数
size(of) 计算结构大小
作业:把银行系统用结构改写,要求可以开多个账户(定义一个account类型的数组保存)
struct account{
long id;
int password;
double balance;
}
create(int password,double balance);
void save(int id , double sum);
int withdraw(int id,int password,double sun);
double query(int id,int password);
1、变量的存储
(1)内存是一块空间,把其中的每个字节做了编号,为了以后计算机能通过编号找到数据
(2)编址方式:绝对编址(在整个程序中使用),相对编址(字节相对于逻辑0偏移量,在进程中使用)
2、取变量地址
(1)"&" &i 表示取内存中i的地址
地址的编址用十六进制表示
(2)逻辑0在代码区
全局变量在数据区,地址的编址是大于0的
局部变量在栈区,地址的编址是小于0的
3、数组、结构的地址
(1)数组中的数据在内存中是连续存储的。 数组中每个元素的地址相差的值应为数组元素类型的大小。
(2)结构的地址:
结构的空间是连续的。
结构的起始地址与第一个成员变量的地址是一样的。
4、存储地址—
指针:存储变量的地址
指针的类型由将要保存的地址的变量类型决定
int*只能保存int变量的地址
指针赋值一定要是同类型的指针才能相互赋值!
5、指针的运算
(1)指针和指针之间的运算
“+”,“*”,“/” 指针与指针间是不能做这些运算,没有意义!
“-” 可以做减法运算,以“sizeof(指针类型)”作为计算单位的! 注意:要同类型的指针才能做此运算,不同的话,会对运算单位产生歧义。
(2)指针和数字之间的运算(加、减都可以)
int i = 100;
int * p = &i;
打印 p+1 -> 相当于在地址上加4,因为存储的变量是int类型的
p+2 -> 相当于在地址上加8
6、通过指针访问所指向的变量
*p 代表指针p所指向的变量 *p <=> i
指针在声明的时候,即初始化
int * p = NULL; 表示没有明确指向,不能 *p ,会出现 “段错误”的异常 -->空指针
段错误原因 (1)空指针
(2)数组越界
(3)递归的条件不正确
7、课堂练习
用指针打印出数组中个元素的值
#include <iostream>
using namespace std;
int main(){
int ai[6]={34,4,12,67,34,2};
int *p = &ai[0];
for(int i = 0 ; i < 6 ; i++){
cout <<"a[" << i << "]=" <<*(p+i) << endl;
}
return 0;
}
int *p = ai ; 数组的本质就是用指针实现的,数组的名字就代表数组的首地址(起始地址)
数组的名字是指向数组首地址(a[0])的指针
ai 数组名,就是指向数组首地址的指针,可以用下标取元素,也可以把数组名当指针来访问元素 *(ai+n)
p 指针名,也是指向首地址的指针,也可以通过下标(像数组名一样)访问数组元素
p[n] <=> *(p+n)
8、结构指针
struct person{
int id;
int age;
}
int main(){
person per = {1,20};
person* p = &per;
cout << "per.id ="<<per.id<<endl; //通过结构名取成员变量
cout << "per.age=" << per.age <<endl;
cout <<"======================"<<endl;
cout << "(*P).id=" << (*P).id <<endl; //通过指针访问结构的成员变量
cout << "(*P).age=" << (*P).age <<endl; // (*p).id <=> p->id 只有结构指针可以这样使用
cout <<"======================"<<endl;
cout << "p->id=" << p->id <<endl;
cout << "p->age=" << p->age <<endl;
return 0;
}
9、指针的地址
指针变量在内存中占4个字节(与类型无关,因为保存地址的指针只保存地址)
保存int型指针(int* p = &i)的地址用int**保存(int** pp = &p)
#include <iostream>
using namespace std;
int main(){
int i = 0 ;
int * p = &i ;
int ** pp = & p ;
cout<<"&i = " << &i << endl;
cout<<"p = " << p << endl;
cout<<"&p = " << &p << endl;
cout<<"pp = " << pp << endl;
cout<<"&pp = " << &pp << endl;
cout<<"i = " <<i << endl;
cout<<"*p = " << *p << endl;
cout<<"*pp = " << *pp << endl;
cout<<"**pp = " << **pp << endl;
return 0 ;
}
执行结果:
&i = 0xffbffbec
p = 0xffbffbec
&p = 0xffbfbe8
pp = 0xffbfbe8
&pp = 0xffbffbe4
i = 0
*p = 0
*pp = 0xffbffbec
**pp = 0
pp -> p -> i 指向关系
pp=&p p=&i
*pp=p *p=i **pp=*p=i
1、数组指针声明的时候不用初始化,声明以后就指向数组的首地址了,以后不允许改变,所以,数组指针可以认为是一个常量,一旦赋值就不能改变
2、char数组
(1)打印char数组的名字即打印数组的内容
(2)对于字符数组,'\0'是结束标志
字符 '\0' = 数组0 可以认为字符'\0'的ASCII码就是0
要保存5个字符,就要把字符数组长度声明为6
(3)strcpy()和memset()
给一个字符串数组赋值 strcpy(),自动为字符串补 '\0'
在使用strcpy之前,要调用memset(str,0,sizeof(str));初始化一片内存
str -> 初始化内存的起始位置
0 -> 初始化的值 (初始化为0,清除垃圾数字)
sizeof(str) -> 初始化空间的大小
(4)strlen(str)
计算实际存储的字符个数,不包括'\0'
(5)strcmp(str1,str2)
比较2个字符串是否相等
实际比较的两个字符串的ASCII码的大小,返回0则内容一样
(6)字符串的拆分 strtok(a,b);
a、被拆分的字符串名字,当此处是NULL时,表示要拆分的不是新串
b、分隔符号
此函数调用一次,拆分一次
当此函数返回NULL时,则表明拆分完毕
"Hello World"叫字符串常量,存储在data数据区里,类型就是char*
其值是一个地址,指向数据区里的一块空间
数据区里的空间保存的内容就是"Hello World"
char line[50];
strcpy(line,"1:liucy:20:male");
char *p = strtok(line , ":"); //p指针指向的变量
cout << p << endl;
while(p != NULL){
p = strtok ( NULL , ":" );
cout << p << endl;
}
(7) strcpy(name,"1234"); //name是个变量,可以改变
strcpy(p,"1234"); //p是个指针,指向一个字符串,是常量,不能改变
p = "1234"; //这样是正确的
指针指向的是常量,不能通过指针改其值,若指向变量,则可以通过指针改变变量的值
(8)字符串连接 strcat(sub,"world");
连接条件:sub字符串数组的剩余长度要大于连接的字符串长度
char *p = "Hello";
strcat(p,""World); //error 指针指向一个常量,不能改变,所以指针后面不能添加东西
3、通过指针传递参数
通过传递地址。改变变量的值
#include <iostream>
using namespace std;
void fn (int *pa){
*pa = 2 * (*pa); //*pa是指针pa指向的变量的值,在此做的操作,会对变量造成永久的改变
}
int main(){
int a = 100;
fn(&a); //把a的地址传个fn函数
cout << a << endl;
return 0;
}
4、课堂练习
字符串 “1:huxinzhe:20:male”
要求:声明一个结构
Person{
int id;
char name[50];
int age;
char gender[10];
}
拆分赋值 提示:atoi可以把字符串转换成int型
#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;
struct Person{
int id;
char name[20];
int age;
char gender[10];
};
int main(){
Person per;
char line[50];
while(true){
cout << "enter a string>";
cin>>line;
if(strcmp(line,"exit")==0)
break;
char *p = strtok(line , ":");
per.id = atoi(p);
p = strtok(NULL , ":");
strcpy(per.name,p);
p = strtok(NULL , ":");
per.age = atoi(p);
p = strtok(NULL , ":");
strcpy(per.gender,p);
cout <<"per.id = " <<per.id << endl;
cout <<"per.name = " <<per.name << endl;
cout <<"per.age = " <<per.age << endl;
cout <<"per.gender = " <<per.gender << endl;
}
return 0;
}
5、内存管理
堆(heap):动态的内存申请与释放
堆空间不能通过像( 数据区,栈)变量访问空间,要使用指针保存地址
(1)堆空间的管理 new / delete
new int
new Person
new 操作符返回值是地址,用指针保存
int *p = new int; //在堆空间中申请4个字节
*p = 100; //赋值
delete p; //释放空间
(2)动态申请空间,对内存的使用变的方便,能更好的控制对内存的占用
动态内存的指针不能重复赋值,这样会造成内存丢失
释放空间后,还可以通过指针访问原来的数据。
释放空间后,还可以再次通过指针赋值,释放的意思就是表示曾经申请的内存的标记位更改,别人可以使用
这样会造成过期的指针更改重要数据,建议在释放指针以后 ,把指针赋值为NULL ,确保数据安全
delete p ;
p = NULL :
(3)在申请空间的同时初始化
int *p = new int(123); 申请4个字节大小,保存数字“123”
(4)int *p = new int[10]; 一次在堆里申请10个int,并且申请的内存是连续的。
*(p+1) , p[1] 都能取到下一个数据
此时的*p就是第一个元素的值
在释放的时候 delete[] p; p = NULL; 确保释放的是多个指针
(5)在堆空间里申请结构
Person *p = new Person;
p->id = 1;
// (*p).id = 1;
6、 传递参数
最好传地址,节省内存(因为不用进行入栈,出栈操作,直接用指针访问数据)
在参数前加const,可避免在函数内部对数据进行非法修改
当一个数组做参数时,编译器会自动把数组的首地址传个函数
所以在函数内部对数组的更改,就是通过指针对数组中元素的永久性更改
7、数组,存数据个数不限,通过一个函数insert()存数据,disp()打印数组