預備知識
1.所有的指標變數只占4個子節 用第一個位元組的地址表示整個變數的地址
//1.cpp 所有的指標變數只占4個子節 用第一個位元組的地址表示整個變數的地址 # include <stdio.h> int main(void) { double * p; double x = 66.6; p = &x; //x占8個位元組 1個位元組是8位, 1個位元組一個地址, p中只存放一個地址,通常是第一個位元組的地址 // 一個變數占好多好多位元組,但是我們通常用一個位元組的地址來表示整體的地址,即首地址來表示它 double arr[3] = {1.1, 2.2, 3.3}; double * q; q = &arr[0]; printf("%p\n", q); //%p實際就是以十六進制輸出 //[Out]: //0019FF1C //0019FF24 q = &arr[1]; printf("%p\n", q); // 一個指標變數,無論它指向的變數占多少個位元組,它本身的大小都是一樣的,只占4個位元組 return 0; }View Code
2.如何通過函式修改實參的值
//3.cpp 如何通過函式修改實參的值 # include <stdio.h> //void f(int *q); void f(int **q); int main(void) { int i = 9; int * p = &i; //等價于int *p; p = &i; printf("%p\n", p); f(&p); // 無論引數是什么樣的變數,要改變它的值,只需要改變它的地址即可 printf("%p\n", p); return 0; } //void f(int * q) //{ // q = (int *)0xFFFFFFFF; //} //[Out]: //0019FF3C //0019FF3C 并未改變主函式的值,因為沒有修改它的地址 void f(int ** q) { *q = (int *)0xFFFFFFFF; } //0019FF3C //FFFFFFFF //以上寫法程式不安全,不能寫成**的方式來更改main函式中的值View Code
一、結構體
1.為什么會出現結構體
為了表示一些復雜的資料,而普通的基本型別變數無法滿足要求(實際上就是java/python中的類)
如: 僅僅用int age; str name 無法區分表示多個學生的整體特征
結構體中沒有方法,模擬一個事物模擬得不是很徹底;但好處在于,撰寫程式時,是以演算法為核心的,
學資料結構,演算法在c語言/面向程序的語言中,是最好的,但是在java這種面向物件的語言中,演算法就不是特別核心了,
2.什么叫結構體
結構體是用戶根據實際需要,自己定義的復合資料型別
3.如何使用結構體
//struct_1.cpp 結構體的使用概述 # include <stdio.h> # include <string.h> struct Student //結構體是資料型別,而不是變數,定義變數一定要分配記憶體, //struct Student是一個整體,是一個模型,有三個成員(不是屬性,java/python中才叫屬性);但是定義結構體并沒有分配記憶體,只是定義了一個新的資料型別 { int sid; char name[200]; int age; }; //分號不能省 int main(void) { struct Student st = {1000, "zhangsan", 20}; //用struct Student 這個資料型別,定義了一個變數st,賦值;struct Student一定要整體寫 printf("%d %s %d\n", st.sid, st.name, st.age); // 第一次撰寫時一定要編譯,不然VC6不會提示,編譯了一次以后就可以提示了 //[Out]: //1000 zhangsan 20 // st.sid = 99; //error, c++中不能這樣賦值,java/python才可以,必須按照上面的寫法 strcpy(st.name, "lisi"); // strcpy實際上就是復制的作用,在開頭要添加上# include <string.h> st.age = 22; printf("%d %s %d\n", st.sid, st.name, st.age); //[Out[ //1000 zhangsan 20 //1000 lisi 22 // printf("%d %s %d\n", st); //error return 0; }View Code
//struct_2.cpp 結構體的兩種使用方式 # include <stdio.h> int main(void) { struct Student st = {1000, "zhangsan", 20}; //st.sid = 99; // 如何使用結構體變數?第一種方式,很少使用,因為不會定義很多變數的名字 struct Student * pst = &st; // 第二種方式是最常用的: 定義一個指標變數*pst,存放struct Student這種變數地址, // struct Student這個變數占多少個位元組? int sid --4 ; char name[200] --200 ; int age --4; 共計208個(理論上,實際上很可能不是) //pst = &st; //pst存放st的地址,則pst指向st了,*pst就是st了 pst->sid = 99; //pst->sid 等價于(*pst).sid, 沒有什么為什么,是規定,只需要記住;而(*pst).sid等價于st.sid, 所以pst->sid 等價于st.sid }View Code
3.1.兩種方式:
struct Student st = {1000, "zhangsan", 20};
struct Student * pst = &st;
3.1.1. st.sid
3.1.2.pst->sid
//pst所指向的結構體變數中,有sid這個成員(最終表示的就是sid這個成員)
4.注意事項:
4.1.結構體變數不能加減乘除,但可以相互賦值;
4.2.普通結構體變數和結構體指標變數作為函式傳參的問題
//struct_3.cpp 結構體變數 # include <stdio.h> # include <string.h> struct Student { int sid; char name[200]; int age; }; void f(struct Student * pst); //前綴宣告 void g(struct Student st); void g2(struct Student * pst); int main(void) { struct Student st; f(&st); //printf("%d %s %d\n", st.sid, st.name, st.age); g(st); // 通過一個函式實作輸出,而不是直接輸出,將208個位元組賦值給另一個函式,這種方式速度慢,浪費空間,不推薦 g(&st); // 結果輸出速度快很多 return 0; } //[Out]:99 zhangsan 22 void g(struct Student st) { printf("%d %s %d\n", st.sid, st.name, st.age); } void g2(struct Student *pst) { printf("%d %s %d\n", pst->sid, pst->name, pst->age); // 通過指標只花了4個位元組的空間,也能夠實作 } void f(struct Student * pst) // 將st的地址發送給了pst,不是*pst { (*pst).sid = 99; // 普通變數使用.來呼叫 strcpy(pst->name, "zhangsan"); // 指標變數用->來呼叫 pst->age = 22; }View Code
二、動態記憶體的分配和釋放
1.動態構造一維陣列
假設動態構造一個int型陣列
int *p = (int *) malloc(int len);
1.1. malloc只有一個int型的形參,表示要求系統分配的位元組數
1.2.malloc函式的功能是請求系統len個位元組的記憶體空間,如果請求分配成功,則回傳第一個位元組的地址,如果分配不成功,則回傳NULL
1.3.malloc函式能且只能回傳第一個位元組的地址,所以我們需要把這個無實際意義的第一個位元組的地址(俗稱干地址)轉化為一個有實際意義的地址
因此,malloc前面必須加(資料型別*),表示把這個無實際意義的第一個地址,通過強制型別轉換,來告訴編譯器,我們回傳的第一個型別的地址,它到底是整型地址,還是其他型別的地址,即為前面(int *)型別的含義,
如: int *p = (int *)malloc(50):
表示將系統分配好的50個位元組的第一個位元組的地址轉化為整型地址,更準確的說是把第一個位元組的地址轉化為4個位元組,這樣p就指向了第一個的4個位元組,
p+1就指向了第2個的4個位元組,p[0]就是第一個元素
double *p = (double *)malloc(80);
表示將系統分配好的80個位元組的第一個位元組的地址轉化浮點型地址,更準確的說是把第一個位元組的地址轉化為8個位元組,這樣p就指向了第一個的8個位元組,
p+1就指向了第2個的8個位元組,p+i就指向了第i+1個的8個位元組,p[0]就是第一個元素
free(p) 釋放p所指向的記憶體,而不是釋放p本身所占用的記憶體
# include <stdio.h> # include <malloc.h> int main(void) { int a[5] = {4, 10, 2, 8, 6}; int len; printf("請輸入你需要分配的陣列的長度: len = "); scanf("%d", &len); int *pArr = (int *)malloc(sizeof(int) *len); //分配20個位元組,邏輯上的效果:pArr指向了前4個位元組,pArr +1 指向后4個 // int * 強制轉換,malloc只能回傳第一個位元組地址 //輸入4 //[Out]:4 10 // 不需要寫死,可以動態的分配記憶體malloc(sizeof(int) *len) // *pArr = 4; //類似于 a[0]=4;pArr指向前4個位元組 // pArr[1] = 10; //類似于a[1]=10; // printf("%d %d", *pArr, pArr[1]); // 我們可以把pArr當做一個普通陣列來使用 for (int i=0; i<len; ++i) //退出for回圈 int就沒有了 scanf("%d", &pArr[i]); for (i=0; i<len; ++i) //退出for回圈 不能再加int, i還能用 要不然就重復定義了 printf("%d\n", *(pArr+i)); free(pArr); //程式運行中可以釋放,把pArr所代表的動態分配的20個位元組的記憶體釋放 //輸入4 //[Out]: // 1 2 3 4 // 1 // 2 // 3 // 4 return 0; }View Code
2.跨函式使用記憶體:只能通過動態來實作
//test1、下程式中,能夠通過呼叫函式fun,使main函式中的指標變數p指向一個合法的整型單元的是: //整型單元-》合法變數 //A) main() { int *p; //主函式的指標變數p,此時為p中為一個垃圾數字 fun(p); //通過呼叫p,使它指向一個合法的整型變數,但是直接呼叫p,是無法改變數值的 // 參考筆記1-> 如何通過被調函式,修改主調函式中普通變數的值: 1)實參為相關變數的地址:&i ... } int fun(int *p) { int s; p=&s; } //B) main() { int *p; fun(&p); // 參考筆記1-> 如何通過被調函式,修改主調函式中普通變數的值: 1)實參為相關變數的地址:&i ... } int fun(int **q) // 此時*q即為p,將p的地址發送給q,型別一致 參考筆記1-> 如何通過被調函式,修改主調函式中普通變數的值: 2)形參為以該變數的型別為型別的指標變數:*p { int s; *q=&s; // 即為p=&s;參考筆記1-> 如何通過被調函式,修改主調函式中普通變數的值: 1)實參為相關變數的地址:p=&i } //執行完此行代碼時,p已經保存了一個合法的整型變數地址s了,但問題是執行完成后,該函式就沒有了,則s也沒有了,最終就是p沒有指向合法的變數s了, //C) #include <stdlib.h> main() { int *p; fun(&p); // 添加了取地址符,正確 ... } int fun(int **q) { *q=(int*)malloc(4); //構造一個int型陣列, 請求系統分配4個位元組的記憶體空間,此時*q即為p,則p指向了4個位元組 } //執行完此行代碼時,p已經指向了4個位元組,且動態分配的記憶體沒有釋放,必須手動通過free釋放 //D) #include <stdlib.h> main() { int *p; fun(p);// 未添加取地址符,錯誤 ... } int fun(int *p) { p=(int *)malloc(sizeof(int)); }View Code
故: 通過此題,可以了解到,我們可以跨函式使用記憶體
注意:
結構體變數不能加減乘除,但可以相互賦值
普通結構體變數和結構體指標變數作為函式傳參的問題
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/135931.html
標籤:其他
下一篇:靜態創建二叉樹及其遍歷
