1. 程式人生 > >《程式設計實習》之【從C走進C++】

《程式設計實習》之【從C走進C++】

函式指標

基本概念

程式執行期間,每個函式都會佔用一 段連續的記憶體空間。而函式名就是該函式所 佔記憶體區域的起始地址(也稱“入口地址”)。 我們可以將函式的入口地址賦給一個指標變 量,使該指標變數指向該函式。然後通過指 針變數就可以呼叫這個函式。這種指向函式 的指標變數稱為“函式指標”。

定義形式

型別名 (* 指標變數名)(引數型別1, 引數型別2,…);

例如:

int (*pf)(int, char);

表示pf是一個函式指標,它所指向的函式,返回值型別應是int, 該函式應有兩個引數,第一個是int 型別,第二個是char型別。

使用方法

可以用一個原型匹配的函式的名字給一個函式指標賦值。要通過函式指標呼叫它所指向的函式,寫法為:

函式指標名(實參表);
#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);      // 輸出結果:4
    return 0;
}

函式指標和qsort函式

C語言快速排序庫函式:

void qsort(void *base, int nelem, unsigned int width,
int ( * pfCompare)( const void *, const void *));

可以對任意型別的陣列進行排序。
對陣列排序,需要知道:

  • 陣列起始地址
  • 陣列元素的個數
  • 每個元素的大小(由此可以算出每個元素的地址)
  • 元素誰在前誰在後的規則

pfCompare: 函式指標,它指向一個“比較函式”。
該比較函式應為以下形式:

int 函式名(const void * elem1, const void * elem2);

比較函式是程式設計師自己編寫的。
排序就是一個不斷比較並交換位置的過程。
qsort

函式在執行期間,會通過pfCompare指標呼叫 “比較函式”,呼叫時將要比較的兩個元素的地址傳給“比較函式”,然後根 據“比較函式”返回值判斷兩個元素哪個更應該排在前面。

比較函式編寫規則:

  • 如果 * elem1應該排在 * elem2前面,則函式返回值是負整數
  • 如果 * elem1和* elem2哪個排在前面都行,那麼函式返回0
  • 如果 * elem1應該排在 * elem2後面,則函式返回值是正整數

例項:
下面的程式,功能是呼叫qsort庫函式,將一個unsigned int陣列按照個 位數從小到大進行排序。比如 8,23,15三個數,按個位數從小到大排 序,就應該是 23,15,8

#include <stdio.h>
#include <stdlib.h>
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

命令列引數

命令列方式執行程式

notepad sample.txt

命令列引數

使用者在CMD視窗輸入可執行檔名的方式啟動程式時,跟在可執行檔名後面的那些字串,稱為“命令列引數”。命令列引數可以有多個,以空格分隔。比如,在CMD視窗敲:

copy file1.txt file2.txt

“copy”, “file1.txt”, “file2.txt”就是命令列引數。如何在程式中獲得命令列引數呢?

int main(int argc, char * argv[]) {
    ……
}

argc: 代表啟動程式時,命令列引數的個數。C/C++語言規定,可執行程式程式本身的檔名,也算一個命令列引數,因此,argc的值 至少是1。
argv: 指標陣列,其中的每個元素都是一個char* 型別的指標,該指標指向一個字串,這個字串裡就存放著命令列引數。
例如,argv[0]指向的字串就是第一個命令列引數,即可執行程式的檔名,argv[1]指向第二個命令列引數,argv[2]指向第三個命令列引數……。

#include <stdio.h>
int main(int argc, char * argv[]) {
    for(int i = 0;i < argc; i ++ )
        printf( "%s\n",argv[i]);
    return 0;
}

將上面的程式編譯成sample.exe,然後在控制檯視窗敲:

sample para1 para2 s.txt 5 “hello world”

位運算

基本概念

位運算:
用於對整數型別(int,char, long 等)變數中的某一位(bit),或者若干位進行操作。比如:
1) 判斷某一位是否為1
2) 只改變其中某一位,而保持其他位都不變

C/C++語言提供了六種位運算子來進行位運算操作:
& 按位與(雙目)
| 按位或(雙目)
^ 按位異或(雙目)
~ 按位非(取反)(單目)
<< 左移(雙目)
>> 右移(雙目)

& 按位與

將參與運算的兩運算元各對應的二進位制位進行與操作,只有對應的兩個二進位均為1時,結果的對應二進位制位才為1,否則為0。

例如:表示式“21 & 18 ”的計算結果是16(即二進位制數10000),因為:

21 用二進位制表示就是:
0000 0000 0000 0000 0000 0000 0001 0101
18 用二進位制表示就是:
0000 0000 0000 0000 0000 0000 0001 0010
二者按位與所得結果是:
0000 0000 0000 0000 0000 0000 0001 0000

通常用來將某變數中的某些位清0且同時保留其他位不變。 也可以用來獲取某變數中的某一位。

例如,如果需要將int型變數n的低8位全置成0,而其餘位不變,則可以執行:

n = n & 0xffffff00;

也可以寫成:

n &= 0xffffff00;

如果n是short型別的,則只需執行:

n &= 0xff00;

如何判斷一個int型變數n的第7位(從右往左,從0開始數) 是否是1 ?
只需看錶達式 “n & 0x80”的值是否等於0x80即可。

0x80: 1000 0000

| 按位或

將參與運算的兩運算元各對應的二進位制位進行或操 作,只有對應的兩個二進位都為0時,結果的對應 二進位制位才是0,否則為1。

例如:表示式“21 | 18 ”的值是23,因為:

21:     0000 0000 0000 0000 0000 0000 0001 0101
18:     0000 0000 0000 0000 0000 0000 0001 0010
21|18:  0000 0000 0000 0000 0000 0000 0001 0111

按位或運算通常用來將某變數中的某些位置1且保留其他位不變。
例如,如果需要將int型變數n的低8位全置成1,而其餘位不變,則可以執行:

n |= 0xff;
0xff: 1111 1111

^ 按位異或

將參與運算的兩運算元各對應的二進位制位進行異或操作,即只有對應的兩個二進位不相同時,結果的對應二進位制位才是1,否則為0。

例如:表示式“21 ^ 18 ”的值是7(即二進位制數111)。

21:    0000 0000 0000 0000 0000 0000 0001 0101
18:    0000 0000 0000 0000 0000 0000 0001 0010
21^18: 0000 0000 0000 0000 0000 0000 0000 0111

按位異或運算通常用來將某變數中的某些位取反, 且保留其他位不變。
例如,如果需要將int型變數n的低8位取反,而其餘位不變,則可以執行:

n ^= 0xff;
0xff: 1111 1111

異或運算的特點是:
如果 a^b=c,那麼就有 c^b = a以及c^a=b。(窮舉法可證)
此規律可以用來進行最簡單的加密和解密。

另外異或運算還能實現不通過臨時變數,就能交換兩個變數的值:

int a = 5, b = 7;
a = a ^ b;
b = b ^ a;
a = a ^ b;

即實現a,b值交換,窮舉法可證。

~ 按位非

按位非運算子“~”是單目運算子。其功能是將運算元中的二進位制位0變成1,1變成0。

例如,表示式“~21”的值是整型數 0xffffffea

21: 0000 0000 0000 0000 0000 0000 0001 0101

~21:1111 1111 1111 1111 1111 1111 1110 1010

<< 左移運算子

表示式:a << b
的值是:將a各二進位全部左移b位後得到的值。左移時,高位 丟棄,低位補0。a 的值不因運算而改變。

例如: 9 << 4
9的二進位制形式:
0000 0000 0000 0000 0000 0000 0000 1001
因此,表示式“9<<4”的值,就是將上面的二進位制數左移4位,得:
0000 0000 0000 0000 0000 0000 1001 0000
即為十進位制的144。

實際上,左移1位,就等於是乘以2,左移n位,就等於 是乘以2^n 。而左移操作比乘法操作快得多。

>> 右移運算子

表示式:a >> b的值是:將a各二進位全部右移b位後得到的值。右移時,移出最右邊 的位就被丟棄。 a 的值不因運算而改變。
對於有符號數,如long,int,short,char型別變數,在右移時,符號位(即最高位)將一起移動,並且大多數C/C++編譯器規定,如果原符號位為1,則右移時高位就補充1,原符號位為0,則右移時高位就補充 0。

實際上,右移n位,就相當於左運算元除以2^n ,並且將結果往小裡取整。

-25 >> 4 = -2
-2 >> 4 = -1
18 >> 4 = 1
#include <stdio.h>
int main() {
    int n1 = 15;
    short n2 = -15;
    unsigned short n3 = 0xffe0;
    char c = 15;
    n1 = n1>>2;
    n2 >>= 3;
    n3 >>= 4;
    c >>= 3;
    printf( "n1=%d,n2=%x,n3=%x,c=%x",n1,n2,n3,c);
}    //輸出結果是:n1=3,n2=fffffffe,n3=ffe,c=1
n1: 0000 0000 0000 0000 0000 0000 0000 1111
n1 >>= 2:
變成3
    0000 0000 0000 0000 0000 0000 0000 0011

n2: 1111 1111 1111 0001
n2 >>= 3:
變成 fffffffe, 即-2
    1111 1111 1111 1110

n3: 1111 1111 1110 0000
n3 >>= 4:
變成 ffe
    0000 1111 1111 1110

c: 0000 1111
c >>= 3;
變成1
   0000 0001

思考題:

有兩個int型的變數a和n(0 <= n <= 31),要求寫一個表示式,使該表示式的值和a的第n位相同。
答案:

( a >> n ) & 1

當0<=n<31時,還可以寫為:

(a & (1 << n )) >> n

當n=31時,1<<31為符號位,與a做與運算後再右移,可能由於符號位問題導致最後結果第n位之前不都為0了。

引用

引用的概念

下面的寫法定義了一個引用,並將其初始化為引用某個變數。
型別名 & 引用名 = 某變數名;

int n = 4;
int & r = n;     // r引用了 n, r的型別是 int &

某個變數的引用,等價於這個變數,相當於該變數的一個別名。

int n = 4;
int & r = n;
r = 4;
cout << r;   //輸出 4
cout << n;   //輸出 4
n = 5;
cout << r;   //輸出 5

注意:

  • 定義引用時一定要將其初始化成引用某個變數。
  • 初始化後,它就一直引用該變數,不會再引用別的變量了。
  • 引用只能引用變數,不能引用常量和表示式。
double a = 4, b = 5;
double & r1 = a;
double & r2 = r1;
r2 = 10;   // r2也引用 a
cout << a << endl; // 輸出 10
r1 = b;  // r1並沒有引用b,只是對r1引用的變數賦值
cout << a << endl; //輸出 5

C語言中,如何編寫交換兩個整型變數值的函式?

void swap( int * a, int * b) {
    int tmp;
    tmp = * a; * a = * b; * b = tmp;
}
int n1, n2;
swap(& n1,& n2) ; // n1,n2的值被交換

有了C++的引用:

void swap( int & a, int & b) {
    int tmp;
    tmp = a; a = b; b = tmp;
}
int n1, n2;
swap(n1,n2);  // n1,n2的值被交換

const

const關鍵字的用法

const int MAX_VAL = 23;
const string SCHOOL_NAME = “Peking University”;
  • 不可通過常量指標修改其指向的內容
int n, m;
const int * p = & n;
* p = 5; //編譯出錯
n = 4;   //ok
p = &m;  //ok, 常量指標的指向可以變化
  • 不能把常量指標賦值給非常量指標,反過來可以
const int * p1; 
int * p2;
p1 = p2;   //ok
p2 = p1;   //error
p2 = (int * ) p1;  //ok, 強制型別轉換
  • 函式引數為常量指標時,可避免函式內部不小心改變引數指標所指地方的內容
void MyPrintf( const char * p ) {
    strcpy( p,"this"); //編譯出錯,strcpy()函式第一個引數為char*型別,但p是const char *型別的,不可以把常量指標賦值給非常量指標
    printf("%s",p);    //ok
}
  • 不能通過常引用修改其引用的變數
int n;
const int & r = n;
r = 5; //error
n = 4; //ok

動態記憶體分配

new運算子實現動態記憶體分配

  • 第一種用法,分配一個變數:
P = new T;

T是任意型別名,P是型別為T *的指標。
動態分配出一片大小為sizeof(T)位元組的記憶體空間,並且將該 記憶體空間的起始地址賦值給P。比如:

int * pn;
pn = new int;
* pn = 5;
  • 第二種用法,分配一個數組:
P = new T[N];

T:任意型別名
P :型別為T * 的指標
N :要分配的陣列元素的個數,可以是整型表示式
動態分配出一片大小為 N*sizeof(T)位元組的記憶體空間,並且將該記憶體空間的起始地址賦值給P。

int * pn;
int i = 5;
pn = new int[i * 20];
pn[0] = 20;
pn[100] = 30;  //編譯沒問題。執行時導致陣列越界
  • new動態分配的記憶體空間,一定要用 delete運算子進行釋放
delete 指標; //該指標必須指向new出來的空間
int * p = new int;
* p = 5;
delete p;
delete p;  //導致異常,一片空間不能被delete多次
  • delete釋放動態分配的陣列,要加[]
delete [ ] 指標;//該指標必須指向new出來的陣列
int * p = new int[20];
p[0] = 1;
delete [ ] p;

行內函數和過載函式

行內函數

函式呼叫是有時間開銷的。如果函式本身只有幾條語句,執行非常快,而且函式被反覆執行很多次,相比之下呼叫函式所產生的這個開銷就會顯得比較大。

為了減少函式呼叫的開銷,引入了行內函數機制。編譯器處理對行內函數的呼叫語句時,是將整個函式的程式碼插入到呼叫語句處,而不會產生呼叫函式的語句。

inline int Max(int a,int b) {
    if( a > b) return a;
    return b;
}

k = Max(n1, n2);
// 在程式中上面的語句會被展開成如下形式:
if (n1 > n2)
    tmp = n1;
else
    tmp = n2;
k = tmp;

函式過載

一個或多個函式,名字相同,然而引數個數引數型別不相同,這叫做函式的過載。

  • 以下三個函式是過載關係:
int Max(double f1,double f2) {}
int Max(int n1,int n2) { }
int Max(int n1,int n2,int n3) { }
  • 函式過載使得函式命名變得簡單。
  • 編譯器根據呼叫語句的中的實參的個數和型別判斷應該呼叫哪個函式。
Max(3.4,2.5);  //呼叫 (1)
Max(2,4);      //呼叫 (2)
Max(1,2,3);    //呼叫 (3)
Max(3,2.4);    //error,二義性

函式預設函式

C++中,定義函式的時候可以讓最右邊的連續若干個引數有預設值,那麼呼叫函式的時候,若相應位置不寫引數,引數就是預設值。

void func( int x1, int x2 = 2, int x3 = 3) { }
func(10 ) ; //等效於 func(10,2,3)
func(10,8) ; //等效於 func(10,8,3)
func(10, , 8) ; //不行,只能最右邊的連續若干個引數預設

函式引數可預設的目的在於提高程式的可擴充性。

即如果某個寫好的函式要新增新的引數,而原先那些 呼叫該函式的語句,未必需要使用新增的引數,那麼 為了避免對原先那些函式呼叫語句的修改,就可以使 用預設引數。

相關推薦

程式設計實習C走進C++

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

C++程式設計1.c走進c++

1.函式指標    (1)基本概念:程式執行期間,每個函式都會佔用一段連續的記憶體空間。而函式名就是該函式所佔記憶體區域的起始位置(也稱入口地址)。我們可以將一個函式的入口地址賦值給一個指標變數,使該指標變數指向該函式。然後通過指標變數就可以呼叫這個函式。這種指向函式的指標被

2017 程式設計實習C++部分作業題彙總

1、C01:看上去好坑的運算子過載 總時間限制: 1000ms 記憶體限制: 65536kB 描述 程式填空 #include <iostream> using namespace std; class MyInt {

Winpcap網路程式設計環境配置,Eclipse配置C/C++環境

本次為大家帶來的是用Eclipse配置C/C++環境的具體步驟,希望對大家有幫助哦。 Eclipse 配置C/C++開發環境講解如下: 1.JDK下載 如果沒有安裝JDK環境的小夥伴請自行去官網下載安裝啦,安裝之後的就可以跳過此步. 2.Eclipse下載 當然,下載JD

程式設計實習STL-list和deque

list容器 雙向連結串列#include<list> 在任何位置插入/刪除都是常數時間 不支援根據下標隨機存取元素 具有所有書序容器都有的成員函式 還支援8個成員函式 成員函式 作用 push

C語言複製檔案的兩種簡單的方法根本解決問題

網上的方法大致有這樣幾種: 1、使用作業系統提供的複製檔案的API 2、使用C語言本身提供的複製檔案的函式 3、直接讀寫檔案,從檔案角度來操作,從而直接將一個檔案複製 這裡我們使用的就是這第三種。 複製檔案的思路大概是這樣的,如果是文字型別,則可直接使用字元讀寫的方式,

5-9 c語言初識win32編程

message 這就是 解釋 while tran href 都是 需要 消息機制 ---恢復內容開始--- 今天學習了win32的相關知識,首先win32是指是指可以在32位或以上Windows系統中運行的程序,我學習的主要利用c/c++語言編寫的win32程序,

劍指offer上往下打印二叉樹

emp pub push coder 二叉 spa cti public ott 題目:   從上往下打印二叉樹 鏈接:   https://www.nowcoder.com/practice/7fe2212963db4790b57431d9ed259701?tpId=13

程式設計網格無水題動態規劃篇最大字串和

Written By MorrowWind,csdnicewing 可以到洛谷的P115上去練習  https://www.luogu.org/problemnew/show/P1115 題目描述         給出資料個

程式設計網格無水題最短歧義串

一個小小的字串,一個三歲小孩都能把它刪掉,但它卻可以累死所有的程式設計師…… 就好比這道題,累死我了。 首先宣告,演算法來自於百度貼吧的 patricia_xiao 前輩,非常感謝,本蒟蒻不生產演算法,我只是演算法的搬運工。 附原帖地址http://tieba.baidu.com/

程式設計實習STL專項練習

#include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> #include <string> #include <sstream> using

16. Spring Boot使用Druid(程式設計注入)零開始學Spring Boot

【視訊&交流平臺】 http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=40000000

Web視頻播放 HTML5 Video標簽

列表 use nbsp intel attr Language http comment 事件 一、說明 HTML5中引入了video標簽用於方便的在瀏覽器中播放視頻,不在需要對flashPlayer進行依賴,更加輕量級。但在瀏覽器兼容、視頻協議支持方面還有一些需要註意的

用戶空間和內核空間通訊proc文件系統

page ext4 sha 依靠 全局變量 dmesg 設備 net url 今天我們介紹還有一種用戶內核空間通信的方法:proc文件系統。 proc文件系統作為linux提供的一種虛擬文件系統並不占用實際外圍存儲空間,它僅存在於內存中。系統斷電即消失。proc

MySQL增刪改查多表聯合查詢

包括 實現 鼠標 thead 黃曉明 eba 字符 order 有時 很多時候在實際的業務中我們不只是查詢一張表。 在電子商務系統中,查詢哪些用戶沒有購買過產品。 銀行中可能查詢違規記錄,同時查詢出用戶的 查詢中獎信息和中獎人員的基本信息。 以上只是列的情況

零學習openCVIOS7下的openCV開發起步(Xcode5.1.1&amp;openCV2.49)

rgb load fcm 12px 轉換 sim 圖像 round ios 前言: 開發IOS7已經有一月的時間了。近期在準備推研的事,有點想往CV方向發展。於是開始自學openCV。 關註CSDN已經非常久了。也從非常多博主那學到了非常多知識,於是我也從這周開

kuangbin專題四 最短路練習入門到熟練

clu struct sin img urn star 通路 gif col 【POJ 2253 Frogger】 這道題求從u到v中所有通路的最大邊最小 我直接二分做了,但實際上這種題是最短路算法的變種,意義在於告訴我們spfa這些算法不僅能維護出最短路,稍加修改後可以維

技術到人生講座

今天有幸聽到公司一位技術出身的副總裁的演講,感觸頗多具體內容如下: 如何做到技術專家 影響力不能沉迷於讚美,所從事的研究成果一定要 實用! 價值的體現: 通過改進和優化演算法, 在屌絲機器上實現了通常只有在高富帥機器才能實現的效果。完美解決

0學Python1.開發環境搭建

從0學Python 最近準備學習python了。主要是考慮到python在深度學習和爬蟲方面的應用較廣,未來發展潛力較大。而且python與java相比,語法簡潔靈活,開發效率比較高。正所謂“人生苦短,我選python!”。 學習的過程打算用部落格記錄下來,這樣能給自己堅持下去的動力,也能養成一個愛總結的

python學習第十七篇:Python中的面向物件(一)

1.什麼是類和類的物件? 類是一種資料結構,我們可以用它來定義物件,後者把資料值和行為特性融合在一起,類是現實世界的抽象的實體以程式設計形式出現。例項是這些物件的具體化。類是用來描述一類事物,類的物件指的是這一類事物的一個個體。例如:“人”就是一個類,而男人,女人,小孩等就是“人”這個類的例項物件;再比如“