摘要:故使用無(wú)具體類型,又稱通用類型,即可以接收任意類型的指針,但是無(wú)法進(jìn)行指針運(yùn)算解引用,整數(shù)等。求指針?biāo)甲止?jié)而不是解引用訪問(wèn)權(quán)限大小。數(shù)組就是整個(gè)數(shù)組的大小,數(shù)組元素則是數(shù)組元素的大小,指針大小都為。
續(xù)前文《C語(yǔ)言進(jìn)階:指針進(jìn)階》
回調(diào)函數(shù):通過(guò)函數(shù)指針調(diào)用的函數(shù),或者說(shuō)使用函數(shù)指針調(diào)用函數(shù)這樣的機(jī)制被稱為回調(diào)函數(shù)。回調(diào)函數(shù)不由實(shí)現(xiàn)方直接調(diào)用,而是作為特殊條件下的響應(yīng)。
概念無(wú)關(guān)緊要,理解并熟練運(yùn)用這種方法才更為重要。
qsort
qsort
函數(shù)邏輯void qsort(void* base, size_t num, size_t width, int (*cmp)(const void* e1, const void* e2));
qsort
無(wú)返回值,有四個(gè)參數(shù)。分別為base
:起始地址,num
:元素個(gè)數(shù),width
:元素大小以及compare
:比較函數(shù)。可與冒泡排序作對(duì)比。
//冒泡排序void Bubble_sort(int arr[], int sz) { for (int i = 0; i < sz - 1; i++) { for (int j = 0; j < sz - 1 - i; j++) { //比較函數(shù) if (arr[j] > arr[j + 1]) { int tmp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = tmp; } } }}
與冒泡排序作對(duì)比發(fā)現(xiàn),冒泡排序僅需起始地址和元素個(gè)數(shù)即可,暗含了其他信息。由于過(guò)度具體化,冒泡排序只能排序整型數(shù)組,且比較函數(shù)過(guò)于簡(jiǎn)單無(wú)需多帶帶列出。
因?yàn)?code>qsort排序可適用于多種類型如浮點(diǎn)型,字符型,自定義類型的數(shù)據(jù),故無(wú)法規(guī)定具體類型,所以需要多個(gè)參數(shù)去描述元素的基本信息。
qsort
之所以能夠適應(yīng)多種數(shù)據(jù),是因?yàn)閰?shù)void* base
再搭配上num
和width
就描述出任意一種類型。
為什么將參數(shù)
base
的類型定義為void*
呢?如下述代碼所示。
char* p1 = &a;//從int*到char*類型不兼容char* p2 = &f;//從float*到char*類型不兼容void* p1 = &a;void* p2 = &f;
確定類型的地址之間直接賦值會(huì)提示類型不兼容,強(qiáng)制轉(zhuǎn)化也可能會(huì)導(dǎo)致精度丟失。
故使用無(wú)(具體)類型void*
,又稱通用類型,即可以接收任意類型的指針,但是無(wú)法進(jìn)行指針運(yùn)算(解引用, ± ± ±整數(shù)等)。
p1++; *p1; p1 - p2; p1 > p2;//表達(dá)式必須是指向完整對(duì)象類型的指針
base
:用于存入數(shù)據(jù)的起始地址。類型定義為void*
,可接受任意類型的指針。
num
:待排序的元素個(gè)數(shù)。
width
:元素寬度,所占字節(jié)大小。
明確了排序的起始位置,元素個(gè)數(shù)和元素大小,貌似已經(jīng)夠了。但是并無(wú)法排序所有類型,因此必須自定義一個(gè)抽象的比較函數(shù)指定元素的比較方式。
cmp
:比較函數(shù),用于指定元素的比較方式。
elem1
小于elem2
,返回值小于0elem1
大于elem2
,返回值大于0elem1
等于elem2
,返回值為0elem1
,elem2
:進(jìn)行比較的兩個(gè)元素的地址作參數(shù)。
qsort
可以說(shuō)是一個(gè)半庫(kù)函數(shù)半自定義函數(shù)。自定義在于其函數(shù)最后一個(gè)參數(shù)為比較函數(shù),該函數(shù)內(nèi)部實(shí)現(xiàn)自由,但返回值必須按照規(guī)定返回相應(yīng)的數(shù)值。
需要qsort
函數(shù)排序各種類型的數(shù)據(jù),
base
起始地址不可為固定的指針類型,只能用void*
。qsort
實(shí)現(xiàn)冒泡排序//比較函數(shù):整型#include int int_cmp(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2;}int main() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), int_cmp); return 0;}
比較函數(shù)int_com
不需要傳參,作為回調(diào)函數(shù)由qsort
直接調(diào)用。比較函數(shù)的傳參過(guò)程由qsort
內(nèi)部實(shí)現(xiàn)。
qsort
實(shí)現(xiàn)結(jié)構(gòu)體排序#include struct stu { char* name; short age; float score;};//按照成績(jī)排序int score_cmp(const void* e1, const void* e2) { //1.升序 return ((struct stu*)e1)->score - ((struct stu*)e2)->score; //2.降序 return ((struct stu*)e2)->score - ((struct stu*)e1)->score;}//按照名字排序int name_cmp(const void* e1,const void* e2) { return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);}int main() { struct stu s[3] = { { "張三", 22, 99.5f },{ "李四", 21, 66.4f },{ "王五", 18, 80.1f } }; int sz = sizeof(s) / sizeof(s[0]); //1. qsort(s, sz, sizeof(s[0]), name_cmp); //2. qsort(s, sz, sizeof(s[0]), score_cmp); return 0;}
由此可得,提取出一個(gè)比較函數(shù),具體交換的方式由qsort
內(nèi)部實(shí)現(xiàn)。
qsort
用
qsort
的函數(shù)邏輯,實(shí)現(xiàn)冒泡排序。
//打印函數(shù)void print_arr(int arr[],int sz) { for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); }}//交換函數(shù)void Swap(char* buf1, char* buf2, size_t width) { for (size_t i = 0; i < width; i++) {//寬度次 char tmp = *buf1; *buf1 = *buf2; *buf2 = tmp; buf1++; buf2++; }}//比較函數(shù)int cmp(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2;}//排序函數(shù)void my_bubble_sort(void* base, size_t num, size_t width, int(*cmp)(const void* e1, const void* e2)) { for (size_t i = 0; i < num - 1; i++) { for (size_t j = 0; j < num - 1 - i; j++) { if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0) {//以字節(jié)為單位 Swap((char*)base + j * width, (char*)base + (j + 1) * width, width); } } }}int main() { int arr[10] = { 9,8,7,6,5,4,3,2,1,0 }; int sz = sizeof(arr) / sizeof(arr[0]); my_bubble_sort(arr, sz, sizeof(arr[0]), cmp); print_arr(arr, sz); return 0;}
地址統(tǒng)一強(qiáng)轉(zhuǎn)為char*
,以最小字節(jié)單位一個(gè)字節(jié)進(jìn)行比較和交換,使代碼更具有普適性。
如果需要排序結(jié)構(gòu)體則只需要在前文代碼中主函數(shù)里替換my_qsort
且把比較函數(shù)替換Name_cmp
即可。
//1.my_qsort(s, sz, sizeof(s[0]), name_cmp);//2.my_qsort(s, sz, sizeof(s[0]), score_cmp);
?
注意點(diǎn)。數(shù)組名代表整個(gè)數(shù)組:
sizeof(數(shù)組名)
&數(shù)組名
除此以外,數(shù)組名都是代表首元素地址。
int a[] = { 1,2,3,4 };printf("%d/n", sizeof(a));//16printf("%d/n", sizeof(a + 0));//4/8printf("%d/n", sizeof(*a));//4printf("%d/n", sizeof(a + 1));//4/8printf("%d/n", sizeof(a[1]));//4printf("%d/n", sizeof(&a));//4/8printf("%d/n", sizeof(*&a));//16printf("%d/n", sizeof(&a + 1));//4/8printf("%d/n", sizeof(&a[0]));//4/8printf("%d/n", sizeof(&a[0] + 1));//4/8
只有數(shù)組名多帶帶放在sizeof
內(nèi)部才是整個(gè)數(shù)組。
a+0
放在sizeof
內(nèi)部表示首元素地址+0。
只要是地址,不管是什么類型的地址大小都是4/8
基本類型指針,數(shù)組指針,函數(shù)指針大小都是4/8個(gè)字節(jié),故sizeof(&a)=sizeof(int(*)[4])=4
。sizeof()
求指針?biāo)甲止?jié)而不是解引用訪問(wèn)權(quán)限大小。
*
和&
在一起會(huì)抵消。
sizeof(*&a)
,&a為整個(gè)數(shù)組的地址類型int(*)[4]
,解引用后int[4]
大小為16。
char arr[] = { "a","b","c","d","e","f" };printf("%d/n", sizeof(arr));//6printf("%d/n", sizeof(arr + 0));//4/8printf("%d/n", sizeof(*arr));//1printf("%d/n", sizeof(arr[1]));//1printf("%d/n", sizeof(&arr));//4/8printf("%d/n", sizeof(&arr + 1));//4/8printf("%d/n", sizeof(&arr[0] + 1));//4/8printf("%d/n", strlen(arr));//隨機(jī)值xprintf("%d/n", strlen(arr + 0));//隨機(jī)值xprintf("%d/n", strlen(*arr));//報(bào)錯(cuò)printf("%d/n", strlen(arr[1]));//報(bào)錯(cuò)printf("%d/n", strlen(&arr));//隨機(jī)值xprintf("%d/n", strlen(&arr + 1));//隨機(jī)值x-6printf("%d/n", strlen(&arr[0] + 1));//隨機(jī)值x-1
sizeof(*arr)
,*arr
對(duì)首元素地址解引用,計(jì)算首元素所占空間大小。
strlen(*arr)
,*arr
依然是首元素,strlen
把a(bǔ)也就是97當(dāng)成地址,訪問(wèn)到非法內(nèi)存所以報(bào)錯(cuò)。
2.strlen(&arr)
雖然是整個(gè)數(shù)組的地址,但依然是從首元素開(kāi)始的,所以strlen
依然從第一個(gè)元素開(kāi)始找。
? strlen(&arr+1)
,先計(jì)算&arr+1
然后再傳參過(guò)去,也就是跳過(guò)了整個(gè)數(shù)組去找。
sizeof
和strlen
的區(qū)別
sizeof
— 操作符 — 以字節(jié)為單位,求變量或類型所創(chuàng)建變量的所占空間的大小
sizoef
不是函數(shù),計(jì)算類型是必須帶上類型說(shuō)明符()
。sizoef
內(nèi)容不參與運(yùn)算,在編譯期間便轉(zhuǎn)化完成。
strlen
— 庫(kù)函數(shù) — 求字符串長(zhǎng)度即字符個(gè)數(shù),遇/0
停止。庫(kù)函數(shù),計(jì)算字符串長(zhǎng)度沒(méi)有遇到
/0
就會(huì)一直持續(xù)下去。返回類型size_t
,參數(shù)char* str
,接收的內(nèi)容都會(huì)認(rèn)為是char*
類型的地址。
一個(gè)求變量所占空間,一個(gè)求字符串大小,二者本身是沒(méi)有關(guān)系的,但總有人把二者綁在一起“混淆視聽(tīng)”。
首先明確二者的區(qū)別:
//1.字符初始化數(shù)組char arr[] = { "a","b","c","d","e","f" };//[a] [b] [c] [d] [e] [f]//2.字符串初始化數(shù)組char arr[] = "abcdef";//[a] [b] [c] [d] [e] [f] [/0]
字符初始化數(shù)組,存了什么元素?cái)?shù)組里就是什么元素。而字符串初始化數(shù)組,除了字符串中可見(jiàn)的字符外,還有字符串末尾隱含的
/0
。/0
存在于字符串的末尾,是自帶的,雖不算字符串內(nèi)容,但是字符串中的字符。
char arr[] = "abcdef";printf("%d/n", sizeof(arr));//7printf("%d/n", sizeof(arr + 0));//4/8printf("%d/n", sizeof(*arr));//1printf("%d/n", sizeof(arr[1]));//1printf("%d/n", sizeof(&arr));//4/8printf("%d/n", sizeof(&arr + 1));//4/8printf("%d/n", sizeof(&arr[0] + 1));//4/8printf("%d/n", strlen(arr));//6printf("%d/n", strlen(arr + 0));//6printf("%d/n", strlen(*arr));//報(bào)錯(cuò)printf("%d/n", strlen(arr[1]));//報(bào)錯(cuò)printf("%d/n", strlen(&arr));//6printf("%d/n", strlen(&arr + 1));//隨機(jī)值printf("%d/n", strlen(&arr[0] + 1));//5
sizeof
計(jì)算變量的長(zhǎng)度,變量可以是數(shù)組,數(shù)組元素以及指針。數(shù)組就是整個(gè)數(shù)組的大小,數(shù)組元素則是數(shù)組元素的大小,指針大小都為4/8。strlen
把傳過(guò)來(lái)的參數(shù)都當(dāng)作地址,是地址就從該地址處向后遍歷找/0
,不是地址當(dāng)作地址非法訪問(wèn)就報(bào)錯(cuò)。char* p = "abcdef";
"abcdef"
是常量字符串,用一個(gè)字符指針p
指向該字符串,實(shí)質(zhì)是p
存入了首字符a
的地址。由于字符串在內(nèi)存中連續(xù)存放,依此特性便可以遍歷訪問(wèn)整個(gè)字符串。
char* p = "abcdef";
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/119679.html
摘要:本章節(jié)在此基礎(chǔ)上,對(duì)語(yǔ)言階段指針進(jìn)行更深層次的研究。數(shù)組指針的類型由數(shù)組類型決定,先找出數(shù)組的類型去掉名就是類型。相當(dāng)于數(shù)組指針?biāo)赶驍?shù)組的數(shù)組名。數(shù)組指針指向整個(gè)數(shù)組,將其看作二維數(shù)組并解引用得到一行的首元素,從而遍歷訪問(wèn)。 ...
摘要:函數(shù)的返回值為指針就按照字面意思,指針函數(shù)的定義顧名思義,指針函數(shù)即返回指針的函數(shù)。 目錄 前言指針與函數(shù)函數(shù)的返回值為指針作為函數(shù)參數(shù)的指針指針函數(shù)可以改變變量...
摘要:釋放不完全導(dǎo)致內(nèi)存泄漏。既然把柔性數(shù)組放在動(dòng)態(tài)內(nèi)存管理一章,可見(jiàn)二者有必然的聯(lián)系。包含柔性數(shù)組的結(jié)構(gòu)用進(jìn)行動(dòng)態(tài)內(nèi)存分配,且分配的內(nèi)存應(yīng)大于結(jié)構(gòu)大小,以滿足柔性數(shù)組的預(yù)期。使用含柔性數(shù)組的結(jié)構(gòu)體,需配合以等動(dòng)態(tài)內(nèi)存分配函數(shù)。 ...
摘要:三文讀透指針上篇本文將繼續(xù)介紹有關(guān)函數(shù)指針的相關(guān)內(nèi)容。在大型工程里,函數(shù)指針應(yīng)用還是挺普遍的。首先看閱讀下面兩段有趣的代碼出自語(yǔ)言陷阱與缺陷看看他們是什么意思代碼代碼函數(shù)指針數(shù)組函數(shù)指針數(shù)組,即存放函數(shù)指針的數(shù)組。 ...
閱讀 3077·2021-11-24 09:38
閱讀 1330·2021-09-22 15:27
閱讀 2967·2021-09-10 10:51
閱讀 1504·2021-09-09 09:33
閱讀 916·2021-08-09 13:47
閱讀 2069·2019-08-30 13:05
閱讀 891·2019-08-29 15:15
閱讀 2424·2019-08-29 12:21