Android NDK開發之C語法(指針相關)

前言 :建議加上宏定義:允許使用過時的函數和不安全的函數,去掉警告
#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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章