指標與字串、指標與函式
標題1、 指標與字串的千絲萬縷
由於在C語言中,是通過字元陣列來儲存字串的,因而前面通過指標對一維陣列和二維陣列的操作方法,同樣適用於字串。其實,對字串的操作最好採用字串操作函式。
#include<stdio.h>
int main()
{
char name[12],*p;
p=name;
gets(p);
puts(p);
return 0;
}
再例如:char *p=”hello”;等價於char *p; p=”hello”。 它的意思是把:hello串在記憶體中的首地址賦值給p。 我們已經講過,如果要儲存和處理我們班49個同學的姓名,可以定義一個二維陣列,假設48個同學的姓名只佔6個位元組,因而第二維的大小為7即可,只有一個同學是少數民族,她的姓名需要佔19個位元組,因而需要給它分配至少20個位元組的單元才能存放,為此,整個二維陣列的第二維的大小必須為20個位元組。char name[49][20]; 顯然,這樣會造成記憶體的浪費:48*13個位元組的空間是無用的。如果隨著問題規模的增大,這個浪費會更大,有沒有一種辦法,當需要多大空間的時候就分配多大的空間,字元指標陣列就可以解決這個問題。 char *name[3]={"hello","lcy","hahahahahahaha"}; 這樣定義之後,”hello”在記憶體中的首地址賦值給name[0],同理”lcy”在記憶體中的首地址賦值給name[1],"hahahahahahaha"在記憶體中的首地址賦值給name[2]。 有同學會問:如果我們班同學的姓名都這樣初始化的話,感覺也挺麻煩的:char* name[49]={“111”,”222”,,,,,,,,,,,,,,,,}; 姓名能不能在程式中輸入呢? #include<stdio.h> #include<string.h> #include<stdlib.h> int main() { char *name[49], s[20]; int i; for(i=0;i<49;i++) { gets(s); name[i]=(char*)malloc(strlen(s)+1); if(name[i]!=NULL) strcpy(name[i],s); } for(i=0;i<49;i++) puts(name[i]); for(i=0;i<49;i++) free(name[i]); return 0; }
因為malloc函式分配的記憶體空間是在堆區上分配的,程式結束後,系統不會將其自動釋放,需要程式設計師自己管理,C提供了free函式來釋放記憶體。
標題2 :指標與函式
1. 指標作為函式引數
指標作為函式引數有兩種形式:
(1)指標變數作為函式引數。前面我們講過的swap函式,就是採用指標變數作為函式引數,才能通過swap函式交換實參的值。
(2)陣列作為函式引數。前面我們講解一維陣列時,如果採用子函式形式來描述氣泡排序和選擇排序時,就是採用了陣列作為函式形參。這裡不再贅述。
下面我們看一下字串的連線函式來加深記憶吧。
(1)採用指標變數作為函式形參:
#include<stdio.h> void connect_string(char *from,char *to) { while(*from!='\0') from++; while(*to!='\0') *from++=*to++; *from='\0'; } int main() { char str1[50],str2[50]; gets(str1); gets(str2); connect_string(str1,str2); puts(str1); return 0; }
(2)採用陣列作為函式形參
#include<stdio.h> #include<string.h> void connect_string(char from[],char to[]) { int i,j; for(i=strlen(from),j=0;j<strlen(to);i++,j++) from[i]=to[j]; from[i]='\0'; } int main() { char str1[50],str2[50]; gets(str1); gets(str2); connect_string(str1,str2); puts(str1); return 0; }
2. 返回指標的函式(也稱為指標函式)
函式的返回值可以是int、double、char、int、void,C語言還允許函式的返回值是一個指標(地址)。
#include<stdio.h>
char *strchr(char *s,char ch)
{
while(*s!='\0')
if(*s==ch) return s;
else s++;
}
int main()
{
char *p=NULL,ch,s[]="hello,big data!";
ch=getchar();
p=strchr(s,ch);
if(p)
{
printf("%c's position is:%x\n",ch,p);
printf("%c's position is:%d\n",ch,p-s+1);
}
else
printf("%c isn't exist!\n");
return 0;
}
該程式的功能,輸出某個字元(該字元可通過鍵盤輸入)在字串中的位置。
3. 指向函式的指標
如果在程式中定義了一個函式,在編譯時,編譯器為該函式程式碼分配了一段儲存空間,這段儲存空間的首地址可以用函式名來描述。因而,可以把這個首地址賦給一個指標變數,使得該指標變數指向該函式,把這種指向函式的指標變數稱為:函式指標變數或者函式指標。
#include<stdio.h>
int func(int a,int b)
{
return a+b;
}
int main()
{
int result;
int(*p)(int,int);
p=func;
result=(*p)(3,5);
printf("%d\n",result);
return 0;
}
其中,p=func; //讓指標變數指向了func函式的首地址
result=(*p)(3,5);//通過指標變數p來呼叫函式
函式指標可作為函式引數。
用函式名來呼叫函式,只能呼叫指定的函式。而通過函式指標變數呼叫函式就比較靈活了,可以根據不同情況先後呼叫不同的函式。
#include<stdio.h>
int add(int a,int b)
{
return a+b;
}
int minus(int a,int b)
{
return a-b;
}
int xc(int a,int b)
{
return a*b;
}
int func(int(*p)(int,int),int b,int n)
{
int result=0,i;
for(i=0;i<n;i++)
result+=(*p)(i,b);
return result;
}
int main()
{
int result;
result=func(add,1,10);
printf("%d\n",result);
result=func(minus,1,10);
printf("%d\n",result);
result=func(xc,1,10);
printf("%d\n",result);
return 0;
}
4. 帶引數的main函式
前面使用的main都是無參的,實際上該函式也可以接收引數。main()函式是程式的入口,通常用來接收來自系統的引數。argc表示在命令列中輸入的引數個數,argv為字元指標陣列,其各個元素值為命令列中各字串的首地址。
#include<stdio.h>
int main(int argc,char* argv[])
{
int i;
printf("%d\n",argc);
for(i=0;i<argc;i++)
puts(argv[i]);
return 0;
}
5、 其它知識
週二實驗課,黎龍洋同學問了一個memset函式的問題,問是不是進行初始化的。其實,還有很多對記憶體進行操作的函式:熟悉的malloc、free,還有memcpy、memmove和memcmp等等。用到的時候查閱下資料,不同的函式均有不同的用途。C++提供了new來進行記憶體空間的動態分配,我感覺也非常方便。
#include<iostream>
using namespace std;
int main()
{
int *p,n,i;
cin>>n;
p=new int[n]{0}; //初始化
for(i=0;i<n;i++)
cout<<*(p+i)<<" ";
cout<<endl;
delete [] p;
return 0;
}
需要注意的是:p=new int[n]{0}; 動態開闢一個含n個int的無名陣列空間,把該空間的首地址賦給P,陣列中的元素均初始化為0。
delete [] p; //釋放空間,因為p指向的是陣列,因而需要加上[ ]。