注意:計算機中使用補碼來表示數據的!!!(由於正數的原碼、反碼、補碼相同,所以在計算機中顯示的二進制(補碼)和我們通過計算把數字轉碼成二進制(原碼)相同)
在c語言中沒有字符串,只能使用字符數組來表示字符串;每個字符數組最後面都有一個結束符\0,所以字符數組的長度都+1;
char c[] = "你好啊hello";
輸入輸出函數
%d - int
%ld – long int
%lld - long long
%hd – 短整型
%c - char
%f - float
%lf – double
%u – 無符號數
%x – 十六進制輸出 int 或者long int 或者short int
%o - 八進制輸出
%s – 字符串
c語言中數組不檢測越界
指針
內存地址
是用十六進制的數字來表示內存地址的 內存中的每一個字節都會有一個地址,地址相當於內存的門牌號
32位系統的地址總線長度是32位的,也就是說系統能分配給內存地址的數量是 2的32次方 個
為什麼32位系統最大只支持4G內存?
因為:系統最多能分配給內存地址的數量是:2^32 個,而每個地址對應一個字節,也就是總共 2^32 b(字節),2^32/1024/1024/1024 = 4G
內存修改器:找到變量所在的地址,然後修改地址上的值
指針變量是用來存儲地址的
一級指針
int* p;// 定義一個 int* 型的指針變量,這個變量只能保存地址,而且保存的地址上存放的數據必須是一個int型(因為:如果你存放其他數據類型,那麼你取那個指針存放的的值時,會取不到值) int i = 3; p = &i;// 將整數3的地址存放到指針p中 *p:引用這個指針所指向的地址上保存的數據
二級指針
int** q;// 定義一個二級指針,二級指針保存的是一級指針的地址
指針如果沒有賦值,那麼就不能使用,此時指針指向的地址是一個隨機值,這種指針叫野指針
值傳遞和引用傳遞
引用傳遞,本質上也是值傳遞,只不過傳遞的這個值是一個地址值
一個函數返回多個值
由於c語言是面向過程的,java是面向對象的,在java中,如果想要讓一個函數返回多個值的話,可以將函數中要返回的數據封裝成對象,然後就可以返回多個值了。 而在c語言中,一個函數只能返回一個值,
#include
#include
void function(int* p,int* q){
*p += 5;
*q += 5;
}
main(){
int i = 3;
int j = 5;
function(&i,&j);
printf("%d\n",i);// 8 直接修改內存地址保存的數據值
printf("%d\n",j);// 10
system("pause");
}
子函數獲取主函數變量的地址
#include
#include
void function(int* p){
printf("%#x\n",p);// 0x28ff44
}
main(){
int i = 3;
function(&i);
printf("%#x\n",&i);// 0x28ff44
system("pause");
}
主函數獲取子函數變量的地址
#include
#include
void function(int** p){
int i = 3;
*p = &i;
printf("i的地址為%#x\n",&i);// 0x28ff44
}
main(){
int* mainp;
function(&mainp);
printf("主函數獲取到i的地址為:%#x\n",mainp);// 0x28ff14,這裡打印的是mainp的值,而mainp是一個指針,所以它的值是一個地址
printf("主函數獲取到i的值為:%d\n",*mainp);//這裡獲取不到正確的值,因為函數中的變量,在函數結束之後會被銷毀。但是當注釋掉前面兩個打印語句之後,可以打印出3這個值,這是因為那個函數中的變量還沒來得及銷毀,所以你取出的值是一個"幻值",這種做法是不正確的
system("pause");
}
int i = 3; 那麼i的值就是3;int* p = &i; p的值是一個地址值(i的地址)
數組
數組每一個元素的地址都是連續的,數組是一個連續的內存空間 數組名保存的其實是第0個元素的地址 通過首地址的運算,就可以拿到每一個數組中每一個元素
指針的運算
int i[] ={1,2,3};
int* p = &i;
*P // 1
*(p+1) // 2
char* q = &i;
*q // 1
*(q+4) // 2 注意:這裡如果寫成*(q+1)是錯誤的!!!因為數組中存儲的是int型,每個元素占4個字節,指針應該向右位移4個字節才能拿到下一個元素
+1表示向右位移一個單位,一個單位是多少個字節,看指針類型而定;如果指針式int型的,那麼位移一個單位是4個字節,而如果指針類型是char型的,那麼位移一個單位是1個字節!
指針的長度
32位操作系統 指針長度4個字節,跟類型無關 64位操作系統 指針長度8個字節,跟類型無關 int* p和double* q的指針長度相同
堆和棧的區別:
(1) 在棧上分配的內存,叫靜態內存
(2) 在堆上分配的內存,叫動態內存
區別:
申請方式
棧: 由系統自動分配,後進先出,由系統釋放這個空間 堆: 需要程序員自己申請,並指明大小,在c中用malloc函數
如char* p1 = (char*) malloc(10); //14byte (雖然程序員申請了10個字節,但是由於p1是一個指針,指針的長度是4個字節,也就是占用4個字節,指針是保存在棧內存中,所以那句語句總共占用14個字節的空間)
但是注意p1本身是在棧中的
系統申請後的反應
棧:只要棧的剩余空間大於所申請空間,系統將為程序提供內存,否則將報異常提示棧溢出。 堆:首先應該知道操作系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時,會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序,另外,對於大多數系統,會在這塊內存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內存空間。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多余的那部分重新放入空閒鏈表中。
申請大小的限制
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。棧頂的地址和棧的最大容量是系統預先規定好的,在WINDOWS下,棧的大小是2M,如果申請的空間超過棧的剩余空間時,將提示overflow。因此,能從棧獲得的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。大小跟硬件有關
申請效率的比較
棧:由系統自動分配,速度較快。但程序員是無法控制的。 堆:由malloc/new分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便.
堆和棧中的存儲內容
棧:函數的各個參數、局部變量等。注意靜態變量是不入棧的。 堆:堆中的具體內容有程序員安排。
內存的回收
棧上分配的內存,編譯器會自動收回 堆上分配的內存,要通過free來顯式地收回,否則會造成內存洩漏。
堆內存申請
使用malloc函數申請堆內存
int* p = malloc(4 * 10);// 申請了40個字節大小的空間,並且申請的這40個空間是連續的,返回的是堆內存地址(用一個指針p接收)
釋放申請的堆內存
free(p);
realloc函數的使用
#include
#include
main(){
printf("請輸入班級人數:");
int count;
scanf("%d", &count);
int* p = malloc(count * sizeof(int));
int i;
for(i = 1; i <= count; i++){
printf("請輸入第%d個學生的學號:", i);
scanf("%d", p + i - 1);
}
printf("請輸入插班人數:");
int newCount;
scanf("%d", &newCount);
//現在原來舊的堆內存後面,擴展新的空間
//如果舊堆內存後面的空間已經被別的程序占用了,那麼就無法擴展
//如果不能擴展,就會尋找一個塊足夠大的內存區域,申請新的堆內存 ,並且會把舊堆內存的數據復制到新的堆內存中
//釋放舊的堆內存
p = realloc(p , (count + newCount) * sizeof(int));
for(i = count + 1; i <= count + newCount; i++){
printf("請輸入第%d個學生的學號:", i);
scanf("%d", p + i - 1);
}
for(i = 1; i <= count + newCount; i++){
printf("第%d個學生的學號為%d\n", i, *(p + i - 1));
}
system("pause");
}
結構體
struce 關鍵字 定義結構體
第一種
struct Student
{
int age;
float score;
char sex;
}
第二種
struct Student2
{
int age;
float score;
char sex;
} st2;
第三種
struct
{
int age;
float score;
char sex;
} st3
結構體中不能定義函數,但是可以定義函數指針 函數也是保存在棧內存中,就像定義變量一樣 結構體長度不固定,具體長度通過sizeof()得到
#include
#include
void study(){
printf("天天打怪獸\n");
}
// 定義結構體
// 結構體中不能定義函數,但是可以定義函數指針
struct student{
int age;
int height;
char sex;
void(*studyp)();//studyp 函數指針名
};
main(){
struct student std = {20,180,'f',study};// std 結構體變量
printf("芳齡%d\n",std.age);
std.studyp();// 第一種方法
struct student* stdp = &std;
(*stdp).studyp(); // 第二種方法
// ->左邊必須是結構體的一級指針,右邊是函數指針名字
stdp->studyp();// 第三種方法(開發中一般使用這種 )
system("pause");
}
聯合體
聯合體作用就是:聯合體變量可以被賦值聯合體定義的任意類型
#include
#include
main(){
union{int i; char c; short s} un;
un.i = 30;
un.s = 20;
printf("%d\n",un.i);
// 20 聯合體同時只能有一個值,後面的值會把前面賦給聯合體變量的值覆蓋掉
printf("%d\n",sizeof(un));
// 4個字節 聯合體長度取變量中的最大值 int
system("pause");
}
枚舉
#include
enum WeekDay
{
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};
int main(void)
{
//int day;
enum WeekDay day = Sunday;// 只能從定義中的內容取,否則報錯
printf("%d\n",day); // 6
system("pause");
return 0;
}
自定義類型
typedef newName oldType;
#include
#include
// hehe這個時候跟int的作用一樣,相當於給int起了個別名
typedef int hehe;
main(){
hehe i = 3;
printf("%d\n", i);// 3
system("pause");
}