1. 數組 c++
聲明:
一維數組: type arrayName [ arraySize ];
多維數組: type name[size1][size2]...[sizeN];
比如結構體數組的初始化和訪問:
#include <iostream>
#include<stdlib.h>
using namespace std;
struct Share {
long int share_price;
char time_date[20];
};
int main()
{
//一維數組
Share one_D_share[] = { {100, "2017\\05\\31_09:00"}, {200,"2017\\05\\31_09:01"} };
for (size_t index = 0; index < 2; ++index) {
cout << one_D_share[index].share_price<<'\t' << one_D_share[index].time_date << endl;
}
//二維數組
Share two_D_share[2][2] = { {{100, "2017\05\31_09:00"}, {200,"2017\\05\31_09:01"}},{{300, "2017\05\31_09:02"}, {400,"2017\05\31_09:04"}} };
for (size_t i = 0; i < 2; ++i)
for(size_t j = 0; j < 2; ++j){
cout << two_D_share[i][j].share_price << two_D_share[i][j].time_date << endl;
}
//對部分元素賦值,爲魏闕賦值的部分整數位0,字符爲'\0'
Share share_3[3] = { {100, "2017\\05\\31_09:00"}, {200,"2017\\05\\31_09:01"} };
for (size_t index = 0; index < 3; ++index) {
cout << share_3[index].share_price << '\t' << share_3[index].time_date << endl;
}
system("pause");
return 0;
}
2. 指針
1. C中指針的概念
數據存放在內存中,而不同類型的數據佔用的字節數不同,比如int佔用4個字節。爲了正確的訪問到這些數據,必須爲每個字節編號,比如旅館中的房間號,根據編號可以準確的找到某個字節。
一下是4G內存每個字節的編號(十六進制表示:)
內存中字節的編號稱爲地址(Address)或指針(Pointer)。地址從 0 開始依次增加,對於 32 位環境,程序能夠使用的內存爲 4GB,最小的地址爲 0,最大的地址爲 0XFFFFFFFF。
#include <stdio.h>
#include <stdlib.h>
void Function() {
}
int main() {
int a = 16;
char str[20] = "C語言指針";
int arr[] = { 10, 100, 1000 };
printf("%#X, %#X, %#X, %#X\n", &a, &str, &arr, &Function);
printf("%#x, %#X, %#X, %#X\n", a, str, arr, Function);
system("pause");
return 0;
}
out:
0X58FEAC, 0X58FE90, 0X58FE7C, 0XFA1348
0x10, 0X58FE90, 0X58FE7C, 0XFA1348
%#x
是16進制輸出
一切都是地址
CPU訪問內存需要的是地址,而非變量名和函數名,在編譯和鏈接成可執行程序後,變量名和函數名都會被替換成地址。
比如:假設int類型 a, b, c的內存地址爲 0X1000, 0x1004, 0x1008,則減法運算c=a-b;
在執行過程中轉換爲:
,其中()
爲取值操作,表達式的意思是,取出地址0X1000,0X1004中的值,相減賦值到0X1008的內存地址中。
需要注意的是:比如在上的胡代碼示例中,變量名代表的是數據本身,而函數名,數組名,字符串名錶示的是首地址
2.C指針
如果想在程序中訪問數據的內存地址,只能需要指針變量了。
在C語言中們可以使用一個變量來存放指針(地址),該變量稱爲指針變量,值爲某一數據的地址,該數據可以使數組,字符串,函數,另一普通變量火指針變量。
定義指針變量
datatype *name
或者datatype *name=value
*
代表是一個指針變量,datatype
爲該指針所指向數據的數據類型。比如:
int *ip1=NULL;
int a = 100;
ip1 = &a;
其中,&
爲取地址運算符
內存指向模型:
inp指向地址0x1000,然後根據類型爲int,則向後取4個字節的地址,作爲數據塊,最終會得到a的數據。
指針可以向普通變量一樣多次寫入和修改
//定義int和char普通變量
int a=0, b=1;
char c='a', d='b';
//定義指針變量
int *ip1 = &a;
char *cp2 = &c;
//修該指針變量
ip1 = &b;
cp2=&c;
/*
與下列修改方法的不同:
*ip1 = b;
*cp2 = d;
指向的地址未變,更改了對應地址的內容,
而上一個是更改了指向的地址,也就是更改了指針變量本身的內容
*/
在聲明變量是必須帶*
,而在給指針變量賦值是不需要,實際上*ip
表示的是解引用,意思是取出ip所指向地址的數據
修改過程中,指針變化情況:
指針變量可以連續定義:注意每一變量前都要有*
int *a, *b, *c; //a、b、c 的類型都是 int*
而如果寫成下面的形式,只有a爲指針 變量,b,c爲int類型的變量。
int *a, b,c;
解引用獲取數據
格式: *pointer;
這裏的*
爲指針運算符,來取出某個地址上的數據:
int a = 20;
int *ip = &a;
printf("%d, %d\n", a, *ip);
out:
20, 20
處理流程爲,在程序被編譯和連接後,a,ip被替換爲相應的地址0x1001和0x2007,使用*ip時,先通過地址0x2007獲得ip的值,0x1001,也就是a的地址,然後再通過這個值取得變量a的數據,共進行兩次運算;而使用a的話,只需要通過0x1001直接取得它的數據,只要一步計算。
修改內存數據
int a = 20, b = 200, c = 2000;
int *ip = &a;
*ip = b;
c = *ip;
printf("%d, %d,%d,%d\n", a, b,c,*ip);
out:
200, 200,200,200
*ip代表的是a中的數據,等價於a。可以將另外一份數據賦值給它,也可以將它賦值給別的數據。
注意:
*
在不同的場景下有不同的作用:在變量定義中,表明是指針變量和普通變量。而在使用指針變量是,前面加*
號表示獲取指針指向的數據
更復雜的指針變量賦值:
int x, y, *px = &x, *py = &y;
y = *px + 5; //表示把x的內容加5並賦給y,*px+5相當於(*px)+5
y = ++*px; //px的內容加上1之後賦給y,++*px相當於++(*px)
y = *px++; //相當於y=(*px)++
py = px; //把一個指針的值賦給另一個指針
關於*和&的謎題
假設int類型的變量a,pa是指向它的指針,則
*&a: 理解爲 *(&a),表示先取a的地址相當於pa,在對地址解引用,最表表示的是a
&*pa: 理解爲&(*pa),表示先解引用pa,相當於a,在取a的地址,最終表示的pa
*
的總結
- 表示乘法,用於算數運算中
- 在定義指針變量時使用,以和普通變量區分
- 在使用指針變量時,表示解引用指針指向的數據
3.指針變量的運算
指針時一個用數值表示的地址,因此可以對指針執行算數: ++,–,+,-和比較運算==,<, >
值得注意的是,每次+或-,地址的增加或減少字節數與定義指針變量的類型有關:
int a = 10, *pa = &a, *paa = &a;
double b = 99.9, *pb = &b;
char c = '@', *pc = &c;
//最初的值
printf("&a=%#X, &b=%#X, &c=%#X\n", &a, &b, &c);
printf("pa=%#X, pb=%#X, pc=%#X\n", pa, pb, pc);
//加法運算
pa++; pb++; pc++;
printf("pa=%#X, pb=%#X, pc=%#X\n", pa, pb, pc);
//減法運算
pa -= 2; pb -= 2; pc -= 2;
printf("pa=%#X, pb=%#X, pc=%#X\n", pa, pb, pc);
//比較運算
if(pa == paa){
printf("equally\n");
}else{
printf("not equallyd\n");
}
return 0;
out:
&a=0X28FF44, &b=0X28FF30, &c=0X28FF2B
pa=0X28FF44, pb=0X28FF30, pc=0X28FF2B
pa=0X28FF48, pb=0X28FF38, pc=0X28FF2C
pa=0X28FF40, pb=0X28FF28, pc=0X28FF2A
not equally
從運算結果來看,pa、pb、pc 每次加 1,它們的地址分別增加 4、8、1個字節,這與他們數據類型所佔的字節相同。
如果兩個指針指向的是相關的變量,可以做比較運算,最常用的是利用算數和比較運算進行數組訪問。
int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
int *p;
for (p = a; p < a + 10; ++p)
{
printf("%#x,%d\n", p, *p);
}
out:
0x96f920,0
0x96f924,1
0x96f928,2
0x96f92c,3
0x96f930,4
0x96f934,5
0x96f938,6
0x96f93c,7
0x96f940,8
0x96f944,9
4. 指針和const關鍵字
以下兩種聲明等效:
const int *ip_a;
int const *ip_a;
代表的是指針指向的數據是常量。不能更改ip_a所指向數據的值,比如*ip_a=42
出錯。
而
int *const ip_b
表示指針(地址)本身是常量,你可以改變ip_b指向的值,比如*ip_b=42;
但是卻不可以改變ip_b本身指向的地址,也就是自己的值,比如ip+b++
出錯。
注意: 數組的首地址就是常量指針,其指向的地址不可改變,比如int a[10];
,a++
出錯
5.數組指針 array pointer
數組在內存區域中是一塊連續的內存塊
定義:
int arr[] = {1,2,3,4,5};
int *p=arr; // 第一種
int *p = &arr[0];//第二種
數組指針指向是數組中某一具體的元素,而非整個數組,所以數組指針的類型和數組元素的類型相同,在代碼中,p指向的數組元素是int類型,所以p的類型是int*
。
數組的遍歷方式:
int arr[] = { 0,1,2,3,4};
int len = sizeof(arr) / sizeof(int);
int *p=arr;
//指針遍歷1
printf("指針遍歷1\n");
for (; p < arr + len; ++p)
{
printf("%#x,%d\n", p, *p);
}
//指針遍歷2
p = arr;
printf("指針遍歷2\n");
for (int i=0; i < len; ++i)
{
printf("%#x,%d\n", p + i, *(p + i));
}
//下標遍歷
p = arr;
printf("下標遍歷\n");
for (int i = 0; i < len; ++i)
{
printf("%#x,%d\n", p + i, p[i]); //等價於printf("%#x,%d\n", a + i, a[i])
}
//逆向遍歷,反向輸出
printf("反向遍歷\n");
for (p = arr + len - 1; p >= arr; --p)
{
printf("%#x,%d\n", p, *p);
}
out:
指針遍歷1
0x6ffae8,0
0x6ffaec,1
0x6ffaf0,2
0x6ffaf4,3
0x6ffaf8,4
指針遍歷2
0x6ffae8,0
0x6ffaec,1
0x6ffaf0,2
0x6ffaf4,3
0x6ffaf8,4
下標遍歷
0x6ffae8,0
0x6ffaec,1
0x6ffaf0,2
0x6ffaf4,3
0x6ffaf8,4
反向遍歷
0x6ffaf8,4
0x6ffaf4,3
0x6ffaf0,2
0x6ffaec,1
0x6ffae8,0
注意:
數組在內存中只是數組元素的簡單排列,沒有開始和結束標誌,在求數組的長度時不能使用sizeof(p) / sizeof(int)
,因爲 p 只是一個指向 int 類型的指針,編譯器並不知道它指向的到底是一個整數還是一系列整數(數組),所以 sizeof§ 求得的是 p 這個指針變量本身所佔用的字節數,而不是整個數組佔用的字節數。
所以在訪問整個數組時,需要有數組長度參數,否則將無法訪問數組。這在數組作爲函數參數時尤其要注意,因爲只是傳入數組首地址。所以還要傳入長度參數
void printf_arr(int arr[],int len) {
printf("通過sizeof獲得的長度:%d\n", sizeof(arr) / sizeof(int));
for (int i = 0; i < len; ++i) {
printf("%d\t", arr[i]);
}
}
int arr[] = { 0,1,2,3,4};
int len = sizeof(arr) / sizeof(int);
printf_arr(arr, len);
out:
通過sizeof獲得的長度:1
0 1 2 3 4
6. 指針數組
顧名思義,指針數組就是數組中的元素爲指針。
定義:
const int MAX=3;
int *p[MAX]
在這裏,把 ptr 聲明爲一個數組,由 MAX 個整數指針組成。因此,ptr 中的每個元素,都是一個指向 int 類型的指針
- 簡單的,指向三個int值
int a = 1, b = 2, c = 3;
int *ptr[3];
//賦值爲整數的地址
ptr[0] = &a;
ptr[1] = &b;
ptr[2] = &c;
for (int i = 0; i < 3; i++)
{
printf("Value of var[%d] = %d\n", i, *ptr[i]);
}
out:
Value of var[0] = 1
Value of var[1] = 2
Value of var[2] = 3
- 每個指針指向int類型的數組,最終ptr指向的是二維數組
int arr1[3] = { 0,1,2 };
int arr2[2] = { 9,99 };
int arr3[5] = { 1,10,100,1000,10000 };
int *ptr[3];
ptr[0] = arr1;
ptr[1] = arr2;
ptr[2] = arr3;
int length[] = { 3, 2, 5 };
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < length[i]; ++j) {
//printf("%d ", *(ptr[i] + j)); 與下列等價
printf("%d ", ptr[i][j]);
}
printf("\n");
}
out:
0 1 2
9 99
1 10 100 1000 10000
內存模型:
7 字符數組和字符串
用來存放字符的數組稱爲字符數組,例如
char a[10]; //一維數組
char b[5][10]; //二維字符數組
char c[20]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a','m'}; // 給部分數組元素賦值
char d[]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm' }; //對全體元素賦值時可以省去長度
字符數組使勁上是一系列字符的集合,也就是字符串(String),但是在C語言中,沒有專門的String類型,使用字符數組來存放字符串
- 定義
-
可以直接將字符串賦值給字符數組
char str[30] = {"c.biancheng.net"}; char str[30] = "c.biancheng.net"; //這種形式更加簡潔,實際開發中常用
-
也可以不指定數組長度
char str[] = {"c.biancheng.net"}; char str[] = "c.biancheng.net"; //這種形式更加簡潔,實際開發中常用
注意: 字符數組只有在定義時才能將整個字符串一次性地賦值,一旦定義完成後,只能一個字符一個字符的賦值。
char str[4];
str = "ab1"; //錯誤
//一下賦值方式正確
str[0] = 'a'; str[1]='b';str[2]='1';
字符串結束標誌(重點)
在C語言中,字符串總是以\0
作爲結尾,\0
也被稱爲字符串結束標誌
\0
是ASCII碼錶中的第0個字符,英文爲NUL
,中文成文“空字符”,在C語言中唯一的效果就是作爲字符串結束標誌
C語言在處理字符串式,會從前往後逐個掃描字符,當遇到\0
時認爲到達字符串的末尾,處理結束。如果沒有\0
,意味着永遠到達不了字符串的結尾。
由""包裹的字符串會在末尾自動添加’\0’,所以會額外佔據一個字節位置。
下圖演示了“C program”在內存中的存儲情況:
注意:
- 逐個字符的給數組賦值並不會自動添加
\0
, - 用字符數組存儲字符串式,要注意爲
\0
多留一個字節
char str[7] = "abc123";
"abc123"看起來只包含了 6 個字符,我們卻將 str 的長度定義爲 7,就是爲了能夠容納最後的’\0’。如果將 str 的長度定義爲 6,它就無法容納’\0’了。
當字符串長度大於數組長度時,有些較老或者不嚴格的編譯器並不會報錯,甚至連警告都沒有,這就爲以後的錯誤埋下了伏筆,讀者自己要多多注意。
最好的辦法是:在創建數組時將所有的元素初始化爲0
在數組中存儲A-Z:
#include <stdio.h>
int main(){
char str[30] = {0}; //將所有元素都初始化爲 0,或者說 '\0'
char c;
int i;
for(c=65,i=0; c<=90; c++,i++){
str[i] = c;
}
printf("%s\n", str);
return 0;
}
注意:在函數內部定義的變量,數組,結構體,共用體等都成爲局部數據,在很多編譯器下,局部數據的初始化通常是隨機初始化爲任意值,而非0值。
8.指針訪問字符數組
指針訪問字符數組和指針訪問普通數組完全一樣,不在贅述
處理上述7中講述的字符數組,還有一種字符串,直接使用指針指向字符串,
char *str="I want to learn c";
字符串中的所有字符在內存中爲連續排列,str指向字符串的第0個字符,通常將第0個字符的地址稱爲字符串的首地址
下面演示如何輸出字符串:
#include <stdio.h>
#include <string.h>
int main(){
char *str="I want to learn c";
int len = strlen(str);
//直接輸出字符串
printf("%s\n", str);
//使用*(str+i)
for(int i=0; i<len; i++){
printf("%c", *(str+i));
}
printf("\n");
//使用str[i]
for(int i=0; i<len; i++){
printf("%c", str[i]);
}
printf("\n");
return 0;
}
一下是字符數組的輸出:
#include <stdio.h>
#include <string.h>
int main(){
char str[] = "I want to learn c";
int len = strlen(str);
//直接輸出字符串
printf("%s\n", str);
//每次輸出一個字符
for(int i=0; i<len; i++){
printf("%c", str[i]);
}
printf("\n");
return 0;
}
上述兩者在輸出方式是很相似,但是兩者最本質的區別是在內存中的存儲區域不同
*字符數組存儲在全局數據區或棧區,而第二種形式的字符串存儲在常量區,*而全局數據區和棧區的字符串(也包括其他數據)有讀取和寫入的權限,而常量區的字符串(也包括其他數據)只有讀取權限,沒有寫入權限
這就意味着:
char *str="dsdsd";
使用指針定義的字符串爲常量字符串,只可讀取。不可更改字符串內容char srt[]="dsdsd";
這種字符數組可以讀取和修改
9. 指針作爲函數參數
用指針變量作爲函數參數可以將函數外部的地址傳遞帶函數內部,使得在函數內部可以操作函數外部的數據,並且這些數據不會隨着函數的結束而被銷燬。
像數組、字符串、動態分配的內存等都是一系列數據的集合,沒有辦法通過一個參數全部傳入函數內部,只能傳遞它們的指針,在函數內部通過指針來影響這些數據集合。
而對於整數、小數、字符等基本類型數據的操作有時也必須要藉助指針,比如交換兩個變量的值
#include <stdio.h>
void swap(int *p1, int *p2){
int temp; //臨時變量
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main(){
int a = 66, b = 99;
swap(&a, &b);
printf("a = %d, b = %d\n", a, b);
return 0;
}
實際上將a和b的地址重新拷貝然後分別給p1,p2.這樣*p1,*p2就是提取地址上的數據,進行數據交換。函數運行結束後p1,p2銷燬,但是外部地址上的數據已經交換了。
數組作爲函數參數
數組是一系列數據的集合,無法通過參數將它們一次性傳遞到函數內部,如果希望在函數內部操作數組,必須傳遞數組指針。
定義max_and_min函數找到數組中的最大最小值
#include <stdio.h>
#include <stdlib.h>
void max_and_min(int * arr, int *max, int *min, int len) {
*max = *min = arr[0];
for (int i = 1; i < len; ++i) {
if (*max < arr[i]) *max = arr[i];
else if (*min > arr[i]) *min = arr[i];
}
}
int main(){
int max = 0, min = 0;
int num[6] = { 1, 2, 3,0, -1, 2 };
max_and_min(num, &max, &min, 6);
printf("Max value is %d!\n", max);
printf("Min value is %d!\n", min);
system("pause");
return 0;
}
out:
Max value is 3!
Min value is -1!
也可以採用:
void max_and_min(int arr[6], int *max, int *min, int len)
void max_and_min(int arr[], int *max, int *min, int len)
兩者都不會創建一個數組出來,編譯器也不會爲它們分配內存,實際的數組是不存在的,它們最終還是會轉換爲int *arr
這樣的指針。這就意味着,兩種形式都不能將數組的所有元素都傳遞進來,大家還得使用數組指針
不管使用哪種方式傳遞數組,都不能在函數內部求得數組長度,因爲 intArr 僅僅是一個指針,而不是真正的數組,所以必須要額外增加一個參數來傳遞數組長度。
C語言爲什麼不允許直接傳遞數組的所有元素,而必須傳遞數組指針呢?
參數的傳遞本質上是一次賦值的過程,賦值就是對內存進行拷貝。所謂內存拷貝,是指將一塊內存上的數據複製到另一塊內存上。
對於像 int、float、char 等基本類型的數據,它們佔用的內存往往只有幾個字節,對它們進行內存拷貝非常快速。而數組是一系列數據的集合,數據的數量沒有限制,可能很少,也可能成千上萬,對它們進行內存拷貝有可能是一個漫長的過程,會嚴重拖慢程序的效率,所以,C語言沒有從語法上支持數據集合的直接賦值。
除了C語言,C++、Java、Python 等其它語言也禁止對大塊內存進行拷貝,在底層都使用類似指針的方式來實現。
10 函數返回指針
C語言支持函數返回指針
定義:
typename * functionName()
但是使用指針作爲函數返回值需要注意一點,函數運行結束後會銷燬內部定義的所有局部數據,包括局部變量,局部數組和形式參數,所以函數返回的指針儘量不要指向這些數據。否則可能會在後續使用過程中可能會引發意向不到的錯誤。
#include <stdio.h>
int *func(){
int n = 100;
return &n;
}
int main(){
int *p = func(), n;
printf("c語言\n");
n = *p;
printf("value = %d\n", n);
return 0;
}
當指向這段代碼時,n將不是100,在函數結束後,n失去對該塊內存的使用權限,程序的其他代碼可以任意使用該塊內存,因而導致代碼出錯。
而如果實在函數內定義static靜態變量,則可以,因爲其存儲在靜態數據區,並不想局部變量一樣存儲在棧區,不會因爲函數結束就銷燬其內存區域。因而可以正常返回其地址。
將代碼中的int n = 100;
改爲static int n = 100;
即可。
11.二級指針
定義:
typename **p1;
代碼:
int a =100;
int *ip1 = &a;
int **ip2 = &ip1;
12. 二維數組
二維數組在概念上是二維的,有行和列,但在內存中所有的數組元素都是連續排列的
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
在內存中,a 的分佈是一維線性的,整個數組佔用一塊連續的內存:
C語言中的二維數組是按行排列的,也就是先存放 a[0] 行,再存放 a[1] 行,最後存放 a[2] 行;每行中的 4 個元素也是依次存放。數組 a 爲 int 類型,每個元素佔用 4 個字節,整個數組共佔用 4×(3×4) = 48 個字節。
C語言允許把一個二維數組分解成多個一維數組來處理。對於數組 a,它可以分解成三個一維數組,即 a[0]、a[1]、a[2]。每一個一維數組又包含了 4 個元素,例如 a[0] 包含 a[0][0]、a[0][1]、a[0][2]、a[0][3]。
假設數組 a 中第 0 個元素的地址爲 1000,那麼每個一維數組的首地址如下圖所示
先看一下以下定義:
int (*p)[4] = a;
括號中的*
表明 p
是一個指針,它指向一個數組,數組的類型爲int [4]
,這正是a
所包含的每個一維數組的類型
注意:
[ ]
的優先級高於*
,( )
是必須要加的,如果赤裸裸地寫作int *p[4]
,那麼應該理解爲int *(p[4])
,p 就成了一個指針數組,而不是二維數組指針。
對指針進行加法(減法)運算時,它前進(後退)的步長與它指向的數據類型有關,p 指向的數據類型是int [4],那麼p+1就前進 4×4 = 16 個字節,p-1就後退 16 個字節,這正好是數組 a 所包含的每個一維數組的長度。也就是說,p+1會使得指針指向二維數組的下一行,p-1會使得指針指向數組的上一行。
數組名a在表達式中也會內轉換爲和p等價的指針
訪問二維數組:
-
p指向數組 a 的開頭,也即第 0 行;p+1前進一行,指向第 1 行。
-
*(p+1)表示取地址上的數據,也就是整個第 1 行數據。注意是一行數據,是多個數據,不是第 1 行中的第 0 個元素
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
int(*p)[4] = a;
printf("%d\n", sizeof(*(p + 1)));
printf("%d\n", sizeof(*(a + 1)));
out:
16
16
3)*(p+1)+1表示第 1 行第 1 個元素的地址
*(p+1)單獨使用時表示的是第 1 行數據,放在表達式中會被轉換爲第 1 行數據的首地址,也就是第 1 行第 0 個元素的地址;就像一維數組的名字,在定義時或者和 sizeof、& 一起使用時才表示整個數組,出現在表達式中就會被轉換爲指向數組第 0 個元素的指針
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
int(*p)[4] = a;
//printf("%d\n", sizeof(*(p + 1)));
//printf("%d\n", sizeof(*(a + 1)));
printf("%#x\n", *(p + 1));
printf("%#x\n", *(p + 1) + 1);
printf("%#x\n", *(p + 2));
out:
0xddfb44
0xddfb48
0xddfb54
4)((p+1)+1)表示第 1 行第 1 個元素的值
根據以上結論,可以退出一下等價關係:
a+i == p+i
a[i] == p[i] == *(a+i) == *(p+i)
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)
二維數組遍歷方式:
int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
int(*p)[4] = a;
int i, j;
//下標訪問
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) printf("%2d ", a[i][j]);
printf("\n");
}
//指針訪問1
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) printf("%2d ", a[i][j]);
printf("\n");
}
//指針訪問2
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) printf("%2d ", *(*(p+i)+j));
printf("\n");
}
//指針訪問3
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) printf("%2d ", *(p[i] + j));
printf("\n");
}
13.函數指針
函數在內存中佔據一段連續的內存區域,函數名在表達式中也被轉換爲函數所在內存區域的首地址,和數組名類似。可以將函數的首地址賦值給指針變量,然後可以通過指針變量就可以調用該函數,這種指針稱爲函數指針。
定義:
returnType (*pointerName)(param list);
returnType 爲函數返回值類型,pointerNmae 爲指針名稱,param list 爲函數參數列表。參數列表中可以同時給出參數的類型和名稱,也可以只給出參數的類型,省略參數的名稱,這一點和函數聲明類似,但是注意指針參數列表和要指向的函數參數列表要完全一樣
int add(int, int);
int main() {
int a = 10, b = 5;
int(*padd)(int, int) = add;
int addition = (*padd)(a, b);
printf("addition of a and b:%d", addition);
system("pause");
return 0;
}
int add(int a , int b) {
return a + b;
}
out:
addition of a and b:15
14 總結:
15 動態分配內存
c
- malloc(size) 分配內存
- free() 釋放指針
比如要分配長度16的double數組:
int n = 16;
double *arr = NULL;//聲明,並指向NULL,避免野指針
arr = (double *)malloc(n * sizeof(double));
if (!arr) {
printf("分配失敗!!!");
}
//賦值
for (int i = 0; i < 16; i++) {
arr[i] = i;
}
for (int i = 0; i < 16; i++) {
printf("%f\n", arr[i]);
}
free(arr);//釋放指針
c++
- new 動態申請
- delete 釋放內存
在c++中,malloc函數依然存在,但是推薦使用new關鍵字,因爲與malloc相比,new不僅分配了內存,還創建了對象。
-
指針變量名=new 類型(初始化)
-
delete 指針名
new表達式的操作序列爲:在自有存儲區(棧區)爲對象或變量分貝內存,然後使用括號中的值初始化該對象或變量。 new表達式是調用庫中操作符new()完成的
比如int *pi=new int(0);
而當pi指向的對象或變量的聲明週期結束時,需要釋放pi所指向的目標所佔用的內存空間:
delete pi;
注意的是,釋放了pi所指向的內存空間,但是並未釋放指針pi本身,也就是說pi本省所佔用的內存空間並未釋放。
c++ vs有保護機制
幾種初始化方式:
//1
int *p=NULL;
p=new int(100);
//2
int *p=new int;
*p=100;
//3
int *p=new int[100];
關於數組delete釋放需要注意:
一次只能釋放申請的一維的數組地址,如果是二維數組,要先釋放列在釋放行
- 一維數組的的申請釋放
指針變量名=new 類型名[長度];
delete []指針變量名
int n;
char *pc;
cout << "請輸入動態數組的元素個數" << endl;
cin >> n;
pc = new char[n];//申請25個字符,可以裝下12個漢字和1個結束符
strcpy(pc, "自由存儲區內存的動態分配");
cout << pc << endl;
delete[]pc;
- 二維動態數組的創建和釋放
加上變量名理解!!!
int **array
// 假定數組第一維長度爲 m, 第二維長度爲 n
// 動態分配空間
array = new int *[m];//分配m行的內存空間,指向各行的指針數組,每一個元素指向一行
for( int i=0; i<m; i++ )
{
array[i] = new int [n] ;//爲每一行分配內存空間
}
//賦值
for(int i=0;i<m;++i)
for(int j=0;j<n;++j){
array[i][j] = i * n + j;
}
//顯示
for(int i=0;i<m;++i){
for(int j=0;j<n;++j){
cout<<array[i][j] <<' ';
}
cout<<endl;
}
//釋放
for( int i=0; i<m; i++ )
{
delete [] arrar[i];
}
delete [] array;
指向結構體/類:
struct Share {
long int share_price;
string time_date;
};
int main()
{
//動態創建結構體指針
Share *pS = new Share;
pS->share_price = 100;
pS->time_date = "2017_09_10 09:10";
//定義結構體數組
Share *pSa = new Share[2];
//賦值
for (int i = 0; i < 2; ++i) {
pSa->share_price = (i + 1) * 100;
pSa->time_date = "2017_09_10";
}
}
參考: