文章目錄
前言 :建議加上宏定義:允許使用過時的函數和不安全的函數,去掉警告
#define _CRT_SECURE_NO_WARNINGS
此篇文章爲筆記型文章。方便筆者日後快速複習~~
導包語法:#include <xxx.h>
常用包:
- stdio 標準函數庫
- stdlib 標準函數庫(system、sizeof等等…)
- Windows windows系統的api
函數入口:
//導入標準函數庫(類似於java的導包)
#include <stdio.h>
//導入標準函數庫(system、sizeof等等......)
#include <stdlib.h>
//windows系統的api
#include <Windows.h>
void main(){
//輸出函數
printf("hello world!");
//這是一個命令(在window裏面特有的)
system("pause");
}
基本數據類型(int char float double long int short)
佔位符:
%d - int
%ld – long int
%c - char
%x - short
%f - float
%u – 無符號數
%hd – 短整型
%lf – double
%x – 十六進制輸出 int 或者long int 或者short int
%#X - 顯示十六進制
%o - 八進制輸出
%s – 字符串
輸入輸出函數:
//輸出函數
printf("請輸入一個整數:");
//輸入函數(注意:輸入函數賦值需要的是地址,而不是值)
//scanf("%d",&i);
//注意:不同編譯工具,對函數支持不一樣
scanf_s("%d", &i);
內存地址&指針:
int i = 100;
//輸出i的值
printf("i的值:%d\n",i);
//輸出i的地址
//&i:取出i變量的地址
//注意:輸出地址,佔位符%p
printf("i的地址:%p\n",&i);
//根據地址獲取值:*(&i)
//總結:&表示取地址 *表示根據地址取值
printf("根據i的地址取出i的值%d\n",*(&i));
//什麼是變量名:就是對內存的一段空間裏的數據的抽象
//0x001CFDD0
多級指針
int i= 99;
//打印i的值
printf("%d\n",i);
//打印i的地址
printf("%p\n", &i);
//定義一個指針變量
//int* 是一個int類型的指針變量,可以存儲一個int類型的變量地址
int* p = &i;
printf("指針變量p的值:%p\n",p);
//輸出指針的地址
printf("指針變量p的地址:%p", &p);
//直接賦值
i = 200;
printf("變量i的值:%d\n", i);
//間接賦值(通過指針變量)
*p = 500;
printf("指針變量賦值後的i的值:%d\n", i);
通過指針學習,可以在方法中傳遞指針地址,在方法內部通過指針地址變量操作真實數據。
方法形參若爲真實變量形參,例如:int a; 則會在方法內爲a開闢一個新內存。修改a的值並不能修改原始值。
指針和地址的區別:指針有類型,地址沒有類型。
指針需要通過類型判斷開闢多大內存空間,例如int 開闢4字節。char開闢兩字節。
指針必須要有初始值。可以爲NULL,但是不能不賦值。否則會報錯
多級指針另一種寫法:int** p2 = &p1;
p2保存的數據是p1地址,*p2上的數據是p1上面數據, p1保存的數據是a的地址,**p2實際上就是a的值
數組
數組遍歷
總結一:ids常量指針,存儲的是數組的首地址
總結二:數組在內存中排列是連續不斷(線性排列)
int ids[] = {23,15,67,38,99,70};
//打印數組
//printf("數組:%#x\n", ids);
//數組第一個元素地址
//printf("數組第一個元素地址:%#x\n",&ids[0]);
//總結:p++每次向前移動sizeof(數據類型)個字節
int* p = ids;
//p++;
//printf("地址:%#x 值:%d", p, *p);
//遍歷數組
//傳統方式
//int i = 0;
//for (; i < 6; i++){
//printf("值:%d\n", ids[i]);
//}
//通過指針遍歷
for (; p < ids + 6; p++){
printf("地址:%#x 值:%d\n",p, *p);
}
//注意:讓指針遞增或者遞減,一般情況下只有在數組遍歷的時候纔有意義,
//基於數組在內存中是線性排列的方式(連續不斷)
數組賦值
int ids[6];
int* p = ids;
//傳統寫法(高級寫法)
//賦值
//int i = 0;
//for (; i < 6; i++)
//{
//ids[i] = i;
//}
//指針方式賦值
int i = 0;
for (; p < ids + 6; p++){
*p = i;
i++;
}
數組遍歷於指針關係分析
int ids[] = { 23, 15, 67, 38, 99, 70 };
int i= 0;
for (; i < 6; i++){
//輸出-高級寫法(常規寫法)
//printf("%d %#x\n", ids[i], &ids[i]);
//之前分析了ids就是常量指針,就是一個首地址
//以下分析是取地址
//分析:ids是首地址
//ids+0 等價於 &ids[0]
//ids+1 等價於 &ids[1]
//以此類推......
//總結:ids+i 等價於 &ids[i]
//分析取值
//*(ids+0) 等價於 ids[0]
//*(ids+1) 等價於 ids[1]
//以此類推......
//總結:*(ids+i) 等價於 ids[i]
printf("%d %#x\n",*(ids+i),ids+i);
}
二維數組
//兩行三列-二維數組
int ids[2][3] = { 23, 15, 67, 38, 99, 70 };
//遍歷二維數組:外層循環控制行,內層循環控制列
int i = 0;
for (; i < 2; i++){
int j = 0;
for (; j < 3; j++){
printf("值:%d 地址:%#x ", ids[i][j],&ids[i][j]);
}
printf("\n");
}
二維數組推理分析:
//打印第二行,第二列
printf("%d\n",ids[1][2]);
//推導
//ids + 0 代表第一行 第一行的第一個值
//ids + 1 第二行(下一行) 第二行的第一個值
//以此類推:ids + i
//取每一行第一個元素的指針:*(ids+i)
//第一行第一個元素指針:*(ids+0)
//第二行第一個元素指針:*(ids+1)
//以此類推:*(ids+i)
//繼續推理
//取第一行第一個指針:*ids + 0
//取第一行第二個指針:*ids + 1
//取第一行第三個指針:*ids + 2
//以此類推:*ids + j
//繼續推理
//取第一行第二個指針:*(ids+0)+1
//取第二行第二個指針:*(ids+1)+1
//以此類推:*(ids+i)+j(這個公式獲取的是地址,是一個指針變量)
//*(*(ids+i)+j)
printf("%d", *(*(ids+1)+2));
指針比較
指針運算-指針大小比較
指針大小比較:都是在遍歷數組的時候運用,其他情況下沒什麼用
指針相等比較:指針地址相等,並不代表值相等,因爲指針有類型,例如:
int* p1 = (int*)0x006578;
double* p2 = (double*)0x006578;
函數指針
void showMessage(){
}
void add(int a, double b){
}
void main(){
//傳統的寫法
//直接調用
//showMessage();
//採用函數指針的方式調用
//void(*p)() = showMessage;
//p();
//帶返回值和參數列表的函數指針
double(*p1)(double a, double b) = add;
double d = p1(2,3);
printf("%lf", d);
//補充:函數指針-不僅僅是地址,必須明確的指定函數的指針類型,以及返回值和參數列表
//回想Java語言:方法重載(方法名相同,參數列表不同,或者參數類型不同)
getchar();
}
動態內存分配
malloc 分配內存
calloc 分配內存(更方便)
int* p = (int*)malloc(5 * sizeof(int));
//第一個參數:數組長度
//第二個參數:每個元素大小
int* p = (int*)calloc(5,sizeof(int));
free 回收內存
free(p)
realloc 重新分配內存
int* p1 = (int*)realloc(p,sizeof(int)*(len+addLen));
注:新的=老的+增加的
realloc更改已經配置的內存空間
- 縮小:會導致一部分數據丟失
- 擴大(連續不斷-線性排列)
- 情況一:如果當前的內存段後面有需要的內存空間,就會直接追加(注意:返回原指針)
- 情況二:如果當前內存段後面空閒的字節空間不夠,就會重新再堆內存中尋找能夠容納該數據大小的內存區域(注意:返回值新的內存地址,原來的被釋放)
- 情況三:如果沒有容身之處,申請內存失敗,將返回NULL,而且原來的指針有效
內存分配注意事項:
1、不能夠多次釋放內存
2、釋放內存之後,給原來的指針設置NULL
3、內存泄漏
int* p = (int*)calloc(5, sizeof(int));
...一頓操作...
//釋放內存
free(p);
//標記內存已經被釋放
p = NULL;
如下會導致內存泄漏
//在堆區開闢了一塊40M的內存空間
int* p = (int*)malloc(1024 * 1024 * 10 * 4);
p = NULL;
//回收內存
free(p);