指針運算
指針的基本運算有三種,分別是:
? 指針±整數(shù)
? 指針-指針
? 指針的關系運算
指針±整數(shù)
因為數(shù)組在內(nèi)存中是連續(xù)存放的,比如int類型的數(shù)組,每個元素相差4個字節(jié),因此我們只需要知道首元素的地址就可以通過加減的方式找到后面元素的地址。
int arr[10] = {1,2,3,4,5,6,7,8,9,10}
#include <stdio.h>
//指針+- 整數(shù)
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i<sz; i++)
{
printf("%d ", *(p+i));//p+i 這?就是指針+整數(shù) i每增加1就往后移動4個字節(jié)
}
return 0;
}
指針-指針
/指針-指針
#include <stdio.h>
int my_strlen(char *s)
{
char *p = s;
while(*p != '\0' )//遇到\0就代表字符串結束
p++;
return p-s;//同過著兩個指針相減我們可以得到這個指針的總長度
}
int main()
{
printf("%d\n", my_strlen("abc"));
return 0;
}
指針的關系運算
//指針的關系運算
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];//取數(shù)組首元素地址
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
while(p<arr+sz) //指針的???較
{
printf("%d ", *p);
p++;//p沒加1就增加4個字節(jié)
}
return 0;
}
野指針
概念:野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的
野指針成因
指針未初始化
#include <stdio.h>
int main()
{
int *p;//局部變量指針未初始化,默認為隨機值
*p = 20;
return 0;
}
指針越界訪問
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)//i=10和i=11時越界了
{
//當指針指向的范圍超出數(shù)組arr的范圍時,p就是野指針
*(p++) = i;
}
return 0;
}
指針指向的空間釋放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;//不是全局變量,在函數(shù)結束后地址就會消失
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
如何規(guī)避野指針
指針初始化
如果明確知道指針指向哪里就直接賦值地址,如果不知道指針應該指向哪里,可以給指針賦值NULL(空指針,也可以理解為0,但是不完全是0,因為0有整形和char類型,只是有那個意思)
NULL 是C語言中定義的?個標識符常量,值是0,0也是地址,這個地址是無法使用的,讀寫該地址會報錯
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endi
初始化如下
#include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
注意指針越界
?個程序向內(nèi)存申請了哪些空間,通過指針也就只能訪問哪些空間,不能超出范圍訪問,超出了就是越界訪問
指針不使用時就用NULL
當指針變量指向?塊區(qū)域的時候,我們可以通過指針訪問該區(qū)域,后期不再使用這個指針訪問空間的時候,我們可以把該指針置為NULL。因為約定俗成的一個規(guī)則就是:只要是NULL指針就不去訪問
因此使用指針之前可以判斷指針是否為NULL。
這里就是用if語句判斷
int main()
{
int arr[10] = {1,2,3,4,5,67,7,8,9,10};
int *p = &arr[0];
for(i=0; i<10; i++)
{
*(p++) = i;
}
//此時p已經(jīng)越界了,可以把p置為NULL
p = NULL;
//下次使?的時候,判斷p不為NULL的時候再使?
//...
p = &arr[0];//重新讓p獲得地址
if(p != NULL) //判斷
{
//...
}
return 0;
}
避免返回局部變量的地址
我們就需要創(chuàng)建的變量不是局部變量,也就是說我們可以創(chuàng)建全局變量,當然你可以在mian函數(shù)里面創(chuàng)建變量,然后將變量的地址傳入函數(shù)中,再通過函數(shù)進行一系列操作,結束時可以將變量的地址傳出,這樣就可以避免返回局部變量了。
assert斷言
assert.h 頭文件定義了宏 assert() ,用于在運行時確保程序符合指定條件,如果不符合,就報錯終止運行。這個宏常常被稱為“斷言”
可以理解為進行了一次安檢,在通過時會對這個變量進行檢測,判斷是否符合條件
assert(p != NULL);
上面代碼在程序運行到這?行語句時,驗證變量 p 是否等于 NULL 。
如果確實不等于 NULL ,程序繼續(xù)運行,否則就會終止運行,并且給出報錯信息提示。
assert() 宏接受?個表達式作為參數(shù)。如果該表達式為真(返回值非零), assert() 不會產(chǎn)生任何作用,程序繼續(xù)運行。
如果該表達式為假(返回值為零),assert() 就會報錯,在標準錯誤流 stderr 中寫入?條錯誤信息,顯示沒有通過的表達式,以及包含這個表達式的文件名和行號。
assert() 的使用對程序員是非常友好的,使用 assert() 有幾個好處:它不僅能自動標識文件和出問題的行號,還有?種無需更改代碼就能開啟或關閉 assert() 的機制。
如果已經(jīng)確認程序沒有問題,不需要再做斷言,就在 #include <assert.h> 語句的前面,定義?個NDEBUG
#define NDEBUG
#include <assert.h>
然后,重新編譯程序,編譯器就會禁用文件中所有的 assert() 語句。如果程序又出現(xiàn)問題,可以移除這條 #define NDBUG 指令(或者把它注釋掉),再次編譯,這樣就重新啟用了 assert() 語句。
assert() 的缺點是,因為引入了額外的檢查,增加了程序的運行時間。
?般我們可以在debug中使用,在release版本中選擇禁用assert就行,在VS這樣的集成開發(fā)環(huán)境中,在release版本中,直接就是優(yōu)化掉了。這樣在debug版本寫有利于程序員排查問題,在release版本不影響用戶使用時程序的效率
指針的使用和傳址調(diào)用
傳址調(diào)用
學習指針的目的是使用指針解決問題,那什么問題,非指針不可呢?
例如:寫?個函數(shù),交換兩個整型變量的值文章來源:http://www.zghlxwxcb.cn/news/detail-693013.html
#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交換前:a=%d b=%d\n", a, b);
Swap1(a, b);
printf("交換后:a=%d b=%d\n", a, b);
return 0;
}
代碼運行入下:
在main函數(shù)內(nèi)部,創(chuàng)建了a和b,a的地址是在調(diào)用Swap1函數(shù)時,將a和b傳遞給了Swap1函數(shù),在Swap1函數(shù)內(nèi)部創(chuàng)建了形參x和y接收a和b的值,但是
x和y確實接收到了a和b的值,不過x的地址和a的地址不一樣,y的地址和b的地址不一樣,相當于x和y是獨立的空間,那么在Swap1函數(shù)內(nèi)部交換x和y的值,自然不會影響a和b,當Swap1函數(shù)調(diào)用結束后回到main數(shù),a和b的沒法交換。Swap1函數(shù)在使用的時候,是把變量本?直接傳遞給了函數(shù),這種調(diào)用函數(shù)的方式我們之前在函數(shù)的時候就知道了,這種叫傳值調(diào)用。因此當我們傳入內(nèi)存后,運行結果如下:
我們可以看到實現(xiàn)成Swap2的方式,順利完成了任務,這里調(diào)用Swap2函數(shù)的時候是將變量的地址傳
遞給了函數(shù),這種函數(shù)調(diào)用方式叫:傳址調(diào)用
結論:實參傳遞給形參的時候,形參會單獨創(chuàng)建?份臨時空間來接收實參,對形參的修改不影響實參。所以Swap是失敗的了文章來源地址http://www.zghlxwxcb.cn/news/detail-693013.html
例子(strlen函數(shù)的實現(xiàn))
//計數(shù)器?式
int my_strlen(const char * str)
{
int count = 0;
assert(str);
while(*str)
{
count++;
str++;
}
return count;
}
int main()
{
int len = my_strlen("abcdef");
printf("%d\n", len);
return 0;
}
到了這里,關于C語言深入理解指針(非常詳細)(二)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!