C语言:随笔6--指针1.2
1、多維數(shù)組與指針
用指針變量可以指向一維數(shù)組中的元素,也可以指向多維數(shù)組的元素。一個數(shù)組他的數(shù)組名就是這個數(shù)組的首地址,而指針變量里邊存放的恰好就是一個地址。我們只要把這個指針變量里邊存放的是一維數(shù)組的首地址,那么我們就說這個指針指向的就是這個數(shù)組。
下面是一個3行4列的數(shù)組,在內(nèi)存中的存儲其實他是線性排列存儲的。
??
#include<stdio.h>
void main()
{int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11,12};printf("a:%d\n",a);printf("*a:%d\n",*a);printf("a[0]:%d\n",a[0]);printf("&a[0]:%d\n",&a[0]);printf("&a[0][0]:%d\n",&a[0][0]);printf("a+1:%d\n",a+1);printf("*(a+1):%d\n",*(a+1));
}
把二維數(shù)組a分解為一維數(shù)組a[0](我們這個a[0]又指向了四個元素,a[0]里面存放的是一個地址,這個地址有四個元素,那么也就是說它就是一個二維數(shù)組,),a[1](同理),a[2](同理)之后(說明這是一個a3x4的一個二維數(shù)組,概念就是當我這個一維數(shù)組這個元素里邊存放的是另一個數(shù)組的地址的時候,那他就是一個二維數(shù)組,因為我這個元素a[0]還沒完,里面還指向一個地址,這個地址是一個數(shù)組的地址,那就是二維數(shù)組了),設(shè)p為指向二維數(shù)組的指針變量。可定義為:
int (*p)[4];//它表示p是一個指針變量,它指向包含四個元素的一維數(shù)組。若指向第一個一維數(shù)組a[0],其值等于a,a[0],或者&a[0][0]等。
//而p+i則指向一維數(shù)組a[i];因為p指向的是a的地址。
//從前面的分析可以得出*(p+i)+j是二維數(shù)組i行j列的元素的地址,而*(*(p+i)+j)則是i行j列元素的值
//怎么理解呢?????????
//因為我們之前解釋,如果一維數(shù)組里面存放的是另外一個數(shù)組的地址那它就是一個二維數(shù)組。
//那我們這里p+i也就是說一維數(shù)組第幾個元素取出他的值(比如是a[i],*(p+i)是一個地址值,**(p+i)才是對應(yīng)第i行的首個元素值)
也就是相應(yīng)一個二維數(shù)組的一個元素的初始地址,再加上j就是這個二維數(shù)組的偏移地址,第幾列,然后我們再把總共的取出他的值。
//二維數(shù)組指針變量說明的一般形式為:
類型說明符? (*指針變量名)[長度]
其中“類型說明符”為所指數(shù)組的數(shù)據(jù)類型。“*”表示其后的變量是指針類型。“長度”表示二維數(shù)組分解為多個一維數(shù)組時,一維數(shù)組的長度,也就是二維數(shù)組的列數(shù)。(//前面的不是應(yīng)該聲明為行數(shù)嗎?那行數(shù)不用定義嗎?)。行數(shù)不用定義,這個行數(shù)是多少。也就是說一維數(shù)組是多少我們的具體長度是看我們給他賦值,賦多少值它自動分配的。所以我們直接給出一個指針,指向它的行數(shù)的首地址就可以了。
例子:用指針輸出二維數(shù)組元素變量的值
#include<stdio.h>
void main()
{int a[3][4]={};int (*p)[4];//定義一個二維數(shù)組的指針,指向二維數(shù)組//切記(*p)中的括號一定要帶p=a;//將二維數(shù)組的地址指向了這個指針的地址int i,j;//定為i和j來定義行和列for(i=0;i<3;i++){for(j=0;j<4;j++){printf("%2d",*(*(p+i)+j));}printf("\n");}
}
例子:通過輸入指定行數(shù)和列數(shù)打印出二維數(shù)組對應(yīng)任一行任一列元素的值。
#include<stdio.h>
void main()
{int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};int (*p)[4],i,j;p=a;printf("i=");scanf("%d",&i);while(i>2||i<0){printf("i=");scanf("%d",&i);}printf("j=");scanf("%d",&j);while(j>3||i<0){printf("j=");scanf("%d",&j);}printf("a[%d,%d]=%d\n",i,j,*(*(p+i)+j));//經(jīng)過兩次取值
}
2、字符串與指針
(1)用字符數(shù)組存放一個字符串,然后輸出該字符串。
例子:定義一個字符數(shù)組,對他初始化,然后輸出該字符串。(通過字符數(shù)組的形式來定義字符串)
#include<stdio.h>
void main()
{char string[]="I Love Fishc.com!";//不定義長度,讓編譯器自行計算printf("%s\n",string);//s就是字符串的形式打印,給他一個地址,給他字符串的首地址他就會依次打印直到出現(xiàn)'\0'字符他就停止打印。
}
下面展示了元素依次存放,空格也占用一個元素。在最后還要補充一個'\0',\0這個字符相當于ASCII中的整形0。
(2)用字符指針指向一個字符串。
例子:可以不定義字符數(shù)組,而定義一個字符指針。用字符指針指向字符串中的字符。
#include<stdio.h>
void main()
{char *string="I Love Fishc.com!";//這里定義的是一個字符指針,不再是一個字符串printf("%s\n",string);
}
(3)對字符串中字符的存取,可以用下標方法,也可以用指針方法。
例子:將字符串a(chǎn)復制為字符串b
//下標法舉例
#include<stdio.h>
void main()
{char a[]="A is a good man!",b[40];int i;for(i=0;*(a+i)!='\0';i++)//只要a數(shù)組的這個元素不為0,0就是字符串結(jié)束的標志{*(b+i)=*(a+i);//不為0的話就依次copy過去}*(b+i)='\0';//然后最后還應(yīng)該把他補上0因為我們這個是字符串,如果沒有補上的話,他就不知道字符串的結(jié)尾是哪里,她就會把內(nèi)存中相關(guān)的我們定義的40個字符嘛,這些隨機的數(shù)據(jù)他都會把它當成字符串給他顯示出來直到?jīng)]有。printf("String a is:%s\n",a);printf("String b is:");//2也可以直接打印bfor(i=0;b[i]!='\0';i++){printf("%c",b[i]);//1使用了數(shù)組的索引方式打印的}printf("\n\n");
}
用指針的方法就是迭代循環(huán)里邊的傳遞不同
//下標法舉例
#include<stdio.h>
void main()
{char a[]="A is a good man!",*p1,*p2;//定義的兩個指針分別指向數(shù)組a和數(shù)組b,那么我取出p1的值也就相當于取出a的值int i;p1=a;p2=b;for(;*p1!='\0';p1++,p2++)//++就是遞增1,往后邊元素,指向后邊元素{*p2=*p1;//不為0的話就依次copy過去}*p2='\0';//然后最后還應(yīng)該把他補上0因為我們這個是字符串,如果沒有補上的話,他就不知道字符串的結(jié)尾是哪里,她就會把內(nèi)存中相關(guān)的我們定義的40個字符嘛,這些隨機的數(shù)據(jù)他都會把它當成字符串給他顯示出來直到?jīng)]有。printf("String a is:%s\n",a);printf("String b is:");//2也可以直接打印bfor(i=0;b[i]!='\0';i++){printf("%c",b[i]);//1使用了數(shù)組的索引方式打印的}printf("\n\n");
}
(4)字符指針作函數(shù)參數(shù)
用函數(shù)調(diào)用實現(xiàn)字符串的賦值
(1)用字符數(shù)組作參數(shù)。
(2)形參用字符指針變量。
//設(shè)一個函數(shù)process,在調(diào)用他的時候,每次實現(xiàn)不同的功能。
//輸入a,b第一次調(diào)用process時找最大,第二次找最小,第三次求和。
#include<stdio.h>
void main()
{int max(int,int);int min(int,int);int add(int,int);void process(int,int,int(*fun)());int a,b;printf("Enter a and b:");scanf("%d %d",&a,&b);printf("max=");process(a,b,max);printf("min=");process(a,b,min);printf("add");process(a,b,add);
}
int max(int x,int y)
{int z;if(x>y){z=x;}else{z=y;}return z;
}
(5)
(6)返回指針值的函數(shù)
一個函數(shù)可以帶回一個整型值、字符值、實型值等,也可以帶回指針型的數(shù)據(jù),即地址。其概念與以前類似,只是帶回的值的類型是指針類型而已。
這種帶回指針值的函數(shù),一般定義形式為:
類型名 *函數(shù)名(參數(shù)列表);
//例如:
int *a(int x,int y);//加上*號表示帶回來的是指向整型的指針。如果不帶回什么就void
例子:有若干個學生的成績(每個學生有4門課程),要求在用戶輸入學生序號以后,能輸出該學生的全部成績,用指針函數(shù)來實現(xiàn)。
#include<stdio.h>
void main()
{//用二維數(shù)組每一行表示每一個同學,4個列表示4門課程double score[][4]={{},{},{},{}};double *search(double(*pointer)[4],int n);//定義了一個指針函數(shù)(他首先是一個函數(shù),因為有一個括號,接著返回指針類型,這個指針還是指向double的指針)double *p;int i,m;printf("PLease enter the number of student:");scanf("%d",&m);printf("The scores of No.%d are:",m);p=search(score,m);//3返回值給p把他的地址給p,然后下邊再打印出來。for(i=0;i<4;i++)//打印第m行的元素值;p代表第m 行的首地址{printf("%5.2f\t",*(p+i));}printf("\n\n\n")
}
double *search(double(*pointer)[4],int n)
{double *pt;pt=*(pointer+n);//1序號加上行數(shù)的索引,取出來的就是說某一個學生的一個歸于那一行地址的一個索引,然后再返回return pt;//2返回的是二維數(shù)組第n行的首地址
}
對上例中的學生,找出其中有不及格課程的學生及其學生號。(對比一下,把各個成績提取出來,如果小于60,Ok就把這個數(shù)組的指針給返回來,然后接收到指針把他對應(yīng)的人給打印出來)
//代碼
PS:指針函數(shù)和函數(shù)指針的區(qū)別(這兩個概念都是簡稱)
指針函數(shù):是指帶指針的函數(shù),即本質(zhì)是一個函數(shù)。
函數(shù)指針:是指向函數(shù)的指針變量,因而函數(shù)指針本身首先是指針變量,只不過該指針變量指向函數(shù)。
(7)指針數(shù)組和指向指針的指針
指針數(shù)組(終究還是一個數(shù)組,數(shù)組里邊的每個元素都是指針類型)的概念:
一個數(shù)組若其元素均為指針類型的數(shù)據(jù),稱為指針數(shù)組,也就說,指針數(shù)組中的每一個元素都相當于一個指針變量(也就是一堆指針變量排排站站成一隊)。一維指針數(shù)組的定義形式為:
類型名 數(shù)組名[數(shù)組長度];
//例如:
int *name[4];//*表示他是一個指針
上述指針數(shù)組定義了四個元素大小,也就是每個元素里邊存放的都是指針地址。例如:
int main()
{//指針數(shù)組:該數(shù)組里邊存放的是地址。下邊你看到的雖然是一個個字符串,其實字符串是有地址的。//存儲char*類型的地址數(shù)組//數(shù)組里邊存儲的是地址,一個字符串就有一個他的地址,這個地址里邊也可以找到他的單個的字符。char* arr[]={"hello","world","nihao","baobei"};//字符指針數(shù)組//別看在里邊它是一個"hello"字符串,其實在里邊表示的是該字符串的首地址。printf("%c\n",*(arr[0]+1));//打印出來的是hello中的e單個字符。因為arr[0]找到的是hello的首地址,再加1就是e這個位置相當于arr[0][1]。//*arr[0]取得才是hello。//*arr[1]取得就是world。return 0;
}
以前的是數(shù)組指針:強調(diào)的是指針,該指針變量指向的是一個數(shù)組。
int arr[10]={0};
int* p;//定義指針變量p;
p=arr;//p需要的是一個地址,arr數(shù)組首地址。
//可以把p當作一個數(shù)組進行操作p[1]=arr[1];*p=arr[0];*(p+1)=arr[1];
(8)總結(jié)
5、兩個指針變量的比較
若兩個指針指向同一個數(shù)組的元素,則可以進行比較,指向前面的元素的指針變量“小于”指向后面的元素的指針變量。(其實比較的是兩個指針分別指向這個數(shù)組元素的序號)
6、void類型
void真正發(fā)揮作用:(1)對函數(shù)返回類型的限定;(2)對函數(shù)參數(shù)類型的限定。
void類型用于指針。ANSIC新標準增加了一種”void“指針類型,即不指定他是指向哪一種類型數(shù)據(jù)的指針變量。(他是空類型啥都不是,當成放一個地址,放一個地址指向的是什么變量呢?暫時還不知道。但是我們可以通過一個括號int即()int把他變成一個整型,或者()其他類型)
李例如void *p;表示指針變量p不指向一個確定的類型數(shù)據(jù),他的作用僅僅是用來存放一個地址。
void指針他可以指向任何類型數(shù)據(jù)(因為他是空的),也就是說可以用任何類型的指針直接給void指針賦值,但是,如果需要將void指針的值賦給其他類型的指針,則需要進行強制類型轉(zhuǎn)換。
7、用三個例子來談?wù)刢onst(指針)
#include<stdio.h>
void main()
{const char *str="Welcome to!\n\n";//這個語句的含義是:聲明一個名為str的指針變量,他指向一個字符型常量,初始化str為指向字符串"Welcome to\n\n"
//再聲明他為const就是常量(就是是一個變量常量化使他不能夠被改變)
#if(0)//宏定義//如果改為1讓他編譯就會報錯:L-value specifies const object.(因為常量對象是不能被賦值的,不能充當左值的)str[0]='w';//這條語句是錯誤的,但可以改變str指針的值。
#enfifstr="I Love!\n\n";//合法//我們強制把他的地址改變成指向"I Love!\n\n"printf("\n\n%s",str);
}
將上邊的修改一下
#include<stdio.h>
void main()
{char *const str="Welcome to!\n\n";//存放在常量的data區(qū)//常量指針是一個固定的指針,不可以改變它的值,但它所指的數(shù)據(jù)可以改變。str[0]='w';//windows禁止常量被重寫(所以我這里把他重寫為小寫的w他就不給寫)//但是編譯是符合c語言的語法規(guī)范的
#if(0)str="I Love!\n\n";//非法
//從上邊可以看到const接近str這個指針的時候,說明把這個指針定義為const變量了,他就不能被修改。
//如果說把const放在前面,那就是定義每一個字符是一個const常量不能被替換。
#enfifprintf("\n\n%s",str);
}
再修改一下:
#include<stdio.h>
void main()
{const char *const str="Welcome to!\n\n";//存放在常量的data區(qū)//兩邊都是const變量str[0]='w';//非法str="I Love!\n\n";//非法printf("\n\n%s",str);
}
API函數(shù):memcpy
//copyes characters between buffers
void *memcpy(void *dest,const void*src,size_t count)
多級指針:
二級指針存儲的是一級指針的地址。(不管是幾級指針存儲的都是地址,但是里邊存儲的地址卻不同,一級指針存儲的是變量的地址,二級指針存儲的是指針變量的地址(一級指針變量的地址))。
int main()
{int a=0;int* p=&a;int **pp=&p;//如果二級指針前邊加一個*代表一級指針的值(但該一級指針的值是一個地址),*pp=&a;// *pp等價于&a等價于p;//二級指針前邊加2個*的話,代表指針指向一級指針指向地址的值**pp=20;//改變的是a的值。return 0;
}
延申:三級指針存放的是二級指針變量的地址。
指針和函數(shù):
? ? ? ? 主函數(shù)調(diào)用 (因為傳的是值)
? ?? 雖然形參值改變了,但是星燦值不會影響實參值。(而且定義的兩個形參變量會在函數(shù)結(jié)束之后給他結(jié)束掉,銷毀掉;臨時變量temp也會掉銷毀掉,因為該函數(shù)結(jié)束之后他會把里邊所有的變量銷毀掉)為什么主函數(shù)中輸出沒有改變a和b的值(主函數(shù)調(diào)用tab()函數(shù),該函數(shù)中沒有打印輸出printf這個語句),而在上述函數(shù)定義中寫輸出語句卻改變了a 和b的值。
值傳遞不能改變實參的值。
??
地址傳遞可以改變實參的值。
數(shù)組作為函數(shù)參數(shù)可以退化為指針,在傳遞數(shù)組時需要加上數(shù)組的個數(shù)。
?
??
指針作為函數(shù)返回值:
數(shù)組也就是char*類型。
這樣寫的情況下:打印出來的具體地址是有的,但沒有打印出來字符串。(因為)
改成如下情況可以打印出字符串helllo world的值:
這種是一個指針,指向的是hello world,這個hello world是一個字符串常量,會在程序運行時放在常量區(qū),生命周期是在程序運行時開始,在程序運行結(jié)束時結(jié)束。(常量區(qū)不能被修改但是可以讀)
在test()函數(shù)中寫的內(nèi)容,除了常量、static修飾的、全局變量其他都會被銷毀掉。而且test函數(shù)返回值類型是一致的,并且保證返回的指針地址對應(yīng)的值是有內(nèi)容的。
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的C语言:随笔6--指针1.2的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C语言:随笔5--指针1
- 下一篇: Papers with Code