国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

網(wǎng)站建設(shè) 在線購買企業(yè)網(wǎng)絡(luò)營銷方案

網(wǎng)站建設(shè) 在線購買,企業(yè)網(wǎng)絡(luò)營銷方案,平穩(wěn)有序推進網(wǎng)站建設(shè),寵物店做網(wǎng)站的論文文章目錄 1.指針的定義2.指針的加減運算3.野指針4.指針 & 數(shù)組 & 傳參 & 字符數(shù)組5.數(shù)組指針 & 指針數(shù)組6.二級指針7.指針函數(shù) & 函數(shù)指針 & 回調(diào)函數(shù)8.函數(shù)指針數(shù)組 & 指向函數(shù)指針數(shù)組的指針 1.指針的定義 指針是內(nèi)存中一個最小單元的編號&…

文章目錄

        • 1.指針的定義
        • 2.指針的加減運算
        • 3.野指針
        • 4.指針 & 數(shù)組 & 傳參 & 字符數(shù)組
        • 5.數(shù)組指針 & 指針數(shù)組
        • 6.二級指針
        • 7.指針函數(shù) & 函數(shù)指針 & 回調(diào)函數(shù)
        • 8.函數(shù)指針數(shù)組 & 指向函數(shù)指針數(shù)組的指針

1.指針的定義

指針是內(nèi)存中一個最小單元的編號,也就是地址。平常口語中所說的指針,通常指的是指針變量,是用來存放內(nèi)存地址的變量。指針的大小在32位平臺是4個字節(jié),在64位平臺是8個字節(jié)??梢酝ㄟ^sizeof求得指針的大小。

#include <stdio.h>int main()
{int a = 5;char c = 'a';int* ptra = &a;char* ptrc = &c; printf("%ld\n",sizeof(ptra));printf("%ld\n",sizeof(ptrc));return 0;
}

我的系統(tǒng)64位的Ubuntu系統(tǒng)所以輸出8。從上面的代碼可以看出用 type + * 定義一個指針變量,而&(取地址) 可以取得變量的地址。另外輸出ptraptrc的結(jié)果都一樣,難道它們的類型一樣。不是的!ptra的類型是int*用來接收int類型變量的地址,而ptrc的類型是char*用來接收char類型變量的地址。對指針賦值的時候類型要匹配!
可以定義指針,那么如何取出指針的指向的內(nèi)容呢,其實它和定義指針用的是同一個操作符 * (解引用操作符或間接訪問操作符)指針的類型決定了,對指針解引用的時候有多大的權(quán)限(能操作幾個字節(jié)),char* 指針的解引用就只能訪問1個字節(jié),int*指針的解引用就只能訪問4個字節(jié)。

#include <stdio.h>int main()
{int a = 5;int* ptra = &a;printf("%d\n",*ptra);return 0;
}
2.指針的加減運算

C語言程序運行指針的整數(shù)加減,但是不能乘除!其實加減有時候就夠頭疼了,要是能乘除那不得了了!指針的加減會根據(jù)指針的類型決定了指針向前或者向后走一步有多大(距離),這時候能直觀體現(xiàn)指針是有類型的。

#include <stdio.h>int main()
{int a = 5;char c = 'a';int* ptra = &a;char* ptrc = &c; printf("prta : before = %p : after = %p\n",ptra,ptra + 1);printf("prtc : before = %p : after = %p\n",ptrc,ptrc + 1);return 0;
}

輸出結(jié)果:輸出的結(jié)果是按照16進制來展示的。

prta : before = 0x7ffca65b0c74 : after = 0x7ffca65b0c78
prtc : before = 0x7ffca65b0c73 : after = 0x7ffca65b0c74
3.野指針

指針是很靈活的,但是有時候靈活就代表著容易出錯,如果對指針操作不當(dāng)那么很容易造成野指針。所謂的野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的)。
1、比如我對int* 類型的指針加一然后解引用訪問,就會出錯!指針越界訪問!(int 只有4個字節(jié)大小的空間屬于它) 通常越界訪問是對數(shù)組經(jīng)行操作,所以要注意數(shù)組下標(biāo)訪問是否越界。

int main()
{int a = 5;int* ptra = &a;printf("*prta : before = %d : after = %d\n",*ptra,*(ptra + 1));return 0;
}

輸出:有些環(huán)境就程序直接就崩潰了,這里就輸出了隨機值。

*prta : before = 5 : after = 1747592652

2、指針未初始化

int main()
{int* ptra;printf("*prta : before = %d : after = %d\n",*ptra,*(ptra + 1));return 0;
}

3、指針指向的空間釋放。對malloc動態(tài)申請的內(nèi)存free后再去訪問,也會造成野指針。

如何避免野指針的出現(xiàn): 1.指針初始化、2.小心指針越界、3.指針指向空間釋放即使置NULL、4.避免返回局部變量的地址、5.指針使用之前檢查有效性。
上面的方法都是依靠程序員有良好的編碼習(xí)慣,而人有時候會犯錯,所以在C++中引入了智能指針來避免野指針。

4.指針 & 數(shù)組 & 傳參 & 字符數(shù)組

數(shù)組名首元素的地址,它和&數(shù)組名輸出的結(jié)果是一樣但是它們代表并不是一個意思,因為指針是有類型的。

int main(int argc,char* argv[])
{int arr[5] = {1,2,3,4,5};printf("%p\n",arr);printf("%p\n",&arr);return 0;
}

輸入結(jié)果

0x7ffebf362450
0x7ffebf362450

如果對數(shù)組名 + 1和&數(shù)組名 + 1會發(fā)現(xiàn)它們輸出的內(nèi)容是不一樣的。

int main(int argc,char* argv[])
{int arr[5] = {1,2,3,4,5};printf("%p\n",arr);printf("%p\n",&arr);printf("%p\n",arr + 1);printf("%p\n",&arr + 1);return 0;
}

輸出結(jié)果:

0x7ffd97b8f650
0x7ffd97b8f650
0x7ffd97b8f654
0x7ffd97b8f664

指針的類型決定了指針向前或者向后走一步有多大(距離)。數(shù)組名 + 1會跳過4個字節(jié)因為它的類型是 int*,而&數(shù)組名 + 1的類型是int (*)[5] 整形數(shù)組指針,跳過了20個字節(jié)。如果嘗試這樣賦值 int * p = &arr,編譯器會提醒你!

test.c:374:15: warning: initialization of ‘int *’ from incompatible pointer type ‘int (*)[5][-Wincompatible-pointer-types]374 |     int * p = &arr;

如果自動一個函數(shù)想要接收一個整形數(shù)組,不需要定義數(shù)組指針,只需要傳遞首元素的地址就能訪問到整個數(shù)組的元素??梢杂腥N方式。
方式一:

void get(int* arr){}

方式二:這種方式需要和傳入的數(shù)組的大小匹配,比較的麻煩,現(xiàn)實中定義函數(shù)的人也知道要定義多大。如果可以在函數(shù)中打印一下arr,發(fā)現(xiàn)就是一個指針的大小,而不是將整個數(shù)組傳遞。

void get(int arr[5])
{printf("%ld\n",sizeof(arr));
}

方式三:直接用方式一就好了。

void get(int arr[]){}

C語言沒有像其他語言一樣提供好用的String類型來處理字符串,它是通過char *指向 ""引起的字符串常量,或者通過字符數(shù)組(棧),再或者存放到malloc開辟的動態(tài)內(nèi)存空間中。不管怎么說以\0標(biāo)志結(jié)尾的C風(fēng)格的字符串挺難用的。
那么char * (字符指針)和字符數(shù)組處理字符有什么區(qū)別呢:

int main()
{const char* str1 = "hello world";const char* str2 = "hello world";char str3[] = "hello world";char str4[] = "hello world";printf("str1 = %p : str2 = %p\n",str1,str2);printf("str3 = %p : str4 = %p\n",str3,str4);return 0;
}

輸出結(jié)果

str1 = 0x55a2615af008 : str2 = 0x55a2615af008
str3 = 0x7fff95f06ca0 : str4 = 0x7fff95f06cac

str1 和 str2 指向了存放在常量區(qū)中的同一個"hello world"字符串的首元素的地址,而str3 和str4在棧區(qū)開辟了兩塊不同的內(nèi)存存放Hello world。這樣做雖然浪費了,一定的內(nèi)存,但是字符數(shù)組中存放的內(nèi)容可以修改,而char* 指向的字符串常量不能被修改,通常會加上一個const來修飾。另外,從鍵盤上輸入字符就需要用到字符數(shù)組來接收。

int main()
{char str1[] = {'h','e','l','l','o'};char str2[] = {'h','e','l','l','o','\0'};char str3[] = "hello";printf("%ld\n",sizeof(str1));printf("%ld\n",sizeof(str2));printf("%ld\n",sizeof(str3));printf("%zd\n",strlen(str1));printf("%zd\n",strlen(str2));return 0
}

這里str1 定義并不是字符串,因為C字符串要以\0結(jié)尾,所以strlen輸出什么都是未定義的。這里注意區(qū)分sizeof 是計算內(nèi)存的大小,而strlen是計算字符串的長度。

5.數(shù)組指針 & 指針數(shù)組

數(shù)組指針是指針,而指針數(shù)組是數(shù)組用來存放指針。它們的定義非常的相似。
數(shù)組指針: int (*p1)[5],p1是一個指針變量,指向的是一個大小為5個整型的數(shù)組。
指針數(shù)組: int *p2[5],p2是數(shù)組名,可以存放著5個int * 類型的指針。
這里定義數(shù)組指針需要括號,因為 [] 的優(yōu)先級要高于 *號的,加上()來保證p1先和*結(jié)合。使用指針數(shù)組可以接收&一維數(shù)組名。也可以接收二維數(shù)組中的元素,注意二維數(shù)組中的元素是一維數(shù)組的地址。

void show(int(*p)[5],int cow,int col)
{for (int i = 0;i < cow ; i++) {for (int j = 0; j < col; j++) {printf("%d ",p[i][j]);}printf("\n");}
}int main()
{int a[2][5] = {{1,2,3,4,5},{6,7,8,9,10}};show(a,2,5);return 0;
}

上面show函數(shù)打印了二維數(shù)組的值,數(shù)組通過 [] 訪問更加清晰一些。在C++中通過重載 [] 也讓容器在邏輯上順序訪問,哪怕它的底層空間不是連續(xù)的。這里的[] 和 * 是等價的。也可以用下面的方式,訪問到二維數(shù)組的元素。

printf("%d ",*(*p + i) + j);
printf("%d ",*((*(p + i)) + j));

再來看一看,指針數(shù)組。來定義一個指針數(shù)組,并打印這些值。

int main(int argc,char* argv[])
{char* names[5] = {"張三","李四","王五","趙六","田七"};for(int i = 0;i < 5;i++){printf("%s\n",names[i]);}return 0;
}

上面的例子似乎沒有什么挑戰(zhàn),那如果從鍵盤上輸入五個人的名字,并輸出呢,怎么做。就有一個關(guān)鍵點,指針數(shù)組中存放的是指針,如果從鍵盤上輸入要用字符數(shù)組,然后賦值給指針。

int main(int argc,char* argv[])
{char* names[5]; for(int i = 0; i < 5;i++){char name[25] = {0};printf("請輸入一個名字:");scanf("%s",name);names[i] = name;}for(int i = 0;i < 5;i++){printf("%s\n",names[i]);}return 0;
}

我們來使用指針數(shù)組完成一個有意思的東西,學(xué)過Linux的同學(xué)知道cat 命令是查看文本中的內(nèi)容。我們自己編寫一個程序也實現(xiàn)cat 命令完成的任務(wù)。沒學(xué)過Linux也沒有關(guān)系,知道了怎么編寫命令,學(xué)的時候就不會被它唬住。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>int main(int argc,const char* argv[])
{if(argc <= 1){printf("Usage: %s <file1> [file2 ...]\n",argv[0]);return -1;}for(int i = 1;i < argc;i++){FILE* file = fopen(argv[i],"r");if(file == NULL){perror("文件不存在!\n");return -1;}char ch;while((ch = fgetc(file)) != EOF){fputc(ch,stdout);}fclose(file);}return 0;
}

通過gcc編譯并添加到環(huán)境變量中

?  day2024_11_14 [master] ?  gcc test.c[自己的文件名] -o easy_cat
?  day2024_11_14 [master] ?  sudo mv easy_cat /usr/bin
?  day2024_11_14 [master] ?  easy_cat test.c
6.二級指針

指針變量也是變量,存放指針變量的指針稱為二級指針。

int main()
{int num = 5;int *p = &num;int **pp = &p;printf("  pp : %p &p : %p\n",  pp,&p);printf("**pp : %d *p : %d\n",**pp,*p);return 0;
}

輸出結(jié)果

  pp : 0x7fff341ddbc8 &p : 0x7fff341ddbc8
**pp : 5 *p : 5

那么二級指針的用處是什么呢。函數(shù)的傳參的方式有傳值和傳地址,著兩種方式,傳值傳遞參數(shù)是指的拷貝,如果想改變兩個變量的地址,那么就需要通過指針傳遞。同樣的道理,我想改變指針變量的內(nèi)容就需要指針變量的地址,那就是二級指針。
交換兩個int 變量的值:

void swap(int* a,int* b)
{int tmp = *a;*a = *b;*b = tmp;
}int main()
{int a = 5;int b = 10;printf("before a = %d,b= %d\n",a,b);swap(&a,&b);printf("after  a = %d,b= %d\n",a,b);
}

交換兩個int* 指針變量指向的值:

void swap(char** a,char** b)
{char* tmp = *a;*a = *b;*b = tmp;
}

當(dāng)你想改變指針變量的指向的時候可以考慮使用二級指針,很多的數(shù)據(jù)結(jié)構(gòu)中,二級指針都是很常見的。另外在鞏固一下,如果要編寫一個函數(shù)打印指針數(shù)組的值改怎么做呢。

void showName(char** names,int size)
{for(int i = 0;i < size;i++){printf("%s ",names[i]);}printf("\n");
}int main()
{char* names[5] = {"張三","李四","王五","趙六","田七"};showName(names,5);return 0;
}

為什么需要二級指針接收呢,可以這么理解:names[0] 類型為char*&names[0]的類型為char**。數(shù)組名就是首元素的地址,name = &names[0]實參傳遞的是 char**類型,showName的形參類型也要用二級指針來接收。

7.指針函數(shù) & 函數(shù)指針 & 回調(diào)函數(shù)

函數(shù)返回值類型是指針的函數(shù)稱為指針函數(shù),通常是返回堆空間的指針,避免返回棧上空間,出了作用域被銷毀,訪問一塊銷毀的空間。

void* malloc(int size);
char* strcpy(char* dest,char* src);

函數(shù)指針的本質(zhì)是指針,用來存放函數(shù)的地址,而函數(shù)名就是函數(shù)在內(nèi)存中首元素的地址。如何判斷函數(shù)的類型。如定義一個add函數(shù):

int add(int a,int b)

確認一個函數(shù)類型最快速的方法是,去掉函數(shù)名和函數(shù)的形參就是函數(shù)的類型。add 函數(shù)的類型是int (int,int)。然后添加一個 (* p)就可以定義一個函數(shù)指針了。

#include <stdio.h>int add(int a,int b)
{return a + b;
}int main()
{int (*p1)(int,int) = add;int (*p2)(int,int);p2 = add;printf("%d\n",p1(1,2));printf("%d\n",p2(1,3));return 0;
}

定義的函數(shù)指針可以在定義的時候賦初值,也可以先聲明在賦值。使用函數(shù)指針像函數(shù)一樣使用即可。既然像函數(shù)一樣使用,那干嘛要麻煩的定義函數(shù)指針呢?定義函數(shù)指針可以讓函數(shù)以參數(shù)的方式傳遞,讓聲明和實現(xiàn)分離,提高了代碼的模塊化程度。 這就像其他如Java語言中定義接口,通過接口來調(diào)用實現(xiàn)接口的類。
下面實現(xiàn)了加減,如果發(fā)生乘除取模(整除)等添加另外的功能函數(shù)的類型一樣都可以通過calculate去調(diào)用。

#include <stdio.h>int add(int a,int b)
{return a + b;
}int sub(int a,int b)
{return a - b;
}int calculate(int cal(int,int),int a,int b)
{return cal(a,b);
}int main()
{printf("%d\n",calculate(add,5,10));printf("%d\n",calculate(sub,10,5));return 0;
}

回調(diào)函數(shù)callback,通常我們調(diào)用別人的函數(shù)API這個過程叫做call,而別人在他定義的函數(shù)中調(diào)用我們的函數(shù)這個行為就是callback。上面的例子中calculate 調(diào)用cal 這個動作就是callback。在C語言中qsort 就用到了callback 。

#include <stdio.h>
#include <stdlib.h>int int_cmp(const void* p1,const void* p2)
{return *(int*)p1 - *(int*)p2;
}int main()
{int arr[] = {1,6,2,3,8,9,3};int size = sizeof(arr) / sizeof(arr[0]);qsort(arr,size,sizeof(int),int_cmp);for(int i = 0;i < size;i++){printf("%d ",arr[i]);}printf("\n");return 0;
}

那qsort大概是怎么實現(xiàn)的呢?使用一個冒泡排序,實現(xiàn)對任意類型數(shù)據(jù)的排序。
C語言中使用void* 來接收任意類型,但是void* 不能加減運算,但是類型又是不確定的,所以只能轉(zhuǎn)成最小類型的指針char* ,按照一個字節(jié)來操作數(shù)據(jù)。比較的時候,只需要兩個確定元素的地址,而char* 一次只能跳過一個字節(jié),所以要根據(jù)size來確定跳幾個字節(jié)。交換的時候是按照一個一個字節(jié),交換內(nèi)容,要交換size次。

void _swap(void* p1,void* p2,int size)
{for(int i = 0;i < size;i++){char ch = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = ch;}
}void my_qsort (void* base, size_t num, size_t size,int (*compar)(const void*,const void*))
{for(int i = 0; i < num - 1;i++){for(int j= 0;j < num -1 -i;j++){if(compar((char*)base + j * size,(char*)base + (j + 1) * size) > 0){				_swap((char*)base + (j* size),((char*)base + (j+ 1)* size),size);}}}
}int int_cmp(const void* p1,const void* p2)
{return *(int*)p1 - *(int*)p2;
}int main()
{int arr[] = {5,1,2,3,8,9,3};int size = sizeof(arr) / sizeof(arr[0]);my_qsort(arr,size,sizeof(int),int_cmp);for(int i = 0;i < size;i++){printf("%d ",arr[i]);}printf("\n");return 0;
}
8.函數(shù)指針數(shù)組 & 指向函數(shù)指針數(shù)組的指針

用來存放函數(shù)指針的數(shù)組。在上面的calculate例子中,可以使用函數(shù)指針數(shù)組來存放函數(shù)指針。

int add(int a,int b)
{return a + b;
}int sub(int a,int b)
{return a - b;
}int main()
{int (*p[2])(int,int) = {add,sub};for(int i = 0;i < 2;i++){printf("%d\n",p[i](10,5));}
}

當(dāng)然還可以定義一個指向函數(shù)指針數(shù)組的指針

int (*(*pp)[2])(int,int) = &p;
http://aloenet.com.cn/news/29402.html

相關(guān)文章:

  • 我想注冊一個做門窗的網(wǎng)站應(yīng)該怎樣做網(wǎng)站如何推廣運營
  • 個人設(shè)計師網(wǎng)站如何做好網(wǎng)絡(luò)推廣
  • 花生殼網(wǎng)站無法登陸提高工作效率
  • Asp做網(wǎng)站前期準備互聯(lián)網(wǎng)營銷師有什么用
  • 自己做網(wǎng)站可以掙錢嗎windows優(yōu)化大師怎么使用
  • 給客戶做網(wǎng)站需要付法律責(zé)任嗎長春網(wǎng)站seo
  • 草坪網(wǎng)站怎么做幫忙推廣的平臺
  • 單頁面網(wǎng)站怎么做的網(wǎng)站流量數(shù)據(jù)
  • 租服務(wù)器做網(wǎng)站app推廣代理加盟
  • 裝飾網(wǎng)站建設(shè)策劃書中山網(wǎng)站建設(shè)
  • 百度制作公司網(wǎng)頁常州百度搜索優(yōu)化
  • 如何做外賣網(wǎng)站app東莞百度搜索網(wǎng)站排名
  • 自己如何免費做網(wǎng)站重慶營銷型網(wǎng)站建設(shè)公司
  • wordpress網(wǎng)站發(fā)布時間網(wǎng)絡(luò)推廣求職招聘交流群
  • 電子商務(wù)網(wǎng)站建設(shè)的過程和步驟廣告聯(lián)盟廣告點擊一次多少錢
  • 微信做單子的網(wǎng)站源碼搜什么關(guān)鍵詞能找到網(wǎng)站
  • 消防做設(shè)計有什么網(wǎng)站無錫網(wǎng)站建設(shè)優(yōu)化公司
  • 做美女網(wǎng)站賺錢千峰培訓(xùn)可靠嗎?
  • 媒體公關(guān)廈門谷歌seo
  • 網(wǎng)絡(luò)公司開發(fā)網(wǎng)站今日nba數(shù)據(jù)帝
  • 免費企業(yè)建站開源系統(tǒng)企業(yè)培訓(xùn)師資格證
  • html5網(wǎng)站編寫青島網(wǎng)站制作推廣
  • 網(wǎng)站可以做無形資產(chǎn)嗎優(yōu)化網(wǎng)站排名需要多少錢
  • 云表無代碼開發(fā)平臺baike seotl
  • 網(wǎng)站備案值得嗎打廣告的免費軟件
  • 專業(yè)做化妝品的網(wǎng)站在線生成html網(wǎng)頁
  • 怎么做淘寶客網(wǎng)站做淘客拼多多代運營一般多少錢
  • 佰匯康網(wǎng)站建設(shè)河南seo快速排名
  • 南京網(wǎng)站建設(shè)案例蘇州做網(wǎng)站哪家比較好
  • 嘉興網(wǎng)站制作軟件如何做一個網(wǎng)站