360 likes | 554 Views
C++ 语言基础 —— 补充知识. 运行时内存 指针和内存地址 指针地址 指针和数组 动态内存分配 函数调用和内存 结构体 及其 调用 ( 复合数据类型) 输入输出( cout 和 cin )( VC++ 类库的使用). 程序的内存分配. 一个由 c/C++ 编译的程序占用的内存分为以下几个部分 : 1 、栈区( stack ) 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。 其操作方式类似于数据结构中的栈。 2 、堆区( heap )
E N D
C++语言基础——补充知识 • 运行时内存 • 指针和内存地址 • 指针地址 • 指针和数组 • 动态内存分配 • 函数调用和内存 • 结构体 及其 调用 (复合数据类型) • 输入输出(cout 和 cin)(VC++类库的使用)
程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分: 1、栈区(stack) 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。 其操作方式类似于数据结构中的栈。 2、堆区(heap) 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。 3、全局区(静态区)(static) 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放。 4、文字常量区 常量字符串就是放在这里的。 程序结束后由系统释放。 5、程序代码区 存放函数体的二进制代码。
例 int a = 0; 全局初始化区char *p1; 全局未初始化区 main() { int b; 栈char s[] = "abc"; 栈char *p2; 栈char *p3 = "123456"; 123456\0在常量区,p3在栈上。 static int c =0; 全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); 分配得来得10和20字节的区域就在堆区。strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的 "123456"优化成一个地方。}
C++语言基础——补充知识 • 运行时内存 • 指针和内存地址 • 指针地址 • 指针和数组 • 动态内存分配 • 函数调用和内存 • 结构体 及其 调用 • 输入输出(cout 和 cin)
0000002 65 a 0 0000003 0 0000004 b 0 0000005 1 0000006 0 0000007 0 0000008 c 0 0000009 2 0000010 内存地址 int main(){ char a = ‘A’; int b = 1; float c = 2; double d = 3; return 0; } 值:内存单元中存放的数据 地址:系统分配给变量的内存单元的起始地址
0000002 0 0000004 65 000000E 0000002 指针变量 存放地址的变量称为指针变量,它用来指向另一个变量,通常用*表示。 int main() { int i=65; int*i_pointer; i_pointer = &i; return 0; }
定义后 p=&i *p=3 j=*p+2 i 1000 i 1000 3 i 1000 3 i j j j 5 j 1004 1004 1004 p 1000 p 1000 p 1000 p 1008 1008 1008 指针操作 int i, j, *p; p=&i; *p=3; j=*p+2; &:取变量的首地址,即指针; *:取指针所指向的地址中的值; 1000 1004 1008
课堂练习 • 运行时内存一般分为哪几部分,各有何作用? • 请绘制以下程序内存示意图。 void swap(int a, int b){ int temp; temp = a; a = b; b = temp; } int main(){ int m, n; scanf(“%d %d”, &m, &n); swap(m, n); printf(“%d %d”, m, n); return 0; } 3. 请绘制右边程序段内存示意图。 int i=65; int*i_pointer; i_pointer = &i; char kk = ‘A’; char *pKK = &kk; char jj = *pKK;
指针ptr-2 指针pt+5 指针ptr+1 1020 992 1004 指针ptr 1000 992 1000 变量i占4字节 ··· ··· 1020 图4.4 指针的算术运算 指针和数组 • 数组元素在存储器中是连续存放的。组名是一个地址常量(指针常量),其值为数组中第一个元素的地址。数组的这些性质以及指针算术运算的特点,使得利用指针可以方便地访问数组元素。
指针与数组的等价性 // &buf[0] 与pbuf 具有相同的意义。 // 数组名不能运算: ++buf 是错误的。 char buf[ ]; char *pbuf; pbuf-----------------&buf[0] pbuf + 1------------&buf[1] *pbuf---------------buf[0] *(pbuf + 1)--------buf[1] *(pbuf + index)---buf[index]
例: #include <iostream.h> void main( ) { static int a[5] = {5,4,3,2,4}; int i,j; i = a[2] + a[4]; j = *(a+2) + *(a+4) cout << i << “\n”<< j ; } 输出:7 7
数组与指针 混用 注意事项 1、数组是数组,指针是指针。 2、数组不能赋值。 3、用数组名可以给指针赋值,得到的值是数组首元素地址。 4、函数传参数时,数组类型的声明实际解释为指针类型的声明。 于是在函数调用时,参数传递就是 2 和 3 的特例:注意传参数就是赋值。
例: • int a[] = {1,2,3};int *p = a;sizeof a / sizeof(a[0]) == 3;void foo(int p[]); // 同 void foo(int *p);复杂的示例:int a[2][3] = {1,2,3, 4,5,6};// a 是 2 元数组,其元素是 int[3] 即 3 元整数数组void bar(int p[][3]);// 同 void bar(int (*p)[3]); // 参数 p 是指针,其所指元素类型是 int[3] 即 3 元整数数组bar(a); // 调用 bar 时,数组 a 解释为首元素地址,赋给指针参数 p
使用操作符new和delete进行动态存储分配 • 普通的变量或数组的定义,是在程序被编译时,由编译器为该变量或数组分配存储空间,这样的存储分配称为自动存储分配或静态存储分配(定义对象时使用了关键字static)。这样的存储分配方式有时会给编程带来不便。 • C++提供了动态分配存储空间的方法---即在程序运行过程中,根据实际需要为变量(对象)或数组分配存储空间。这种存储分配方法称为动态存储分配。 • 程序运行时,操作系统为程序分配一块内存空间,存放程序中所有动态创建的变量(对象),这块空间称为程序的自由存储区或堆。
实现动态存储分配 • C++使用操作符new和delete来完成动态存储分配。 1、动态创建单个变量的语法: 指针名 = new 数据类型; 2、动态创建数组的语法: 指针名 = new 数据类型[表达式1][表达式2]…; • 例如: int *ptr1=new int; //动态创建一个整型变量 float *ptr2=new float[10]; //动态创建包含10个元素的浮点数组, • new操作符用于实现动态存储分配,系统根据数据类型决定所需内存的大小,如果分配成功,则将所分配的内存空间首地址赋值给相同类型的指针变量。 • 可以使用由圆括号扩住的初值表对动态创建的变量进行初始化。例如: float *ptr1=new float(123.45);
释放动态分配的内存 • 在程序中由操作符new为对象动态分配的存储空间,在程序运行结束前必须用delete操作符动态释放。 • 释放动态创建的变量的语法: delete 指针名; • 释放动态创建的数组的语法: delete [] 指针名;
例 根据输入的数组长度动态创建整型数组。给 数据元素赋值,并输出数组。 #include<iostream> using namespace std; void main() { int length,i,*ptr1; cout<<"请输入数组的长度: "; cin>>length; ptr1=new int[length]; for(i=0;i<length;i++) *(ptr1+i)=i*10; for(i=0;i<length;i++) cout<<ptr1[i]<<" "; cout<<endl; delete[] ptr1; }
课堂练习 • 从键盘读入正整数k,并动态创建内存空间,从键盘循环读入k个字符存入该片内存。 void fun() { int k; cin>>k; char *pBuf = new char[k]; for (int i=0; i<k; i++) cin>>pBuf[i]; delete []pBuf; }
C++语言基础——补充知识 • 运行时内存 • 指针和内存地址 • 指针地址 • 指针和数组 • 动态内存分配 • 函数调用和内存 • 结构体 及其 调用 • 输入输出(cout 和 cin)
函数 的 声明、实现、及 使用 • 函数声明:给函数定一个标准 (一般放在 xx.h 中) 返回值类型 函数名(参数列表…); int Func(int para1, float para2); • 函数实现:具体实现函数声明的功能 (xx.cpp) int Func(int para1, float para2) { …… ; return xxx; } • 函数使用:使用函数 ( main.cpp 或其他调用者) main( ) { int tmp; tmp = Func(100, 200.0); }
main函数 a函数 b函数 ② ③ ④ ① 调用b函数 ⑤ 调用a函数 ⑨ ⑦ ⑥ ⑧ 结束 函数调用中究竟发生了什么? (1)为所有的形参分配内存,计算各个实际参数表达式的值,依次赋予对应的形式参数。(若是“无参函数”,上述工作不执行) (2)进入函数体,执行函数中的语句,实现函数的功能,当执行到“返回语句”时,计算返回值,释放本函数体中的变量等(静态型变量不释放),收回分配给形参的内存,返回主调函数。 (3)继续执行主调函数中的后继语句。
参数传递 函数间传递参数有2种方式: 值传递方式(以及指针传递);引用传递方式。 例设计一个数据交换函数swap( ),实现两个数的交换 void swap(int a, int b){ int temp; temp = a; a = b; b = temp; } int main(){ int m, n; scanf(“%d %d”, &m, &n); swap(m, n); printf(“%d %d”, m, n); return 0; } 值传递
函数参数引用传递 void swap(int &a, int &b){ int temp; temp = a; a = b; b = temp; } int main(){ int m, n; scanf(“%d %d”, &m, &n); swap(m, n); printf(“%d %d”, m, n); return 0; } 引用传递
函数的重载 int fun(int a, int b) { return a+b; } void main(void) { cout<<fun(3,5)<<endl; cout<<fun(5)<<endl; } int fun (int a) { return a*a; } 所谓函数的重载是指完成不同功能的函数可以具有相同的函数名。 C++的编译器是根据函数的实参来确定应该调用哪一个函数的。
课堂练习 • 1. 写出求点(x,y)对称点坐标的函数。 • 2. 使用函数重载求2个及3个数中最大值的函数。
C++语言基础——补充知识 • 运行时内存 • 指针和内存地址 • 指针地址 • 指针和数组 • 动态内存分配 • 函数调用和内存 • 结构体 及其 调用 • 输入输出(cout 和 cin)
结构体的定义 • 结构体 是一种复合 数据类型 • 定义结构体后,结构体名 的使用和一般数据类型类似 structSTUDENT { int num; char name[20]; char sex; int age; float score; char addr[30]; }; STUDENT stu1,stu2; struct 结构体名 { 类型标识符 成员名; 类型标识符 成员名; ……………. }; 结构体名变量列表;
例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu1,stu2; 例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu1,stu2; stu1.num=10; 例 struct student { int num; char name[20]; struct date { int month; int day; int year; }birthday; }stu1,stu2; 例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu1,stu2; stu1.score=85.5; printf(“%d,%s,%c,%d,%f,%s\n”,stu1); () stu1.birthday.month=12; stu1.score+=stu2.score; stu1.age++; stu1={101,“Wan Lin”,‘M’,19,87.5,“DaLian”}; () stu2=stu1; ( ) 例 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }stu1,stu2; birthday num name month day year if(stu1==stu2) …….. () 引用方式: 结构体变量名.成员名 • 结构体变量的引用 • 引用规则 • 结构体变量不能整体引用,只能引用变量成员 • 可以将一个结构体变量赋值给另一个结构体变量 • 结构体嵌套时逐级引用 成员(分量)运算符 优先级: 1 结合性:从左向右
(*结构体指针名).成员名 结构体指针名->成员名 结构体变量名.成员名 p num name struct student { int num; char name[20]; char sex; int age; }stu; struct student *p=&stu; stu sex age main() { struct student { long int num; char name[20]; char sex; float score; }stu_1,*p; p=&stu_1; stu_1.num=89101; strcpy(stu_1.name,"Li Lin"); p->sex='M'; p->score=89.5; printf("\nNo:%ld\nname:%s\nsex:%c\nscore:%f\n", (*p).num,p->name,stu_1.sex,p->score); } • 结构体和指针 • 指向结构体变量的指针 • 定义形式:struct 结构体名 *结构体指针名; 例 struct student *p; • 使用结构体指针变量引用成员形式 存放结构体变量在内存的起始地址 例 int n; int *p=&n; *p=10; n=10 struct student stu1; struct student *p=&stu1; stu1.num=101; (*p).num=101 例 指向结构体的指针变量 指向运算符 优先级: 1 结合方向:从左向右
p num name stu[0] sex age p+1 stu[1] stu[2] 例 指向结构体数组的指针 struct student { int num; char name[20]; char sex; int age; }stu[3]={{10101,"Li Lin",'M',18}, {10102,"Zhang Fun",'M',19}, {10104,"Wang Min",'F',20}}; main() { struct student *p; for(p=stu;p<stu+3;p++) printf("%d%s%c%d\n",p->num,p->name,p->sex,p->age); } 指向结构体数组的指针
目录 • 运行时内存 • 指针和内存地址 • 指针地址 • 指针和数组 • 动态内存分配 • 函数调用和内存 • 输入输出(cout 和 cin)
标准输出流对象cout • cout是流类库中预定义的对象,它是标准输出流类ostream的对象,代表标准输出设备---显示器。例如: cout<<"Welcome to C++!!!\n"; //向显示器输出字符串 cout<<“i=”<<i<<endl;//向显示器输出变量i的值 • 语句中的“<<”称为插入运算符,功能是把要输出的数据信息插入到输出流中。 • cout是在头文件iostream和iostream.h中定义的,所以要在程序中使用cout,就要使用如下指令包含该头文件。 #include<iostream.h> 或者 #include<iostream> using namespace std;
标准输入流对象cin • cin也是流类库中预定义的对象,它是标准输入流类istream的对象,代表标准输入设备---键盘。例如: cin>>i>>j; //从键盘输入变量i和j的值 • 语句中的“>>”称为提取运算符,功能是从输入流中提取数据,赋值给程序中的变量。 • cin对象也是在头文件iostream和iostream.h中定义的。