第6章 数组、指针与字符串(一)

第6章 数组、指针与字符串(一)

数组的定义与初始化

数组的定义与使用

数组是具有一定顺序关系的若干相同类型变量的集合体,组成数组的变量成为该数组的元素。

数组的定义
类型说明符 数组名[常量表达式][常量表达式]......;

数组名的构成方法与一般变量名相同。

  • 例如:int a[10];

    表示a为整型数组,有10个元素:a[0]…a[9]

  • 例如:int a[5][3];

    表示a为整型二维数组,其中第一维有5个下标(04),第二维有3个下标(02),数组的元素个数为15,可以用于存放5行3列的整型数据表格。

数组元素的使用
  • 数组必须先定义,后使用。

  • 可以逐个引用数组元素。

  • 例如:

    a[0] = a[5] + a[7] - a[2*3]
    b[1][2] = a[2][3]/2
    

例6-1

#include <iostream>
using namespace std;
int main(){
    int a[10], b[10];
    for(int i = 0; i < 10; i++){
        a[i] = i * 2 - 1;
        b[10 - i - 1] = a[i];
    }
    for(int i = 0; i < 10; i++){
        cout << "a[" << i << "] = " << a[i] << " ";
        cout << "b[" << i << "] = " << b[i] << endl;
    }
    return 0;
}
数组的储存与初始化
一维数组的存储

数组元素在内存中顺次存放,它们的地址是连续的。元素间物理地址上的相邻,对应着逻辑次序上的相邻。

例如:

int a[10];

在这里插入图片描述

一维数组的初始化
  • 列出全部元素的初始值

    例如:static int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

  • 可以只给一部分元素指定初值

    例如:static int a[10] = {0, 1, 2, 3, 4};

  • 列出全部数组元素初值时,可以不指定数组长度

    例如:static int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

二维数组的存储
  • 按行存放

    例如:float a[3][4];

    在这里插入图片描述

    其中数组a的存储顺序为:

    a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23

二维数组的初始化
  • 将所有初值写在一个{}内,按顺序初始化

    例如:static int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

  • 分行列出二维数组元素的初值

    例如:static int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12};

  • 可以只对部分元素初始化

    例如:static int a[3][4] = {{1}, {0, 6}, {0, 0, 11}};

  • 列出全部初始值时,第一维下标个数可以省略

    例如:static int a[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

    static int a[][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12};

  • 如果不做任何初始化,局部作用域的非静态数组中存在垃圾数据,static数组中的数据默认初始化为0

  • 如果只对部分元素初始化,剩下的未显式初始化的元素,将自动被初始化为零

例:求Fibonacci数列的前20项,将结果存放于数组中

#include <iostream>
using namespace std;
int main(){
    int f[20] = {1, 1};
    for(int i = 2; i < 20; i++)
        f[i] = f[i - 2] + f[i - 1];
    for(int i = 0; i < 20; i++){
        if(i % 5 == 0) cout << endl;
        cout.width(12);//设置输出宽度为12
        cout << f[i];
    }
    return 0;
}
           1           1           2           3           5
           8          13          21          34          55
          89         144         233         377         610
         987        1597        2584        4181        6765
一维数组应用举例
  • 循环从键盘读入若干组选择题答案,计算并输出每组答案的正确率,直到输入ctrl+z为止。
  • 每组连续输入5个答案,每个答案可以是‘a’…‘d’。
#include <iostream>
using namespace std;
int main(){
    const char key[ ]{'a', 'c', 'b', 'a', 'd'};
    const int NUM_QUES = 5;
    char c;
    int ques = 0, numCorrect = 0;
    cout << "Enter the " << NUM_QUES << " question test:" << endl;
    while(cin.get(c)){
        if(c != '\n'){
            if( c == key[ques]){
                numCorrect++; cout <<" ";
            }else
                cout << "*";
            ques++;
        }else{
            cout << " Score " << static_cast<float>(numCorrect)/NUM_QUES*100 << "%";
            ques = 0; numCorrect = 0;cout << endl;
        }
    }
    return 0;
}
Enter the 5 question test:
acbba
   ** Score 60%
acbad
      Score 100%
abbda
 * ** Score 40%
bdcba
***** Score 0%

数组作为函数的参数

  • 数组元素作实参,与单个变量一样。
  • 数组名作参数,形、实参数都应是数组名,类型要一样,传送的是数组首地址。对形参数组的改变会直接直接影响到实参数组。

例6-2 使用数组名作为函数参数

主函数中初始化一个二维数组,表示一个矩阵,并将每个元素都输出,然后调用子函数,分别计算每一行的元素之和,将和直接存放在每行的第一个元素中,返回主函数之后输出各行元素的和。

#include <iostream>
using namespace std;
void rowSum(int a[][4], int nRow){
    for(int i = 0, i < nRow; i++){
        for(int j = 1; j < 4; j++){
            a[i]a[0] += a[i]a[j];
        }
    }
}
int main(){
    int table[3][4] = {{1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4,5, 6}};
    for(int i = 0, i < 3; i++){
        for(int j = 0; j < 4; j++)
            cout << table[i][j] << " ";
            cout << endl;
    }
    rowSum(table, 3);
    for(int i = 0; i < 3; i++)
        cout << "Sum of row" << i << "is" << table[i][0] << endl;
    return 0;
}

对象数组

对象数组的定义与访问
  • 定义对象数组

    类名 数组名[元素个数];

  • 访问对象数组元素

    访问下标访问:数组名[下标].成员名

对象数组初始化
  • 数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。

  • 通过初始化列表赋值。

    例:Point a[2] = {Point(1, 2), Point(3, 4)};

  • 如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用默认构造函数)。

数组元素的构造和析构
  • 构造数组时,元素所属的类未声明构造函数,则采用默认构造函数。
  • 各元素对象的初值要求为相同的值时,可以声明具有默认形参值的构造函数。
  • 各元素对象的初值要求为不同的值时,需要声明带形参的构造函数。
  • 当数组中每一个对象被删除时,系统都要调用一次析构函数。

例6-3 对象数组应用举例

//Point.h

#ifndef _POINT_H
#define _POINT_H

class Point{
public:
    Point();
    Point(int x, int y);
    ~Point();
    void move(int newX, int newY);
    int getX() const{return x;}
    int getY() const{return y;}
    static void showCount();
private:
    int x, y;
};

#endif //_POINT_H

//Point.cpp
#include <iostream>
#include "Point.h"
using namespace std;
Point::Point(): x(0), y(0){
    cout << "Default Constructor called." << endl;
}
Point::Point(int x, int y): x(x), y(y){
    cout << "Constructor called." << endl;
}
Point::~Point(){
    cout << "Destructor called." << endl;
}
void Point::move(int newX, int newY) {
    cout << "Moving the Point to (" << newX << ", " << newY << ")" << endl;
    x = newX;
    y = newY;
}
//6-3.cpp
#include "Point.h"
#include <iostream>
using namespace std;
int main(){
    cout << "Entering main.." << endl;
    Point a[2];
    for(int i = 0; i < 2; i++)
        a[i].move(i + 10, i + 20);
    cout << "Exiting main.." << endl;
    return 0;
}
Entering main..
Default Constructor called.
Default Constructor called.
Moving the Point to (10, 20)
Moving the Point to (11, 21)
Exiting main..
Destructor called.
Destructor called.

基于范围的for循环

int main(){
    int array[3] = {1, 2, 3};
    int *p;
    for(p = array; p < array + sizeof(array) / sizeof(int); ++p)
    {
        *p += 2;
        std::cout << *p << std:endl;
    }
	return 0;
}
int main(){
    int array[3] = {1, 2, 3};
    for(int & e: array)
    {
        e += 2;
        std::cout << e << std:endl;
    }
	return 0;
}

指针的定义和运算

指针的概念、定义和指针运算
内存空间的访问方式:
  • 通过变量名访问

  • 通知地址访问

指针的概念
  • 指针:内存地址,用于间接访问内存单元
  • 指针变量:用于存放地址的变量
指针变量的定义
  • 例:

    static int i;
    

    在这里插入图片描述

在这里插入图片描述

  • 例:

    *ptr = 3;
    

    在这里插入图片描述

在这里插入图片描述

与地址相关的运算—“*”和“&”
  • 指针运算符:*
  • 地址运算符:&
指针的初始化和赋值
指针变量的初始化
  • 语法形式

    存储类型 数据类型 *指针名 = 初始地址;

  • 例:

    int *pa = &a;
    
  • 注意事项

    用变量地址作为初值时,该变量必须在指针初始化之前已经声明过,且变量类型应与指针类型一致。

    可以用一个已有合法值的指针去初始化另一个指针变量。

    不要用一个内部非静态变量去初始化static指针。

指针变量的赋值运算
  • 语法形式 指针 = 地址

  • 注意:

    “地址”中存放的数据类型与指针类型必须相符

    向指针变量赋的值必须是地址常量或变量,不能是普通整数

  • 例如:

    通过地址运算“&”求得已定义的变量和对象的起始地址

    动态内存分配成功时返回的地址

  • 例外:整数0可以赋给指针,表示空指针。

  • 允许定义或声明指向void类型的指针。该指针可以被赋予任何类型对象的地址。

    例:void *general;

指针空值nullptr
  • C++11使用nullptr关键字,是表达更准确,类型安全的空指针

例6-5 指针的定义、赋值与使用

#include <iostream>
using namespace std;
int main(){
    int i;
    int *ptr = &i;
    i = 10;
    cout << "i = " << i << endl;
    cout << "*ptr = " << *ptr << endl;
    return 0;
}
i = 10
*ptr = 10

例6-6 void类型指针的使用

#include <iostream>
using namespace std;
int main(){
	//!void voidObject;错,不能声明void类型的变量
    void *pv;
    int i = 5;
    pv = &i;
    int *pint = static_cast<int *>(pv);
    cout << "*pint = " << *pint << endl;
    return 0;
}
指向常量的指针
  • const指针

  • 不能通过指向常量的指针改变为所指对象的值,但指针本身可以改变,可以指向另外的对象。

  • 例:

    int a;
    const int *p1 = &a;
    int b;
    p1 = &b;
    *p1 = 1//编译时出错,不能通过p1改变所指的对象
    
指针类型的常量
  • 若声明指针常量,则指针本身的值不能被改变。

  • int a;
    int * const p2 = &a;
    p2 = &b;//错误,p2是指针常量,值不能改变
    
指针的算术运算、关系运算
指针类型的算术运算
  • 指针与整数的加减运算

  • 指针++,–运算

  • 指针p加上或减去n

    • 其意义是指针当前指向位置的前方或后方第n个数据的起始位置。
  • 指针的++、–运算

    • 意义是指向下一个或前一个完整数据的起始.
  • 运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置。

  • 当指针指向连续存储的同类型数据时,指针与整数的加减运算和自增自减运算才有意义。

指针与整数相加的含义

在这里插入图片描述

指针类型的关系运算
  • 指向相同类型数据的指针之间可以进行各种关系运算。
  • 指向相同类型数据的指针,以及指针与一般整数变量之间的关系运算是无意义的。
  • 指针可以和零之间进行等于或不等于的关系运算。
    • 例如:p == 0p != 0

综合实例

个人银行账户管理程序

  • 一个活期储蓄账户包括:
    • 信息:
      • 账号(id)、余额(balance)、年利率(rate)等
    • 操作:
      • 显式账户信息(show)、存款(deposit)、取款(withdraw)、结算利息(settle)等

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

设计难点:利息的计算1

  • 实现该类的难点在于利息的计算。由于账户的余额是不断变化的,因此:

    • 余额 × 年利率 = 年利? NO!

    • 一年当中每天的余额累积起来/一年的总天数=日均余额

      日均余额 × 年利率=年利 YES!

  • 例如:如果年利率是1.5%,某账户第5天存入5000元,第45天存入5500元,第90天结算利息。

    • 它第5天到第45天之间的余额为5000元(持续40天)
    • 它第45天到第90天之间的余额为10500元(持续45天)
    • 90天的利息是(40×5000 + 45×10500)×1.5% / 365 = 27.64元

设计难点:利息的计算2

  • 为了计算余额的按日累计值,SavingsAccount引入了:

    • 私有数据成员lastDate:存储上一次余额变动的日期
    • 私有数据成员accumulation:存储上次计算利息以后直到最近一次余额变动时余额按日累加的值
    • 私有成员函数accumulation:计算截至指定日期的账户余额按日累计值。
  • 当余额变动时,需要做的是将变动前的余额与该余额所持续的天数相乘,累加到accumulation中,再修改lastDate。

    • accumulation + balance * (date - lastDate);
      

在这里插入图片描述

#include <iostream>
#include <cmath>
using namespace std;
class SavingsAccount {
private:
    int id;
    double balance;
    double rate;
    int lastDate;
    double accumulation;
    void record(int date, double amount);
    double accumulate(int date) const{
      return accumulation + balance * (date - lastDate);
    }
public:
    SavingsAccount (int date, int id, double rate);
    int getId(){return id;}
    double getBalance(){return balance;}
    double getRate(){return rate;}
    void deposit(int date, double amount);
    void withdraw(int  date, double amount);
    void settle(int date);
    void show();
};
SavingsAccount::SavingsAccount(int date, int id, double rate):id(id), balance(0), rate(rate), lastDate(date), accumulation(0){
    cout << date << "\t#" << id << " is created" << endl;
} 
void SavingsAccount ::record(int date, double amount){
    accumulation = accumulate(date);
    lastDate = date;
    amount = floor(amount * 100 + 0.5) / 100;//保留小数点后两位
    balance += amount;
}
...
void SavingsAccount::settle(int date){
        double interest = accumulate(date) * rate / 365
        if(interest != 0){
            record(date, amount);
        accumulation = 0;
        }
}
int main(){
    SavingsAccount sa0(1, 21325302, 0.015);
    SavingsAccount sa1(1, 58320212, 0.015);
    sa0.deposit(5 ,5000);
    sa1.deposit(25 ,10000);
    sa0.deposit(45 ,5500);
    sa1.withdraw(60 ,4000);
    sa0.settle(90);
    sa1.settle(90);
    sa0.show(); cout << endl;
    sa1.show(); cout << endl;
    return 0;
}

实验六(上)

例题一:

编写矩阵转置函数,输入参数为3×3整型数组。

编写main()函数实现输入、输出。

提示;

使用循环语句实现矩阵元素的行列对调,注意在循环语句中究竟需要对哪些元素进行操作

#include <iostream>
using namespace std;
void swap(int& a, int& b){
    int temp = a;
    a = b;
    b = temp;
}
int main(){
    int a[3][3];
    cout << "输入9个整数作为矩阵元素值" << endl;
    for(i = 0; i < 3; i++)
        for(int j = 0; j < 3; j++)
            cin >> a[i][j];
        cout << "初始矩阵 : " << endl;
    for(i = 0; i < 3; i++){
        for(int j = 0; j < 3; j++)
            cout << a[i][j] << ' ';
        cout << endl;
    }
    for(i = 0; i < 3; i++){
        for(int j = 0; j < 3; j++)
            swap(a[i][j], a[j][i]);
    cout << "转置后的矩阵 : " <<  endl;
    for(i = 0; i < 3; i++){
        for(int j = 0; j < 3; j++)
            cout << a[i][j] << ' ';
        cout << endl;
    }
    return 0;
}
输入9个整数作为矩阵元素值
1 2 3 4 5 6 7 8 9
初始矩阵 :
1 2 3
4 5 6
7 8 9
转置后的矩阵 :
1 4 7
2 5 8
3 6 9

例题二:

声明一个Employee类,其中包括姓名、街道地址、城市和邮编等属性,以及change_name()和display()等函数。

display()显式姓名、街道地址和邮编等属性;

change_name()改变对象的姓名属性,实现并测试这个类。

//employee.h
#ifndef EMPLOYEE_H_
#def EMPLOYEE_H_

#include <iostream>
using namespace std;

class Employee{
    char* name;
    char* address;
    char* city;
    char* code;
public:
    Employee(char* n = " ", char* add = " ",  char* ct = " ", char* cd = " "): name(n), address(add), city(ct), code(cd){}
    void display(){
        cout << "name: " << name << endl;
        cout << "address: " << address << endl;
        cout << "city: " << city << endl;
        cout << "code: " << code << endl;
    }
    void change_name(char* nm){
        name = nm;
    }
};

#endif
#include <iostream>
#include employee.h
using namespace std;
int main(){
    Employee e("Wang Er", "Haidian", "Beijing", "100084");
    e.display();
    e.change_name("Li San");
    e.display();
    return 0;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值