1、指標數組和數組指標
指標數組本質為數組,只是數組的元素是指向某種類型資料的指標,其定義形式如下:
類型名 *數組名[數組長度]。
數組指標本質上還為一個指標,只是這個指標指向的資料類型為數組,其定義形式如下:
類型名 (*指標名)數組長度]
#include <stdio.h>void main(int argc,char argv[]){ int arr[4][4]= {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; int (*p1)[4]; // 數組指標,指標指向int[4]的資料類型 int *p2[4]; // 指標數組,數組裡面的元素為指標 int i,j,k; p1=arr; for(i=0; i<4; i++) { for(j=0; j<4; j++) { printf("arr[%d][%d]=%d\t",i,j,*(*(p1+i)+j));// 通過數組指標輸出數組各元素 } printf("\n"); } for(k=0; k<4; k++) p2[k]=arr[k];// 此時的arr[k]為指向一維數組的地址,還可以使用*(arr+i)或arr+i來表示每行的起始地址 for(i=0; i<4; i++) { for(j=0; j<4; j++) { printf("arr[%d][%d]=%d\t",i,j,*((p2[i]+j)));// 使用指標數組輸出數組各元素 } printf("\n"); } return ;}
在Code:Blocks中運行,結果如所示。
2、指標函數和函數指標
指標函數是指帶指標的函數,本質上是一個函數,只是返回的是某種類型的指標,其定義的格式如下:
類型標識符 *函數名(參數列表)
函數指標,從本質上來說是一個指標,只是它指向的不是一般的變數,而是一個函數。因為每個函數都有一個入口地址,函數指標指向的就是函數的入口地址。定義的基本格式如下:
類型標識符 (指標變數名稱)(形參列表)
#include <stdio.h>// 函數指標的本質上是一個指標char* (*fun)(char *str,char *substr);void input(char *str,char *substr){ printf("請輸入字串:"); gets(str); printf("請輸入要搜尋的字串:"); gets(substr);}int strlen(char *str)// 計算輸入str字串的長度{ int i=0; while(str[i]!='\0') i++; return i;}char* search_str(char *str,char *search_str)// 指標函數{ int i,k,j; k=strlen(str)-strlen(search_str); if(k>0&&NULL!=str&&NULL!=search_str) { for(i=0; i<=k; i++) { for(j=i; str[j]==search_str[j-i]; j++) if(search_str[j-i+1]=='\0') return str+i+strlen(search_str); } } return NULL;}void print(char* ret_str){ if(ret_str!=NULL) printf("搜尋之後的字串為:%s\n",ret_str); else printf("沒有找到字串\n");}void main(){ char str1[50],str2[50]; char search_str1[50],search_str2[50]; char * ret_str1,* ret_str2; //------------------------------------------------ input(str1,search_str1); ret_str1=search_str(str1,search_str1);//調用指標函數 printf("直接調用函數search_str()\n"); print(ret_str1); //------------------------------------------------- input(str2,search_str2); fun=search_str;// 讓函數指標fun指向函數的入口地址,函數指標要與它所指向的函數具有相同的類型 ret_str2=fun(str2,search_str2); printf("使用函數指標調用search_str()\n"); print(ret_str2); return ;}
在Code:Blocks中運行,結果如所示。
3、指標與數組的關係
數組名在當作參數進行傳遞時,數組名為指向數組的指標。
#include <stdio.h>void copy_string(char from[],char to[]){ printf("careful,%d\n",'\0'); //C語言的while迴圈不同與Java的while迴圈,繼續迴圈的條件為非零,由於'\0'字元與整數的相通性,所以當為字串結束標誌'\0'時,while停止迴圈 while(*to++=*from++);// while迴圈在實現時,由於str字元數組的後面有結束符'\0',因此巧妙地將這一條件作為複製是否結束的標誌。 return ;}void main(){ char str[]="this is a string!";//完全超過了20個字元的長度?????? printf("%s\n",str); char dec_str[20]; copy_string(str,dec_str);//在使用數組傳遞參數時,傳遞的是指向這個數組的指標變數 printf("%s\n",dec_str); return ;}
在Code:Blocks中運行,結果如所示。
4、指標與字串的關係
在C語言中可以採用兩種方式來定義和訪問一個字串,一種是數組,另外一種是指標。但是通過數組定義的字串是可以進行修改的,而通過指標定義的字串在記憶體中只具有可讀屬性,不能在其後的代碼中做任何的修改,只可以引用。
5、指標在使用時必須分配記憶體單元
不能直接對未經初始化的指標進行賦值操作,所以賦值之前需要使用指標指向一個可用的地址空間,否則指標的指向是不確定的,在運行時會直接崩潰而退出。
#include <stdio.h>void copy_string(char from[],char to[]){ /* 1、在為to參數傳遞指標變數時,並沒有為dec_str分配儲存單元 2、在改變to指標變數指向後,to指向了可用的記憶體單元,而dec_str還是保持了以前的指向 */ to=(char*)malloc(sizeof(char)*20);// 是為指標指向的目標變數分配記憶體的大小,如果超過了這個大小?? char *start_to=to; while(*to++=*from++); printf("copy_string函數:%s\n\n",start_to);}void main(){ char str[]="this is a string!";//完全超過了20個字元的長度 printf("main函數:%s\n",str); char *dec_str; printf("copy_string函數調用前:%s\n",dec_str); copy_string(str,dec_str); printf("copy_string函數調用後:%s\n",dec_str); return ;}
在採用指標定義字串時沒有顯式地為指標分配內在,而編譯哭喊在編譯過程中會在記憶體中為字串分配一個記憶體地區,同時將分配後的首地址傳遞給指標變數,因此在通過指標定義字串時不必為其分配記憶體空間。
在Code:Blocks中運行,結果如所示。
#include "stdio.h"#include <sys/time.h>// const關鍵字,在為一個變數加上const修飾符後,通過需要對它進行初始化,在之後的程式中就不能再去改變它int main(int argc,char * argv[]){ char *str="Hello"; char s[8]="fdsa"; printf("strlen(str)=%d\n",strlen(str)); // 為5,表示字串的長度 printf("sizeof(str)=%d\n",sizeof(str)); // 為4,因為在32 位的電腦中,指標佔用4個位元組的長度 printf("sizeof(*str)=%d\n",sizeof(*str)); // 為1,由於str指標指向字串的首地址,而*str是指向第一個字元,當然為1 printf("字元數組的長度:%d\n",strlen(s)); //----------- 輸出字元數組中的值---------------- int i; for(i=0; i<strlen(s); i++)// 不可以在for()中聲明int i=0,否則將出錯 { printf("%d\n",s[i]); printf("%c\n",s[i]); } //----------- 輸出字元數組中的值---------------- for(i=0; i<5; i++)// 不可以在for()中聲明int i=0,否則將出錯 { printf("%c\n",*str); str++; printf("%d\n",strlen(str));// 注意,如果指標移動了,那麼指標所指向的字串長度也會相應縮小 } return 0;}
6、指標的強制類型轉換
#include "stdio.h"int main(){ char *str="aaaabbbbccccdddd"; int *ptr; ptr=(int *)str; while(*ptr!='\0') { printf("%s\n",ptr); ptr++; } printf("%s\n",str);//字元指標 return 0;}
7、位元組對齊的操作
#include <stdio.h>#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)4)->MEMBER) // 位元組對齊的操作typedef struct stu1{ int a; int b;} stu1;void main(){ printf("offsetof(stu1,a):\%d\n",offsetof(stu1,a)-4); printf("offsetof(stu1,b):\%d\n",offsetof(stu1,b)-4); printf("%.2f",2.0); return ;}
#include <stdio.h>typedef char * va_list;#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))// 對齊位元組的操作????????????????#define va_start(ap,v) (ap=(va_list)&v+sizeof(v)) //先擷取變數V的地址,然後轉換為char類型的指標,再加上變數V所佔用的記憶體大小,使指標ap指向下一個參數#define va_arg(ap,t) (*(t *)((ap+=sizeof(t))-sizeof(t)))// 先將指標ap強制類型轉換,,然後減去當前參數的地址#define va_end(ap) (ap=(va_list)0) // 清除ap指標void print(int n,...)// 變參函數的實現就是參數壓棧採用的4位元組對齊{ int arg,i; va_list p; va_start(p,n);// 進行了壓棧的操作 for(i=0; i<n; i++)// 迴圈取出壓棧的資料 { arg=va_arg(p,int);// 當為arg=va_arg(p,char)時的情況為:只能取出一個A。因為壓棧是4位元組的操作,而取的時候為1個位元組,當然不行 printf("%c\t",arg); } printf("\n"); va_end(p); return ;}int main(){ print(4,'A','B','C','D'); return 0;}
8、使用關鍵字進行類型的定義
#include <stdio.h>typedef int (*print)(int );// typedef是關鍵字,使用關鍵字進行類型的定義也屬於一個語句,後面需要加分號int fun1(int i){ return i;}void fun2(int n,print ptr){ int i; for(i=0; i<n; i++) { printf("%d\t",ptr(i)); }}int main(){ int n=9; fun2(n,fun1); return 0;}
9、函數指標調用函數
#include <stdio.h>int max(int x,int y){ return x>y?x:y;}int min(int x,int y){ return x>y?y:x;}void main(){ int (*f)(int x,int y)=max;//定義了一個函數指標f,由於max或min代表函數的首地址,所以經過賦值後,指標f指向了函數代碼的首地址 printf("max(2,6)=%d\tf(5,4)=%d\n",max(2,6),f(5,4));// 指向函數的指標沒有自加或自減運算,這個需要注意 f=min; printf("min(2,6)=%d\tf(5,4)=%d\n",min(2,6),f(5,4));}
10、變參列表
#include "stdio.h"#include <stdarg.h>void print(int n,...){ int arg,i; va_list p;// typedef char* va_list; va_start(p,n); for(i=0; i<n; i++) { arg=va_arg(p,int); printf("%d\t",arg); } printf("\n"); va_end(p); return ;}int main(){ print(3,35,25,64); return 0;}
#include "stdio.h"void print(int n,...)// ...為函數的預留位置,是一個可變的參數{ int *p,i; p=&n+1; for(i=0; i<n; i++) printf("%d\t",p[i]); printf("\n"); return;}int main(){ print(4,24,25,64,66); char* str="Hello world!"; printf("%s\n",str); return 0;}
7、其它需要注意的問題
1、 sizeof可以對一個運算式求值,但是不會對錶達式進行計算。當參數為函數時,得到的僅是函數體的傳回型別所佔用記憶體單元的大小,而函數體並不執行
2、沒有分配記憶體的指標變數指向的是不可使用的記憶體地區,所以如果要對指標變數進行初始化,必須先在記憶體單元中為其分配一塊可用的記憶體地區
3、對於定義的枚舉類型,不管它包含多少個枚舉常量,所佔用的記憶體大小都為4個位元組,與所使用的整型不同??
4、不能在定義數組後再對數組名進行一次性字串賦值,而指標可以採用這種方式進行初始化.如果結構體中存在數群組類型的成員,可以利用strcpy()函數對數組進行初始化,只能實現字元數組的初始化
5、typedef是關鍵字,使用關鍵字進行類型的定義也屬於一個語句,後面需要加分號
6、%.2lf表示以後兩個小數點的長整數
7、共用體成員共用同一片記憶體地區
#include "stdio.h"#include <sys/time.h>// const關鍵字,在為一個變數加上const修飾符後,通過需要對它進行初始化,在之後的程式中就不能再去改變它int main(int argc,char * argv[]){ char *str="Hello"; char s[8]="fdsa"; printf("strlen(str)=%d\n",strlen(str)); // 為5,表示字串的長度 printf("sizeof(str)=%d\n",sizeof(str)); // 為4,因為在32 位的電腦中,指標佔用4個位元組的長度 printf("sizeof(*str)=%d\n",sizeof(*str)); // 為1,由於str指標指向字串的首地址,而*str是指向第一個字元,當然為1 printf("字元數組的長度:%d\n",strlen(s)); //----------- 輸出字元數組中的值---------------- int i; for(i=0; i<strlen(s); i++)// 不可以在for()中聲明int i=0,否則將出錯 { printf("%d\n",s[i]); printf("%c\n",s[i]); } //----------- 輸出字元數組中的值---------------- for(i=0; i<5; i++)// 不可以在for()中聲明int i=0,否則將出錯 { printf("%c\n",*str); str++; printf("%d\n",strlen(str));// 注意,如果指標移動了,那麼指標所指向的字串長度也會相應縮小 } return 0;}
#include "stdio.h"// 對判斷運算式的短路,也就是說有一部分會不執行// 無論對||還是&&都是一樣的// 對於float與double的比較,不能用!=或== 來比較,浮點運算存在精度問題,不能按照理論值來對其進行處理int fun(char *str){ printf("%s\n",str); return 0;}void main(){ int a; a=7; if(a>9&&fun("a=7")); a=10; if(a>9&&fun("a=10")); return ;}
#include "stdio.h"void main(){ double a=(double)6/5;// 先將6轉換為浮點類型,然後再將5隱式轉換為浮點類型後,為1.2 int b=(double)6/5;// 結果為1 printf("%f,%d",a,b); return ;}
#include "stdio.h"void main(){ unsigned short i=0; unsigned long n=0; while(i<n-1) { printf("進入了while迴圈!");// ??????? break; } printf("i<n-1=%u\n",i<n-1);// n-1是無符號的長整型,發生了類型的隱式轉換 int y=0; int k=-1; printf("%d,%u\n",k,n-1); return ;}
C語言分清的問題:
char str[100];
str[0]=='0'// 數組的第一個數為0字元
str[i]==0// 字串結束
str[i]==' '// 空格