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()打印数组
  

 

 

posted @ 2013-06-25 12:38  夜雨阑珊  阅读(205)  评论(0编辑  收藏  举报