數(shù)組與指針.png
一本道來其他系列
C語言關(guān)鍵字
C語言注釋符號(hào)一本道來
C語言編譯預(yù)處理技術(shù)一本道來
指針的基礎(chǔ)
注意本節(jié)內(nèi)容可能在gcc下不能完成編譯酬荞,請(qǐng)切換到Windows平臺(tái),使用
dev-cpp
或其他
- 指針本質(zhì)上也是一個(gè)變量
- 指針要占用一定的內(nèi)存空間(任何類型的指針的內(nèi)存大小是一樣的)
- 指針用于保存內(nèi)存地址的值
*號(hào)的意義
- 在指針聲明時(shí)锹安,*號(hào)表示所聲明的變量為指針
- *號(hào)表示取指針指向的內(nèi)存空間中的值
最佳實(shí)踐(指針占用的內(nèi)存空間)
#include <stdio.h>
int main()
{
int i;
int* pI;
char* pC;
float* pF;
pI = &i;
printf("%0X, %0X, %d\n", pI, &i, i);
printf("%d, %d, %0X\n", sizeof(int*), sizeof(pI), &pI);
printf("%d, %d, %0X\n", sizeof(char*), sizeof(pC), &pC);
printf("%d, %d, %0X\n", sizeof(float*), sizeof(pF), &pF);
return 0;
}
最佳示例(寫地址)需要在VS或其他windows系統(tǒng)下測試,
#include<stdio.h>
int main()
{
int i;
int * p;
p=&i;
*((int*)0x28FEB8)=100;
printf("%0X\n",p);
printf("%d\n",i);
return 0;
}
傳值調(diào)用與傳址調(diào)用
- 當(dāng)一個(gè)函數(shù)體內(nèi)部需要改變實(shí)參的值,則需要使用指針參數(shù)
- 函數(shù)調(diào)用時(shí)實(shí)參將賦值到形參
數(shù)組的基礎(chǔ)
- 數(shù)組在一片連續(xù)的內(nèi)存中存儲(chǔ)元素
- 元素的個(gè)數(shù)可以是顯示的也可以是隱式的
例子
a[5]={1,2}
其中其他的都用0來填充,所以數(shù)組最后為
a[5]={1,2,0,0,0}
數(shù)組地址與數(shù)組名
- 數(shù)組名代表數(shù)組首元素的地址
- 數(shù)組的地址需要取地址才可以得到
- 數(shù)組首元素的地址值與數(shù)組的地址值相同
- 數(shù)組收元素的地址與數(shù)組的地址是兩個(gè)不同的概念
數(shù)組名的盲點(diǎn)
- 數(shù)組名可以看作一個(gè)常量指針(指針?biāo)赶虻膬?nèi)容不能改變)
- 數(shù)組“指向” 的是內(nèi)存中數(shù)組首元素的起始位置
- 在表達(dá)式中數(shù)組名只能作為右值使用
- 只有在下列場合中數(shù)組名不能看作常量指針
- 數(shù)組名作為sizeof操作符的參數(shù)
- 數(shù)組名作為& 運(yùn)算符的參數(shù)
最佳錯(cuò)誤
新建一個(gè).c
文件
char * p="hello world";
再建一個(gè).c
文件
#include<stdio.h>
extern char p[];
void main()
{
printf("%s\n",p);
}
思路很簡單并闲,就是輸出hello world
细睡,然而輸出的結(jié)果是錯(cuò)誤的,原因就是上面列出來的幾點(diǎn)中的一點(diǎn)。
修改
Linux
#include<stdio.h>
extern char p[];
void main()
{
printf("%s\n",(char *)*(unsigned int *)p);
}
Windows平臺(tái)
#include<stdio.h>
extern char p[];
void main()
{
printf("%s\n",(char *)*(unsigned int *)p);
}
雖然會(huì)有warning但是程序運(yùn)行是正確的
C語言中的字符串
- 從概念上講帝火,C語言中沒有字符串?dāng)?shù)據(jù)類型
- 在C語言中使用字符數(shù)組來模擬字符串
- C語言中的字符串是以'\0'結(jié)束的字符數(shù)組
- C語言中的字符串可以分配于椓镝悖空間,堆空間或者只讀存儲(chǔ)區(qū)(不能被改變)
char* s1="Hello World1"犀填;(在只讀存儲(chǔ)區(qū))
字符串的長度
- 字符串的長度就是字符串說包含字符的個(gè)數(shù)
- C語言中的字符串的長度值得是第一個(gè)'\0'字符串出現(xiàn)的字符個(gè)數(shù)
- C語言中通過'\0'結(jié)束來確定字符串的長度
標(biāo)準(zhǔn)庫,用來解決字符長度
strlen()
彩蛋(一行程序?qū)崿F(xiàn)strlen)
int strlen(const char * p)
{
return (assert(s),(* p? strlen(p+1)+1:0));
}
不受限制的字符串函數(shù)
- 不受限制的字符串函數(shù)是通過尋找字符串得結(jié)束符'\0'來判斷長度
- 字符串復(fù)制函數(shù): char* strcpy(char* dst,const char* src)
- 字符串連接:char* strcat(char* dst,const char* src)
- 字符串比較函數(shù):int strcmp(const char* s1,const char* s2)
注意事項(xiàng)
- 不受限制的字符串函數(shù)都是以'\0'作為結(jié)束標(biāo)記來進(jìn)行的蠢壹,因此輸入?yún)?shù)必須包含'\0'
- strcat和strcpy必須保證目標(biāo)字符數(shù)組的剩余空間足以保存整個(gè)源字符串
- strcmp以0值表示兩個(gè)字符串相同
- 第一個(gè)字符串大于第二個(gè)字符串的時(shí)候返回值大于0
- 第一個(gè)字符串小于第二個(gè)字符串的時(shí)候返回值小于0
- strcmp不會(huì)修改參數(shù)值,但依然以'\0'作為結(jié)束符號(hào)
彩蛋(實(shí)現(xiàn)strcpy)
char* strcpy(char* dst,const char* src)
{
char* ret=dst;
assert(dst && src);
while((*dst++=*src++)!='\0');//指針循環(huán)效率高
return ret;
}
長度受限制的字符串函數(shù)
- 長度受限的字符串函數(shù)接收一個(gè)顯示的長度參數(shù)用于限定操作的字符串
- 字符串復(fù)制:char* strncpy
- 字符串連接
- 字符串比較
指針數(shù)組和數(shù)組指針分析
數(shù)組類型
- C語言中的數(shù)組有自己特定的類型
- 數(shù)組的類型由元素類型和數(shù)組大小共同決定
- int array[5]的類型為int[5]
定義數(shù)組類型
- C語言中通過typedef為數(shù)組類型重命名
- typedef type(name)[size];
- 數(shù)組類型(重命名了一種一個(gè)數(shù)組類型):
- typedef int(AINT5)[5];
- typedef float(AFLOAT10)[10];
- 數(shù)組定義:
- AINT5 i
- AFLOAT10 f
數(shù)組指針
- 數(shù)組指針用于指向一個(gè)數(shù)組
- 數(shù)組名是數(shù)組首元素的起始地址宏浩,但并不是數(shù)組的起始地址
- 通過將取地址符號(hào)&作用于數(shù)組名可以得到數(shù)組的起始地址
- 可通過數(shù)組類型1定義數(shù)組指針:ArrayType* pointer;
- 也可以直接定義:type (*pointer)[n];(用于定義數(shù)組指針)
- pointer為數(shù)組指針的變量名
- type為指向的數(shù)組的類型
- n為指向的數(shù)組的大小
最佳示例
#include <stdio.h>
typedef int(AINT5)[5];
typedef float(AFLOAT10)[10];
typedef char(ACHAR9)[9];
int main()
{
AINT5 a1;
float fArray[10];
AFLOAT10* pf = &fArray;
ACHAR9 cArray;
char(*pc)[9] = &cArray;
char(*pcw)[4] = cArray;
int i = 0;
printf("%d, %d\n", sizeof(AINT5), sizeof(a1));
for(i=0; i<10; i++)
{
(*pf)[i] = i;
}
for(i=0; i<10; i++)
{
printf("%f\n", fArray[i]);
}
printf("%0X, %0X, %0X\n", &cArray, pc+1, pcw+1);
//pc+1指向整個(gè)數(shù)組的后一個(gè)數(shù)組
}
指針數(shù)組
- 指針數(shù)組是一個(gè)很普通的數(shù)組
- 指針數(shù)組中每個(gè)元素為一個(gè)指針
- 數(shù)組指針的定義:type* pArray[n];
- type*為數(shù)組中每個(gè)元素的類型
- pArray為數(shù)組名
- n為數(shù)組大小
#include <stdio.h>
#include <string.h>
int lookup_keyword(const char* key, const char* table[], const int size)//第二個(gè)是指針數(shù)組
{
int ret = -1;
int i = 0;
for(i=0; i<size; i++)
{
if( strcmp(key, table[i]) == 0 )
{
ret = i;
break;
}
}
return ret;
}
#define DIM(a) (sizeof(a)/sizeof(*a))
int main()
{//指針數(shù)組
const char* keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while",
"case",
"static"
};
printf("%d\n", lookup_keyword("return", keyword, DIM(keyword)));
printf("%d\n", lookup_keyword("main", keyword, DIM(keyword)));
}
main函數(shù)的參數(shù)
- main函數(shù)可以理解為操作系統(tǒng)調(diào)用的函數(shù)
- 在執(zhí)行程序的時(shí)候可以向main函數(shù)傳遞參數(shù)
main函數(shù)的四種參數(shù)類型
int main()
int main(int argc)
int main(int argc,char *argv[])
int main(int argc,char *argv[],char *env[])
小結(jié)
- 數(shù)組指針本質(zhì)上是一個(gè)指針
- 數(shù)組指針指向的值是數(shù)組的1地址
- 指針數(shù)組本質(zhì)上是一個(gè)數(shù)組
- 指針數(shù)組中的每個(gè)元素的類型是指針
多維數(shù)組和多維指針
- 指針變量在內(nèi)存中會(huì)占用一定的空間
- 可以定義指針來保存指針變量得地址值
int main()
{
int a=0;
int* p=NULL;
int** pp=NULL;
pp=&p;
*pp=&a;
return 0;
}
指向指針的指針
- 為什么需要指向指針的指針知残?
- 指針在本質(zhì)上也是一個(gè)變量
- 對(duì)于指針來講也有傳值調(diào)用與傳址調(diào)用
最佳示例(動(dòng)態(tài)內(nèi)存大小更變)
#include<stdio.h>
#include<malloc.h>
int rest(char** p,int size,int new_size)
{
int ret=1;
int len=0;
int i=0;
char* mid=NULL;
char* pt=NULL;
char* pp=*p;
if((p!=NULL)&&(new_size>0))
{
mid=(char*)malloc(3);
pt=mid;
len=(size<new_size)?size:new_size;
for(i=0;i<len;i++)
{
*pt++=*pp++;
}
free(*p);
*p=pt;
}else{
ret=0;
}
return ret;
}
void main()
{
char *p=(char*)malloc(5);
printf("%0X\n",p);
if(rest(&p,5,3))
{
printf("%0X\n",p);
}
}
二維數(shù)組與數(shù)組指針
- 二維數(shù)組在內(nèi)存中以一維的方式排布
- 二維數(shù)組中的第一維是一維數(shù)組
- 二維數(shù)組中的第二維才是具體的值
- 二維數(shù)組的數(shù)組名可以看作常量指針
二維數(shù)組.png
#include<stdio.h>
#include<malloc.h>
void printArray(int a[],int size)
{
int i=0;
printf("printfArray:%d\n",sizeof(a));
for(i=0;i<size;i++)
{
printf("%d\n",a[i]);
}
}
int main()
{
int a[3][3]={{0,1,2},{3,4,5},{6,7,8}};
int *p=&a[0][0];
printArray(p,9);
return 0;
}
數(shù)組名
- 一維數(shù)組名代表數(shù)組收元素的地址
- 二維數(shù)組同樣代表數(shù)組首元素的地址
#include<stdio.h>
int main()
{
int a[5][5];
int (*p) [4];
p=a;
printf("%d\n",&p[4][2]-&a[4][2]);
}
答案為-4,因?yàn)椋?p)一次跨越4個(gè)比庄,而a一次跨越5個(gè)
數(shù)組的遍歷
int a[3][3]={{}};
for(i=0;i<3;i++)
for(j=0;j<3;j++)
*(*(a+i)+j)
動(dòng)態(tài)分配二維數(shù)組
原理大家很聰明就不解釋二維數(shù)組動(dòng)態(tài)分配.png
#include<stdio.h>
#include<malloc.h>
int** malloc2d(int row,int col)
{
int** ret=(int**)malloc(sizeof(int*)*row);
int* p=(int*)malloc(sizeof(int)*row*col);
int i=0;
if(ret&&p)
{
for(i=0;i<row;i++)
{
ret[i]=p+col*i;
}
}else{
free(ret);
free(p);
ret=NULL;
p=NULL;
}
return ret;
}
void del_Array(int** a)
{
free(a);
}
void main()
{
int i=0,j=0;
int row=0,col=0;
printf("please input the row\n");
scanf("%d",&row);
printf("please input the col\n");
scanf("%d",&col);
int** p=malloc2d(row,col);
for(i=0;i<row;i++)
{
for(j=0;j<col;j++)
{
p[i][j]=i+j;
}
}
for(i=0;i<row;i++)
{
for(j=0;j<col;j++)
{
printf("%d ",p[i][j]);
}
printf("\n");
}
}
數(shù)組參數(shù)和指針參數(shù)分析
注意C語言的編譯器會(huì)讓(不論是一維數(shù)組還是二維數(shù)組)數(shù)組參數(shù)退化為指針
為什么退化
- C語言中只會(huì)以值拷貝的方式傳遞參數(shù)
- 當(dāng)向函數(shù)傳遞數(shù)組時(shí)
- 將整個(gè)函數(shù)拷貝一份傳入函數(shù)(不可惹竺谩)
- 將數(shù)組名看作常量指針傳數(shù)組首元素地址
二維數(shù)組參數(shù)
- 二維數(shù)組參數(shù)同樣存在退化問題
- 二維數(shù)組可以看作是一維數(shù)組
- 二維數(shù)組中的每一個(gè)元素是一維數(shù)組
- 二維數(shù)組參數(shù)中第一維的參數(shù)可以省略(退化過程)
void f(int a[10])->void f(int a[])->void f(int *a)
void g(int a[3][3])->void g(int a[][3])->void g(int (*a)[3])
注意事項(xiàng)
- C語言中無法向一個(gè)函數(shù)傳遞任意的多維數(shù)組(針對(duì)二維以上)
- 為了提供正確的指針運(yùn)算,必須提供除一維之外的所有維的長度
- 限制
- 一維數(shù)組-必須提供結(jié)束的標(biāo)志
- 二維數(shù)組-不能直接傳遞給函數(shù)
- 多維-無法使用
#include<stdio.h>
#include<malloc.h>
void access(int a[][3],int row)
{
int col=sizeof(*a)/sizeof(int);//去推導(dǎo)出列的數(shù)量
int i=0,j=0;
for(i=0;i<row;i++)
{
for(j=0;j<col;j++)
{
printf("%d ",a[i][j]);
}
printf("\n");
}
}
void main()
{
int a[3][3]={
{1,2,3},
{4,5,6},
{7,8,9}
};
access(a,3);
}
函數(shù)與指針分析
函數(shù)
函數(shù)類型
- C語言中的函數(shù)有自己特定的類型
- 函數(shù)的類型由返回值佳窑,參數(shù)類型和參數(shù)個(gè)數(shù)共同決定的
- C語言中通過typedef為函數(shù)類型重命名
- typedef type name(parameter list)
- typedef int f(int,int);
函數(shù)指針
- 函數(shù)指針用于指向一個(gè)函數(shù)
- 函數(shù)名是執(zhí)行函數(shù)體的入口地址(類似于數(shù)組名字)
- 可通過函數(shù)類型定義函數(shù)指針:FuncType* pointer;
- 也可以直接定義:type (*pointer)(parameter list);
- pointer為函數(shù)指針變量名
- type為指向函數(shù)的返回值
- 參數(shù)類型的列表
函數(shù)指針的本質(zhì)與使用
回調(diào)函數(shù)
- 回調(diào)函數(shù)是利用函數(shù)指針實(shí)現(xiàn)的一種調(diào)用機(jī)制
- 回調(diào)機(jī)制的原理
- 調(diào)用者不知道具體事件發(fā)生的時(shí)候需要調(diào)用的具體函數(shù)
- 被調(diào)用函數(shù)不知道何時(shí)被調(diào)用制恍,只知道被調(diào)用后需要完成的任務(wù)
- 當(dāng)具體事件發(fā)生時(shí),調(diào)用者通過函數(shù)指針調(diào)用具體函數(shù)
- 回調(diào)機(jī)制的將調(diào)用者和被調(diào)用的函數(shù)分開神凑,兩者互相不依賴
最佳示例C語言回調(diào)函數(shù)
#include <stdio.h>
typedef int(FUNC)(int);
int test(int i)
{
return i * i;
}
void f()
{
printf("Call f()...\n");
}
int main()
{
FUNC* pt = test;
//void(*pf)() = &f;//老方法
//pf();
//(*pf)();//老方法
printf("Function pointer call: %d\n", pt(2));
}
進(jìn)階回調(diào)
#include <stdio.h>
typedef int(*FUNCTION)(int);
int g(int n, FUNCTION f)
{
int i = 0;
int ret = 0;
for(i=1; i<=n; i++)
{
ret += i*f(i);
}
return ret;
}
int f1(int x)
{
return x + 1;
}
int f2(int x)
{
return 2*x - 1;
}
int f3(int x)
{
return -x;
}
int main()
{
printf("x * f1(x): %d\n", g(3, f1));//注冊(cè)
printf("x * f2(x): %d\n", g(3, f2));
printf("x * f3(x): %d\n", g(3, f3));
}