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