北京大學C語言學習第6天

結構的概念
現實需求
在現實問題中,常常需要用一組不同類型的數據來描述一個事物。比如一個
學生的學號、姓名和績點。一個工人的姓名、性別、年齡、工資、電話…
如果編程時要用多個不同類型的變量來描述一個事物,就很麻煩。當然希望
只用一個變量就能代表一個“學生”這樣的事物。
C++允許程序員自己定義新的數據類型。因此針對“學生”這種事物,可以
定義一種新名爲Student的數據類型,一個Student類型的變量就能描述一個
學生的全部信息。同理,還可以定義數據類型 Worker以表示工人。
5
結構(struct)
用“struct”關鍵字來定義一個“結構”,也就定義了一個新的數據類型:
struct 結構名
{
類型名 成員變量名;
類型名 成員變量名;
類型名 成員變量名;
……
};
6
結構(struct)
 例:
struct Student {
unsigned ID;
char szName[20];
float fGPA;
};
Student 即成爲自定義類型的名字,可以用來定義變量
Stuent s1,s2;
7
結構(struct)
 例兩個同類型的結構變量,可以互相賦值。但是結構變量之間不能用

==”、“!=”、“<”、“>”、“<=”、“>=”進行比較運算。
一般來說,一個結構變量所佔的內存空間的大小,就是結構中所有成員變
量大小之和。結構變量中的各個成員變量在內存中一般是連續存放的,
8
struct Student {
unsigned ID;
char szName[20];
float fGPA;
};
結構(struct)
 一個結構的成員變量可以是任何類型的,包括可以是另一個結構類型:

struct Date {
int year;
int month;
int day;
};
struct StudentEx {
unsigned ID;
char szName[20];
float fGPA;
Date birthday;
};

9
結構(struct)
 結構的成員變量可以是指向本結構類型的變量的指針
struct Employee {
string name;
int age;
int salary;
Employee * next;
};
10
訪問結構變量的成員變量
一個結構變量的成員變量,可以完全和一個普通變量
一樣來使用,也可以取得其地址。使用形式:
結構變量名.成員變量名
StudentEx stu;
cin >> stu.fGPA;
stu.ID = 12345;
strcpy(stu.szName, “Tom”);
cout << stu.fGPA;
stu.birthday.year = 1984;
unsigned int * p = & stu.ID; //p指向stu中的ID成員變量
11
struct Date {
int year;
int month;
int day;
};
struct StudentEx {
unsigned ID;
char szName[20];
float fGPA;
Date birthday;
};
結構變量的初始化
 結構變量可以在定義時進行初始化:
StudentEx stu = { 1234,“Tom”,3.78,{ 1984,12,28 }};
12
struct Date {
int year;
int month;
int day;
};
struct StudentEx {
unsigned ID;
char szName[20];
float fGPA;
Date birthday;
};

結構數組和指針
結構數組

StudentEx MyClass [50];
StudentEx MyClass2[50] = { 
{ 1234,"Tom",3.78,{ 1984,12,28 }},
{ 1235,"Jack",3.25,{ 1985,12,23 }},
{ 1236,"Mary",4.00,{ 1984,12,21 }},
{ 1237,"Jone",2.78,{ 1985,2,28 }}
};
MyClass[1].ID = 1267;
MyClass[2].birthday.year = 1986;
int n = MyClass[2].birthday.month;
cin >> MyClass[0].szName;

14
指向結構變量的指針
 定義指向結構變量的指針
結構名 * 指針變量名;
StudentEx * pStudent;
StudentEx Stu1;
pStudent = & Stu1;
StudentEx Stu2 = * pStudent;
15
指向結構變量的指針
 通過指針,訪問其指向的結構變量的成員變量:
指針->成員變量名
或:
(* 指針).成員變量名
StudentEx Stu;
StudentEx * pStu;
pStu = & Stu;
pStu->ID = 12345;
(*pStu).fGPA = 3.48;
cout << Stu.ID << endl; //輸出 12345
cout << Stu.fGPA << endl; //輸出 3.48
16
C++程序結構
信息科學技術學院《程序設計與算法》
17

全局變量
局部變量
靜態變量
全局變量和局部變量
 定義在函數內部的變量叫局部變量(函數的形參也是局部變量)
 定義在所有函數的外面的變量叫全局變量
 全局變量在所有函數中均可以使用,局部變量只能在定義它的函數內部使用
19
全局變量和局部變量

#include <iostream>
using namespace std;
int n1 = 5, n2 = 10; //全局變量
void Function1() {
int n3 =4;
n2 = 3;
}
void Function2() {
int n4;
n1 = 4;
n3 = 5; //編譯出錯
,n3無定義
}

20
靜態變量
全局變量都是靜態變量。局部變量定義時如果前面加了“static”關鍵字,則
該變量也成爲靜態變量
靜態變量的存放地址,在整個程序運行期間,都是固定不變的
非靜態變量(一定是局部變量)地址每次函數調用時都可能不同,在函數的一次
執行期間不變
如果未明確初始化,則靜態變量會被自動初始化成全0(每個bit都是0),局部
非靜態變量的值則隨機
21
靜態變量示例

#include <iostream>
using namespace std;
void Func()
{
static int n = 4; //靜態變量只初始化一次
cout << n << endl;
++ n;
}
int main()
{
Func(); Func(); Func();
}

22
輸出結果:
4
5
6
靜態變量應用:strtok的實現

#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char str[] ="- This, a sample string, OK.";
//下面要從str逐個抽取出被" ,.-"這幾個字符分隔的字串
char * p = strtok (str," ,.-"); 
while ( p != NULL) //只要p不爲NULL,就說明找到了一個子串
{
cout << p << endl;
p = strtok(NULL, " ,.-");
//後續調用,第一個參數必須是NULL
}
return 0;
} 

23
This
a
sample
string
OK

  • T h i s , a s a m p l e s t r i n g , O K \0
    char * p = strtok (str," ,.-");
    while ( p != NULL) //只要p不爲NULL,就說明找到了一個子串
    {
    cout << p << endl;
    p = strtok(NULL, " ,.-");
    //後續調用,第一個參數必須是NULL
    }
    This
    a
    sample
    string
    OK
    靜態變量應用:strtok的實現
char * Strtok(char * p,char * sep) 
{
static char * start ; //本次查找子串的起點
if(p)
start = p;
for(; *start && strchr(sep,*start); ++ start); //跳過分隔符號
if( * start == 0)
return NULL; 
char * q = start; 
for(; *start && !strchr(sep,*start); ++ start); //跳過非分隔符號
if( * start) {
* start = 0;
++ start;
}
return q;
}

25
標識符作用域
變量生存期
標識符的作用域
變量名、函數名、類型名統稱爲“標識符”。一個標識符能夠起作用的範圍
,叫做該標識符的作用域
在一個標識符的作用域之外使用該標識符,會導致“標識符沒有定義”的編
譯錯誤。使用標識符的語句,必須出現在它們的聲明或定義之後
在單文件的程序中,結構、函數和全局變量的作用域是其定義所在的整個文

27
標識符的作用域
函數形參的作用域是整個函數
局部變量的作用域,是從定義它的語句開始,到包含它的最內層的那一對大
括號“{}”的右大括號 “}”爲止
for循環裏定義的循環控制變量,其作用域就是整個for循環
同名標示符的作用域,可能一個被另一個包含。則在小的作用域裏,作用域
大的那個標識符被屏蔽,不起作用。
28
標識符的作用域

void Func(int m)
{
for( int i = 0; i < 4;++i ) {
if( m <= 0 ) {
int k = 3;
m = m *( k ++ );
}
else {
k = 0; //編譯出錯,k無定義
int m = 4;
cout << m;
}
}
i= 2; //編譯出錯,i無定義
}

29
變量的生存期
所謂變量的“生存期”,指的是在此期間,變量佔有內存空間,其佔有的內
存空間只能歸它使用,不會被用來存放別的東西。
而變量的生存期終止,就意味着該變量不再佔有內存空間,它原來佔有的內
存空間,隨時可能被派做他用。
全局變量的生存期,從程序被裝入內存開始,到整個程序結束。
30
變量的生存期
靜態局部變量的生存期,從定義它語句第一次被執行開始,到整個程序結束
爲止。
函數形參的生存期從函數執行開始,到函數返回時結束。非靜態局部變量的
生存期,從執行到定義它的語句開始,一旦程序執行到了它的作用域之外,其
生存期即告終止。
31
變量的生存期
32

void Func(int m)
{
for( int i = 0; i < 4;++i ) {
if( m <= 0 ) {
int k = 3;
m = m *( k ++ );
}
else {
k = 0; //編譯出錯,k無定義
int m = 4;
cout << m;
}
}
i= 2; //編譯出錯,i無定義
}

簡單排序

排序問題
 例題: 編程接收鍵盤輸入的若干個整數,排序後從小到大輸出。先輸入一個
整數n,表明有n個整數需要排序,接下來再輸入待排序的n個整數。
解題思路:先將n個整數輸入到一個數組中,然後對該數組進行排序,最後遍
歷整個數組,逐個輸出其元素。
對數組排序有很多種簡單方法,如“冒泡排序”、 “選擇排序”、 “插入排
序”等
34
選擇排序
如果有N個元素需要排序,那麼首先從N個元素中找到最小的那個(稱爲第0
小的)放在第0個位子上(和原來的第0個位子上的元素交換位置),然後再從剩
下的N-1個元素中找到最小的放在第1個位子上,然後再從剩下的N-2個元素
中找到最小的放在第2個位子上……直到所有的元素都就位。
36
選擇排序

void SelectionSort(int a[] ,int size)
{
for( int i = 0; i < size - 1; ++i ){//每次循環後將第i小的元素放好
int tmpMin = i; 
//用來記錄從第i個到第size-1個元素中,最小的那個元素的下標
for( int j = i+1; j < size ; ++j) {
if( a[j] < a[tmpMin] ) 
tmpMin = j;
}
//下面將第i小的元素放在第i個位子上,並將原來佔着第i個位子的元素挪到後面
int tmp = a[i];
a[i] = a[tmpMin]; 
a[tmpMin] = tmp; 
}
} 
37

插入排序
將整個數組a分爲有序的部分和無序的兩個部分。前者在左邊,後者在右邊。
開始有序的部分只有a[0],其餘都屬於無序的部分
每次取出無序部分的第一個(最左邊)元素,把它加入到有序部分。假設插
入到合適位置p, 則原p位置及其後面的有序部分元素,都向右移動一個位子。
有序的部分即增加了一個元素。
直到無序的部分沒有元素
39
插入排序

void InsertionSort(int a[] ,int size)
{
for(int i = 1;i < size; ++i ) { 
//a[i]是最左的無序元素,每次循環將a[i]放到合適位置
for(int j = 0; j < i; ++j) 
if( a[j]>a[i]) { 
//要把a[i]放到位置j,原下標j到 i-1的元素都往後移一個位子
int tmp = a[i];
for(int k = i; k > j; --k)
a[k] = a[k-1];
a[j] = tmp;
break;
}
}
}

40
冒泡排序
將整個數組a分爲有序的部分和無序的兩個部分。前者在右,後者在左邊。
開始,整個數組都是無序的。有序的部分沒有元素。
每次要使得無序部分最大的元素移動到有序部分第一個元素的左邊。移動的
方法是:依次比較相鄰的兩個元素,如果前面的比後面的大,就交換他們的位
置。這樣,大的元素就像水裏氣泡一樣不斷往上浮。移動結束有序部分增加了
一個元素。
直到無序的部分沒有元素
42
冒泡排序

void BubbleSort(int a[] ,int size)
{
for(int i = size-1;i > 0; --i ) { 
//每次要將未排序部分的最大值移動到下標i的位置
for(int j = 0; j < i; ++j) //依次比較相鄰的兩個元素
if( a[j] > a[j+1]) {
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
}
}
}

43
簡單排序的效率
 上面3種簡單排序算法,都要做 n2量級次數的比較(n是元素個數)!
 好的排序算法,如快速排序,歸併排序等,只需要做n*log2n量級次數的比
較!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章