網站首頁 編程語言 正文
什么是指針
C/C++語言擁有在程序運行時獲得變量的地址和操作地址的能力,這種用來操作地址的特殊類型變量被稱作指針。
翻譯翻譯什么tmd叫tmd指針!
變量或常量的指針存儲的數據是 :對應的變量或常量在內存中的地址。
圖解:
此時 我們定義三個指針 p1, p2, p3 分別指向a, b, c ,那么p1中存儲的數據是變量a所占用內存的首地址:0x00;
b和c中存儲的數據是什么呢? 沒錯,就是0x02和0x04。
顯然。若要知道變量a的值,首先讀取指針p1中的數據0x00,然后讀取內存0x00和0x01中的數據就可以了。這個時候,會出現一個問題:怎么才能確定究竟要讀取幾個單位內存中的數據呢?畢竟p1中只存儲了0x00呀。這個時候 ,指針的類型就發揮了作用。舉個例子:如果定義的指針是int類型的,那么讀取的時候自動從指針中存儲的初值向后讀取4個字節;float類型的指針就是讀取8個字節。這樣就可以既完整又不多余的取出所需要的數據了。
總結一下一個指針的兩個要素:指針類型和指針所指變量的初地址。
定義指針變量
指針在定義的時候最好直接初始化,否則可能出現意想不到的結果。
@定義一個指向變量的指針。
/*定義一個指向int類型變量a的指針p*/
int a = 3;
int * p;
p = &a; //把a的地址賦值給p;
/*下面的語句最好不要用!!!!*/
p = &28; //這也是可以的,p指向的地址中存儲的數據是28.
/*可以用下面的語句代替上面的語句*/
*p = 28;
間接引用指針
間接引用操作符也是 *
*p 取出p指向的內存空間中的值。
所以區分定義或是引用指針可能有些麻煩。
只有同類型的指針和變量才能通過 * 和 & 互相建立關系。
int a = 0;
int temp;
int * p = &a;
/*指針的間接引用*/
cout<< *p; //輸出結果是0
/*以下兩種表達等價*/
*p = 3; //把3賦值給a
a = 3;
/*以下兩種表達效果相同。都是把變量a賦值給temp*/
temp = a;
temp = *p; //temp被賦值為0
/* 以下表達是錯誤的*/
float f = 3.1;
int * p = &f; //error,指針類型與變量類型不匹配
常or常常
這個部分本來應該在定義指針變量里,但是因為需要指針的間接引用作為鋪墊,所以把它單獨拿出來了
我們先區分一組概念:常量、指向常量的指針、常指針以及指向常量的常指針。
1.常量:顧名思義不發生改變的量,想必大家是熟悉的。
2.指向常量的指針:只限制間接訪問操作,不限制指針指向的值本身的操作規范。
3.常指針:指針中存儲的地址一經初始化就不能改變了,也就是說常指針只能指向一個固定的地址,但是地址中存儲的數據是可以改變的哦~
4.指向常量的常指針:根據上面三個概念大家應該可以理解了,就是指針中存儲的地址和該地址中的數據皆不可更改。
例如:
int b = 4;
const int a = 5; //定義一個常量a。
/*注意觀察以下三個定義中const的位置*/
const int *p = &a; //定義一個指向常量的指針p,指向常量a。
p = &b; //這個也是沒問題的哦
*p = 6; //error,這個間接訪問操作是不可以。
int * const p2 = &b;//定義一個常指針p2
*p2 = 6; //這個是可以的,注意區分和上面的區別
const int * const p3 = &a; //指向常量的常指針p3
/* emm我也不知道該注釋點什么,自行體會吧~*/
*p = 11; //error,因為p指向的是常量,常量的值不可更改
*p2 = 2; //true,p2是常指針,指向的地址不變,但地址中的值可以更改
*p3 = 4; //error
p3 = &b; //error
指向指針的指針
指針本質上也是一個變量或常量,那么指針也是有地址的,而指向這些地址的指針被稱為指向指針的指針。
int a = 25;
int * p = &a;
int pp = &p; //true,pp指向的地址中存儲的是指針p的地址。
指針與數組
一個數組的數組名就是一個常指針。
int arr[] = {5,4,6,9,8,3};
arr就是一個指針,而且指向數組的第一個元素arr[0]。
指針數組:
char * arr[] ={"this is", "a", "C++ !"};
在此提及一下字符串常量:
char * a
/*
雙引號的作用:
在字符串結尾加一個\0,并分配內存空間,返回首地址。
*/
a = "dshfw";
指針的運算
指針只能支持 + 和 - 的運算,但這已經足夠滿足大多數指針操作的需求了。
/*接下來以數組為例,只介紹加法 ,減法同理*/
int arr[] = {5,4,6,9,8,3};
cout<< *p; //輸出結果為5
cout<< *(p+1); //輸出結果為4
/*打印整個數組*/
for(int *p = arr; p < arr+5; ++p)
{
cout<< *p<<' ';
}
cout<<endl;
堆內存分配
C語言
/*函數原型*/
void * malloc(size_t size);
/*使用:開辟5個int類型變量的存儲空間,返回首地址*/
/**/
int *arr;
if(arr = (int *) malloc(5 *sizeof(int)) == NULL)
{
exit(1);
}
/*釋放堆內存*/
free(arr);
C++語言
注意釋放數組內存空間時,delete后有[] !!!
/*申請一個5個元素的數組空間*/
int * arr = new int[5];
delete[] arr;
/*申請一個變量的空間*/
int * arr = new int;
delete arr;
指針與函數
函數名和數組名一樣都是一個指針,有時我們需要把函數名作為參數傳入其他函數中。
數組名作為函數的入口參數
arr[] = {4,6,9,8,5,3,2,7,5};
/*兩種不同的寫法均可以*/
void Sort_Shell(int arr[], int n)
{
/*code*/
}
void Sort_Shell(int * arr, int n)
{
/*code*/
}
/*下面這種是不可以的!!!*/
void Sort_Shell(int * arr[], int n)
{
/*code*/
}
舉個小小的例子
/*來個小小的希爾排序算法*/
void Sort_Shell(int arr[], int n)
{
int gap = n / 2;
for(; gap > 0; gap /= 2)
{
for(int i = gap; i <n; ++i)
{
int temp = a[i];
int j = i;
while(j >= gap&& temp < arr[j - gap])
{
a[j] = a[j - gap];
j -=gap;
}
a[j] = temp;
}
}
}
開了個小差,接下來回歸正題,我們的指針。
函數名作為參數傳入其他函數
static bool cmp(int a, int b)
{
return a < b;
}
void show(bool * b)
{
if( &b)
{
cout<< true;
}
else
{
cout<<false;
}
}
使用指針修改函數參數
首先來看個例子:
void swip(int a, int b)
{
int c = a;
a = b;
b = c;
}
int main()
{
int a = 5, b = 6;
swip(a, b);
cout<<a<<' '<<b;
}
輸出結果:5 6
并未達到交換的效果,因為函數內部對形參的修改并不能反映到上層的main函數中。
此時我們可以通過指針作為函數的入口參數來實現預期的功能。
void swip(int * a, int* b)
{
int c = *a;
*a = *b;
*b = c;
}
int main()
{
int a = 5, b = 6;
swip(&a, &b);
cout<<a<<' '<<b;
}
輸出結果:6 5
傳遞函數的指針雖然能達到預期效果,但是確實以破壞函數的黑盒為代價,可讀性差,調試困難。
有沒有什么更好的辦法呢?接下來 引用登場了。
變量的引用作為函數的參數
void swip(int &a, int &b)
{
int c = a;
a = b;
b = c;
}
int main()
{
int a = 5, b = 6;
swip(a, b);
cout<<a<<' '<<b;
}
輸出結果:6 5
克服了指針作為參數的弊端。話又有點多了,哈哈,這和指針有什么關系呢······
兩個常用的字符串函數
- strcmp() :用于比較字符串的大小。
- strcpy() :復制字符串。
//函數原型
int strcmp(const char * arr1, const char * arr2);
//使用舉例:
char arr1[] = "dfetf";
char arr2[] = "cfefef";
int b = strcmp(arr1, arr2);
輸出b的值是大于0 的。
總結
指針強大而又危險,卻也是C和C++的靈魂,由于其可以直接操作內存地址的特殊性,使得理解指針是如何工作的在C和C++中必不可少,若想成為一名優秀的C++工作者,必須要掌握指針。
原文鏈接:https://blog.csdn.net/m0_63011787/article/details/126445392
相關推薦
- 2022-07-30 @Autowired實現的原理
- 2022-07-31 深入了解Go的interface{}底層原理實現_Golang
- 2022-08-28 python?數據保存為npy和npz格式并讀取的完整代碼_python
- 2022-03-29 關于Android冷啟動耗時優化詳解_Android
- 2022-09-26 數據庫基本增刪改查語法和多表鏈接查詢的方式
- 2022-07-17 用Android?studio實現簡易計算器功能_Android
- 2022-10-18 golang中使用匿名結構體的方法_Golang
- 2022-08-22 探究C#訪問null字段會拋異常原因_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支