指針入門 ******
int i = 123;
//一般計算機中用16進制數(shù)來表示一個內存地址
printf("%#x\n",&i);
//int* int類型的指針變量 pointer指針 指針變量只能用來保存內存地址
//用取地址符&i 把變量i的地址取出來 用指針變量pointer 保存了起來
//此時我們可以說 指針pointer指向了 i的地址
int* pointer = &i;
printf("pointer的地址 = %#x\n",pointer);
printf("*pointer的值%d\n",*pointer);
*pointer = 456;//修改pointer的值
printf("i的值是%d\n",i);
system("pause");
由于pointer存的是i的內存地址,多以對pointer的操作就是對變量i的操作*pointer在等號左邊就是賦值操作,在等號右邊是取值操作
java zhong 兩個值的交換操作
![IJUK6LYQGDJ_2]~D10%X0.png](http://upload-images.jianshu.io/upload_images/2648920-33ec6258097a0db3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 指針常見錯誤
- 聲明了指針變量后 未初始化直接通過*p 進行賦值操作 運行時會報錯
- 未賦值的指針稱為野指針
- 指針類型錯誤 如int* p 指向了double類型的地址, 通過指針進行讀取操作時,讀取值會出錯
- 聲明了指針變量后 未初始化直接通過*p 進行賦值操作 運行時會報錯
#include<stdio.h>
#include<stdlib.h>
/**
*/
main(){
//野指針 指針使用之前要初始化 賦給它一個自己程序中聲明的變量的地址
//指針使用的時候要注意 int類型的指針要指向int類型的內存地址, double類型的指針要指向double類型的地址 ....
//如果亂指會出bug
int i;
double d = 3.1415;
int* pointer = &d;
printf("pointer的值=%#x\n",pointer);
printf("*pointer = %d\n",*pointer);
system("pause");
}
指針的練習

Paste_Image.png
- 值傳遞和引用傳遞(交換兩個數(shù)的值)
引用傳遞本質是把地址傳遞過去
-
所有傳遞其實本質都是值傳遞,引用傳遞其實也是傳遞一個值,但是這個值是一個內存地址
void swap(int* p, int* p2){ int temp = *p; *p = *p2; *p2 = temp; } main(){ int i = 123; int j = 456; //將i, j的地址傳遞過去 swap(&i,&j); printf("i = %d, j = %d", i, j); }

Paste_Image.png
- 返回多個值
- 把地址作為參數(shù)傳入函數(shù)中,當函數(shù)執(zhí)行完畢時,參數(shù)的值就已經(jīng)被修改了
#include<stdio.h>
#include<stdlib.h>
/**
"*" 的幾個含義 int* 聲明一個int類型的指針變量
x * y 乘法運算
int* pointer;
*pointer; 取出指針變量pointer 中保存的內存地址對應的內存中的值
*/
function(int* pointer, int* pointer2){
*pointer *= 2;
*pointer2 *=2;
}
main(){
int i =1;
int j = 2;
//char c;
function(&i,&j);
printf("i = %d,j = %d\n",i,j);
system("pause");
}
多級指針
int* p; int 類型的一級指針 int** p2; int 類型的二級指針
二級指針變量只能保存一級指針變量的地址
有幾個* 就是幾級指針 int*** 三級指針
-
通過int類型三級指針 操作int類型變量的值 ***p
int i = 123; //int類型一級指針 int* p = &i; //int 類型 二級指針 二級指針只能保存一級指針的地址 int** p2 = &p; //int 類型 三級指針 三級指針只能保存二級指針的地址 int*** p3 = &p2; //通過p3 取出 i的值 printf("***p3 = %d\n", ***p3); 多級指針案例 取出子函數(shù)中臨時變量的地址

Paste_Image.png
#include<stdio.h>
#include<stdlib.h>
/**
多級指針 數(shù)星星 有幾個星就是幾級指針 取值通過三級指針拿到最初的變量的值 ***point3
*/
main(){
int i = 123;
//int類型的一級指針
int* pointer = &i;
//int類型的二級指針 二級指針只能保存一級指針的地址
int** pointer2 = &pointer;
//int類型的三級指針
int*** pointer3 = &pointer2;
printf("***pointer3 = %d\n",***pointer3);
system("pause");
}
數(shù)組和指針的關系
- 數(shù)組占用的內存空間是連續(xù)的
- 數(shù)組變量保存的是第0個元素地址,也就是首地址
- *(p + 1):指針位移一個單位,一個單位是多少個字節(jié),取決于指針的類型
#include<stdio.h>
#include<stdlib.h>
/**
數(shù)組實際上就是一塊連續(xù)的內存空間
*/
main(){
// char array[] = {'a','b','c','d','\0'};
int array[] = {1,2,3,4};
printf("array[0]的地址%#x\n",&array[0]);
printf("array[1]的地址%#x\n",&array[1]);
printf("array[2]的地址%#x\n",&array[2]);
printf("array[3]的地址%#x\n",&array[3]);
printf("array的地址%#x\n",&array);
//數(shù)組變量名的地址實際上是第一個元素的地址
char* pointer = &array;
//int* pointer = &array;
char array2[] = "hello from c"
//c語言當中的char類型的數(shù)組就是c語言當中的字符串,現(xiàn)在*pointer存的是數(shù)組的首地址,printf("%s\n",pointer2);是字符串的輸出,所以從char數(shù)組的首地址開始輸出字符串
char* pointer2="hello from c";
//printf("%s\n",pointer2);
/*
printf("*(pointer+0)=%c\n",*(pointer+0));
printf("*(pointer+0)=%c\n",*(pointer+1));
printf("*(pointer+0)=%c\n",*(pointer+2));
printf("*(pointer+0)=%c\n",*(pointer+3));
*/
printf("*(pointer+0)=%d\n",*(pointer+0));
printf("*(pointer+1)=%d\n",*(pointer+1));
printf("*(pointer+2)=%d\n",*(pointer+2));
printf("*(pointer+3)=%d\n",*(pointer+3));
system("pause");
}
指針的長度
- 不管變量的類型是什么,它的內存地址的長度一定是相同的
- 類型不同只決定變量占用的內存空間不同
- 32位環(huán)境下,內存地址長度都是4個字節(jié),所以指針變量長度只需4個字節(jié)即可
- 區(qū)分指針類型是為了指針位移運算方便
#include<stdio.h>
#include<stdlib.h>
/**
32位操作系統(tǒng)地址總線是32位 4個字節(jié)的變量來保存32位操作系統(tǒng)的內存地址 1byte 8位 4*8=32
32位操作系統(tǒng) 指針變量占4個字節(jié)
64位操作系統(tǒng) 指針變量占8個字節(jié)
*/
main(){
int* pointer;
double* pointerD;
printf("int類型的指針變量占%d個字節(jié)\n",sizeof(pointer));
printf("double類型的指針變量占%d個字節(jié)\n",sizeof(pointerD));
system("pause");
}
#include<stdio.h>
#include<stdlib.h>
/**
main函數(shù)獲取子函數(shù)中臨時變量的地址
*/
function(int** pointer){
int i = 123;
*pointer = &i;
printf("i的地址%#x\n",&i);
}
main(){
int* pointer1;
function(&pointer1);
printf("pointer1的值%#x\n",pointer1);
system("pause");
}

Paste_Image.png
堆棧概念 靜態(tài)內存分配 動態(tài)內存分配
- 棧內存
- 系統(tǒng)自動分配
- 系統(tǒng)自動銷毀
- 連續(xù)的內存區(qū)域
- 向低地址擴展
- 大小固定
- 棧上分配的內存稱為靜態(tài)內存
- 靜態(tài)內存分配
- 子函數(shù)執(zhí)行完,子函數(shù)中的所有局部變量都會被銷毀,內存釋放,但內存地址不可能被銷毀,只是地址上的值沒了
#include<stdio.h>
#include<stdlib.h>
/**
棧內存 系統(tǒng)統(tǒng)一分配統(tǒng)一回收
靜態(tài)內存分配 棧內存大小固定的 內存地址是連續(xù)的
*/
int* getData(){
int array[] ={1,2,3,4,5};
printf("%#x\n",&array);
return &array;
}
int* getData2(){
int array[] ={5,4,3,2,1};
printf("%#x\n",&array);
return &array;
}
main(){
int* pointer = getData();
getData2();
printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
printf("%#x\n",pointer);
system("pause");
}
- 堆內存
- 程序員手動分配
- java:new
- c:malloc
- 空間不連續(xù)
- 大小取決于系統(tǒng)的虛擬內存
- C:程序員手動回收free
- java:自動回收
- 堆上分配的內存稱為動態(tài)內存
- 程序員手動分配
#include<stdio.h>
#include<stdlib.h>
/**
java new對象就會申請一塊堆內存
c malloc memory allocation 內存分配
c的堆內存 程序員手動申請手動釋放 malloc
free
申請一塊堆內存 動態(tài)內存分配
堆內存 不連續(xù)的
堆內存大小不固定 取決機器的狀態(tài)
*/
main(){
//malloc 接收的參數(shù) 申請內存大小 返回一個內存地址值 申請到的也是一塊連續(xù)的內存空間
int* pointer = malloc(sizeof(int)*5);
*(pointer+0) = 1;
*(pointer+1) = 2;
*(pointer+2) = 3;
*(pointer+3) = 4;
*(pointer+4) = 5;
//C for 循環(huán) 循環(huán)的臨時變量i 要先聲明再使用
int i;
for(i = 0;i<5;i++){
printf("第%d個元素的值= %d\n",i,*(pointer+i));
}
free(pointer);
printf("第一個元素的值%d\n",*(pointer+0));
system("pause");
}
#學生管理系統(tǒng)#
#include<stdio.h>
#include<stdlib.h>
/**
保存班級人數(shù)
申請一塊堆內存保存學生的學號
來了幾個插班生
擴展一下堆內存
保存插班生的學號
realloc re-
*/
main(){
printf("請輸入班級的人數(shù):");
int count;
scanf("%d",&count);
//申請一塊堆內存
int* pointer = malloc(sizeof(int)*count);
int i;
for(i = 0;i<count;i++){
printf("請輸入第%d個學生的學號:",i+1);
scanf("%d", pointer+i);
}
for(i = 0;i<count;i++){
printf("第%d個學生的學號是:%d\n",i+1,*(pointer+i));
}
printf("請輸入插班生的人數(shù):");
//聲明一個變量increment用來保存 插班生的人數(shù)
int increment;
//接受用戶的輸入
scanf("%d",&increment);
//重新申請一塊足夠大的內存
//如果 malloc申請到的內存后面還有足夠的空間 realloc會在malloc申請的內存空間后繼續(xù)申請足夠大的內存空間
//如果 malloc申請到的內存后面沒有足夠的空間 realloc會找到一塊足夠大的堆內存 并且把 malloc申請到的內存中的值復制過來
pointer = realloc(pointer,sizeof(int)*(count+increment));
for(i = count;i<count+increment;i++){
printf("請輸入第%d個學生的學號:",i+1);
scanf("%d", pointer+i);
}
for(i = count;i<count+increment;i++){
printf("第%d個學生的學號是:%d\n",i+1,*(pointer+i));
}
system("pause");
}
結構體
- 結構體中的屬性長度會被自動補齊,這是為了方便指針位移運算
- 結構體中不能定義函數(shù),可以定義函數(shù)指針
- 程序運行時,函數(shù)也是保存在內存中的,也有一個地址
- 結構體中只能定義變量
- 函數(shù)指針其實也是變量,它是指針變量
- 函數(shù)指針的定義 返回值類型(*變量名)(接收的參數(shù));
- 函數(shù)指針的賦值: 函數(shù)指針只能指向跟它返回值和接收的參數(shù)相同的函數(shù)
#include<stdio.h>
#include<stdlib.h>
/**
c結構體 類似java的class struct來聲明c的結構體
結構體的大小大于等于結構體中每一變量的占字節(jié)數(shù)的和
結構體的大小是最大的那個變量所占字節(jié)數(shù)的整數(shù)倍
C結構體中不能定義函數(shù)
函數(shù)指針的定義 返回值(*函數(shù)指針變量名字)(返回值);
-> 間接引用運算符
*/
void study(){
printf("good good study!\n");
}
typedef struct Student{
int age; //8
int score; // 4
char sex; //1
void(*studypointer)();
} stud;
main(){
stud stu = {18,100,'f'};
stu.studypointer = &study;
stu.studypointer();
struct Student* stuPointer = &stu;//結構體指針
printf("*stuPointer.age = %d\n",(*stuPointer).age);
(*stuPointer).sex ='m';
printf("stu.sex = %c\n",stu.sex);
printf("stuPointer->age = %d",stuPointer->age);
//printf("stu.age = %hd\n",stu.age);
//printf("stu.score = %d\n",stu.score);
//printf("stu.sex = %c\n",stu.sex);
// printf("結構體student占%d個字節(jié)\n",sizeof(stu));
system("pause");
}
聯(lián)合體
- 長度等于聯(lián)合體中定義的變量當中最長的那個
- 聯(lián)合體只能保存一個變量的值
- 聯(lián)合體共用同一塊內存
#include<stdio.h>
#include<stdlib.h>
/**
聯(lián)合體占字節(jié)數(shù)取決于 其中成員 占內存空間最大的那一個
*/
union u{
int num; //4
double d; //8
}
main(){
union u u1;
u1.num = 123;
u1.d = 123.456;
printf("union占%d個字節(jié)\n",sizeof(u1));
printf("u1.num = %d\n",u1.num);
system("pause");
}