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

指針和二維數組
如果定義二維數組:
T a[M][N];
ai是一個一維數組
a[i]的類型是 T *
sizeof(a[i]) = sizeof(T) * N
a[i]指向的地址: 數組a的起始地址 + i×N×sizeof(T)
5
指針和二維數組

void Reverse(int * p,int size) { //顛倒一個數組
for(int i = 0;i < size/2; ++i) {
int tmp = p[i];
p[i] = p[size-1-i];
p[size-1-i] = tmp;
}
}
int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12}};
Reverse(a[1],4); 
=> { {1,2,3,4},{8,7,6,5},{9,10,11,12}};
Reverse(a[1],6); 
=> { {1,2,3,4},{10,9,5,6},{7,8,11,12}};
6

指向指針的指針
定義:
T ** p;
p是指向指針的指針,p指向的地方應該存放着一個類型爲 T * 的指針
*p 的類型是 T *
8
指向指針的指針

#include <iostream>
using namespace std;
int main()
{
int **pp; //指向int*類型指針的指針
int * p; 
int n = 1234;
p = &n; // p指向n
pp = & p; //pp指向p
cout << *(*pp) << endl; // *pp是p, 所以*(*pp)就是n
return 0;
}
=> 1234
9

指針和字符串
字符串常量的類型就是 char *
字符數組名的類型也是 char *

#include <iostream>
using namespace std;
int main() 
{
char * p = "Please input your name:\n";
cout << p ; // 若不用cout, printf(p) 亦可
char name[20];
char * pName = name;
cin >> pName; 
cout << "Your name is " << pName;
return 0;
}
11
Please input your name:
Jack↙
Your name is Jack

指針和字符串
字符數組名的類型也是 char ,就是一個地址
char name[20];
int n;
scanf("%d%s",&n, name);
cin >> n >> name;
12
字符串操作庫函數
13
函數名稱 功能
strcat 將一個字符串連接到另一個字符串後面
strchr 查找某字符在字符串中最先出現的位置
strrchr 查找某字符在字符串中最後出現的位置
strstr 求子串的位置
strcmp 比較兩個字符串的大小(大小寫相關)
stricmp 比較兩個字符串的大小(大小寫無關)
strcpy 字符串拷貝
strlen 求字符串長度
strlwr 將字符串變成小寫
strupr 將字符串變成大寫
strncat 將一個字符串的前n個字符連接到另一個字符串後面
strncmp 比較兩個字符串的前n個字符
strncpy 拷貝字符串的前n個字符
strtok 抽取被指定字符分隔的子串
atoi 將字符串轉換爲整數(在cstdlib中聲明)
atof 將字符串轉換爲實數(在cstdlib中聲明)
itoa 將整數轉換爲字符串(在cstdlib中聲明)
字符串操作庫函數
14
char * strchr(const char * str,int c);
尋找字符c在字符串str中第一次出現的位置。如果找到,就返回指向該位置的char

針;如果str中不包含字符c,則返回NULL
char * strstr(const char * str, const char * subStr);
尋找子串subStr在str中第一次出現的位置。如果找到,就返回指向該位置的指針;如
果str不包含字符串subStr,則返回NULL
int stricmp(const char * s1,const char * s2);
大小寫無關的字符串比較。如果s1小於s2則返回負數;如果s1等於s2,返回0;s1大
於s2,返回正數。不同編譯器編譯出來的程序,執行stricmp的結果就可能不同。
int strncmp(const char * s1,const char * s2,int n);
比較s1前n個字符組成的子串和s2前n個字符組成的子串的大小。若長度不足n,則取
整個串作爲子串。返回值和strcmp類似。
char * strncpy(char * dest, const char * src,int n);
拷貝src的前n個字符到dest。如果src長度大於或等於n,該函數不會自動往dest中寫
入‘\0’;若src長度不足n,則拷貝src的全部內容以及結尾的‘\0’到dest。
字符串操作庫函數
15
char * strtok(char * str, const char * delim);
連續調用該函數若干次,可以做到:從str中逐個抽取出被字符串delim中的字符分隔
開的若干個子串。
int atoi(char *s);
將字符串s裏的內容轉換成一個整型數返回。比如,如果字符串s的內容是“1234”,那
麼函數返回值就是1234。如果s格式不是一個整數,比如是"a12",那麼返回0。
double atof(char *s);
將字符串s中的內容轉換成實數返回。比如,“12.34"就會轉換成12.34。如果s的格式
不是一個實數 ,則返回0。
char *itoa(int value, char *string, int radix);
將整型值value以radix進製表示法寫入 string:
char szValue[20];
itoa( 27,szValue,10); //使得szValue的內容變爲 “27”
itoa( 27,szValue,16); //使得szValue的內容變爲"1b”
字符串操作庫函數
16

#include <iostream>
#include <cstring> 
using namespace std;
int main() 
{
char s1[100] = "12345";
char s2[100] = "abcdefg";
char s3[100] = "ABCDE";
strncat(s1,s2,3); // s1 = "12345abc"
cout << "1) " << s1 << endl; //輸出 1) 12345abc
strncpy(s1,s3,3); // s3的前三個字符拷貝到s1,s1="ABC45abc"
cout << "2) " << s1 << endl; //輸出 2) ABC45abc
strncpy(s2,s3,6); // s2 = "ABCDE"
cout << "3) " << s2 << endl; //輸出 3) ABCDE
cout << "4) " << strncmp(s1,s3,3) << endl; 
//比較s1和s3的前三個字符,比較結果是相等,輸出 4) 0
char * p = strchr(s1,'B'); //在s1中查找 'B'第一次出現的位置
17
if( p ) // 等價於 if( p!= NULL) 
cout << "5) " << p - s1 <<"," << *p << endl; //輸出 5) 1,B
else
cout << "5) Not Found" << endl;
p = strstr( s1,"45a"); //在s1中查找字串 "45a"。s1="ABC45abc"
if( p )
cout << "6) " << p - s1 << "," << p << endl; //輸出 6) 3,45abc
else
cout << "6) Not Found" << endl;
//以下演示strtok用法:
cout << "strtok usage demo:" << endl;
char str[] ="- This, a sample string, OK.";
//下面要從str逐個抽取出被" ,.-"這幾個字符分隔的字串
p = strtok (str," ,.-"); //請注意," ,.-"中的第一個字符是空格
while ( p != NULL) { //只要p不爲NULL,就說明找到了一個子串
cout << p << endl;
p = strtok(NULL, " ,.-"); //後續調用,第一個參數必須是NULL
}
return 0;
}
This
a
sample
string
OK

void 指針
19
void指針:
void * p;
可以用任何類型的指針對 void 指針進行賦值或初始化:
double d = 1.54;
void * p = & d;
void * p1;
p1 = & d;
void 指針
20
因 sizeof(void) 沒有定義,所以對於 void * 類型的指針p,
p 無定義
++p, --p, p += n, p+n,p-n 等均無定義
信息科學技術學院
挪威峽灣
內存操作庫函數
內存操作庫函數memset
22
頭文件cstring中聲明:
void * memset(void * dest,int ch,int n);
將從dest開始的n個字節,都設置成ch。返回值是dest。ch只有最低的字節起
作用。
例:將szName的前10個字符,都設置成’a’:
char szName[200] = “”;
memset( szName,‘a’,10);
cout << szName << endl;
=>aaaaaaaaaa
內存操作庫函數memset
23
用memset函數將數組內容全部設置成0:
int a[100];
memset(a,0,sizeof(a));
則數組a的每個元素都變成0
內存操作庫函數memcpy
24
頭文件cstring中聲明:
void * memcpy(void * dest, void * src, int n);
將地址src開始的n個字節,拷貝到地址dest。返回值是dest。
將數組a1的內容拷貝到數組a2中去,結果是a2[0] = a1[0], a2[1] =
a1[1]……a2[9] = a1[9] :
int a1[10];
int a2[10];
memcpy( a2, a1, 10
sizeof(int));
如何編寫內存操作庫函數memcpy
25

void * MyMemcpy( void * dest , const void * src, int n)
{
char * pDest = (char * )dest;
char * pSrc = ( char * ) src;
for( int i = 0; i < n; ++i ) {
//逐個字節拷貝源塊的內容到目的塊
* (pDest + i) = * ( pSrc + i ); 
}
return dest;
}

有缺陷,在dest區間和src區間有重疊時可能出問題!!!

函數指針
基本概念
程序運行期間,每個函數都會佔用一段
連續的內存空間。而函數名就是該函數所佔
內存區域的起始地址(也稱“入口地址”)。我
們可以將函數的入口地址賦給一個指針變量
,使該指針變量指向該函數。然後通過指針
變量就可以調用這個函數。這種指向函數的
指針變量稱爲“函數指針”。 函

函數指針 體
27
定義形式
類型名 (* 指針變量名)(參數類型1, 參數類型2,…);
例如:
int (*pf)(int ,char);
表示pf是一個函數指針,它所指向的函數,返回值類型應是int
,該函數應有兩個參數,第一個是int 類型,第二個是char類型。
28
使用方法
可以用一個原型匹配的函數的名字給一個函數指針賦值。
要通過函數指針調用它所指向的函數,寫法爲:
函數指針名(實參表);
29
使用方法

#include <stdio.h>
void PrintMin(int a,int b) {
if( a<b )
printf("%d",a);
else
printf("%d",b);
}
int main() {
void (* pf)(int ,int);
int x = 4, y = 5;
pf = PrintMin; 
pf(x,y);
return 0;
}
30
PrintMin
pf
輸出結果: 4

函數指針和qsort庫函數
C語言快速排序庫函數:
void qsort(void *base, int nelem, unsigned int width,
int ( * pfCompare)( const void *, const void *));
可以對任意類型的數組進行排序
31
函數指針和qsort庫函數
32
a[0] a[1] …… a[i] …… a[n-1]
對數組排序,需要知道:

  1. 數組起始地址
  2. 數組元素的個數
  3. 每個元素的大小(由此可以算出每個元素的地址)
  4. 元素誰在前誰在後的規則
    函數指針和qsort庫函數
    void qsort(void *base, int nelem, unsigned int width,
    int ( * pfCompare)( const void *, const void *));
    33
    a[0] a[1] …… a[i] …… a[n-1]
    base: 待排序數組的起始地址,
    nelem: 待排序數組的元素個數,
    width: 待排序數組的每個元素的大小(以字節爲單位)
    pfCompare :比較函數的地址
    函數指針和qsort庫函數
    void qsort(void *base, int nelem, unsigned int width,
    int ( * pfCompare)( const void *, const void *));
    pfCompare: 函數指針,它指向一個“比較函數”。
    該比較函數應爲以下形式:
    int 函數名(const void * elem1, const void * elem2);
    比較函數是程序員自己編寫的
    34
    函數指針和qsort庫函數
    排序就是一個不斷比較並交換位置的過程。
    qsort函數在執行期間,會通過pfCompare指針調用 “比較函
    數”,調用時將要比較的兩個元素的地址傳給“比較函數”,然後根
    據“比較函數”返回值判斷兩個元素哪個更應該排在前面。
    35
    函數指針和qsort庫函數
    36
    int 比較函數名(const void * elem1, const void * elem2);
    a[0] a[1] …… a[i] …… a[n-1]
    pfCompare( e1, e2 );
    比較函數編寫規則:
  5. 如果 * elem1應該排在 * elem2前面,則函數返回值是負整數
  6. 如果 * elem1和* elem2哪個排在前面都行,那麼函數返回0
  7. 如果 * elem1應該排在 * elem2後面,則函數返回值是正整數
    函數指針和qsort庫函數
    37
    實例:
    下面的程序,功能是調用qsort庫函數,將一個unsigned int數組按
    照個位數從小到大進行排序。比如 8,23,15三個數,按個位數從小到
    大排序,就應該是 23,15,8
#include <cstdio>
#include <cstdlib>
using namespace std;
int MyCompare( const void * elem1, const void * elem2 )
{
unsigned int * p1, * p2;
p1 = (unsigned int *) elem1; // “* elem1” 非法
p2 = (unsigned int *) elem2; // “* elem2” 非法
return (* p1 % 10) - (* p2 % 10 ); 
}
#define NUM 5
int main() 
{
unsigned int an[NUM] = { 8,123,11,10,4 };
qsort( an,NUM,sizeof(unsigned int),MyCompare); 
for( int i = 0;i < NUM; i ++ )
printf("%d ",an[i]);
return 0;
}

輸出結果:
10 11 123 4 8

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