標準c基礎知識六
寫一個宏函數,給一個數組排序,可以通過參數來決定是按照從大到小還是從小到大排序如SORT(a,10,<);SORT(b,8,>)
//打印出自己
vi printself.c
#include <stdio.h>
int main()
{
char* s="#include <stdio.h>%cint main()%c{%c%cchar*s=%c%s%c%c%cprintf(s,10,10,10,9,34,10,9,10,9,10,10);%c%creturn 0;%c}%c";
printf(s,10,10,10,9,34,s,34,10,9,10,9,10,10);
//換行為10,
return 0;
}
指針pointer 地址:保存地址的變量
指向point to
#include <stdio.h>
int main()
{
int a[5]={11,22,33,44,55};
int* p;//野指針,胡亂指向的指針;避免野指針,使用空指針代替
printf("p=%p\n",p);
int* q=0;//如果還沒讓它指哪里可以設置為0;在這里表示空地址,等價于null;空指針;邏輯指針;邏輯地址的假
//可以寫成0,'0',NULL,3>5(邏輯假)
#define T int*
union { //為什么要用聯合呢?
T x;
char b[sizeof(T)];
}u;
u.x=q;
for (i=0;i<sizeof(T);i++){
printf("%d ",u.b[i]);
}
printf("\n");
//*p=100;可能導致非法內存訪問
p=a;//數組當作數據用時表示第一個元素的地址;剛好一致
//看以下地址運算
for (i=0;i<5;i++) printf("%d ",a[i]);printf("\n");
for(i=0;i<5;i++) printf("%d ",*(a+i));printf("\n");
for (i=0;i<5;i++) printf("%d ",p[i]);printf("\n");
for(i=0;i<5;i++) printf("%d ",*(p+i));printf("\n");
printf("%d\n",(p+2)[1]);//*((p+2)+1)==>*(p+3)==>p[3]
//在地址運算時,p+1(1在這里代表的是元素的實際長度,如int,32位長度)方括號直接當作加號即可
q=p+3;//在這里相當于p[3]
printf("%d\n",1[q]);//1[q]與q[1]是等價的;
printf("p=%p,q=%p,q-p=%d\n",p,q,q-p);
for(i=0;i<5;i++) printf("%d ",*p+i);printf("\n");//輸出11 12 13 14 15
for(i=0;i<5;i++) printf("%d ",*p++);printf("\n");//輸出11 22 33 44 55
return 0;
}
深入理解指針,
vi suspend.c
#include <stdio.h>
char * func()
{
char a='#';
return &a;//不要返回普通局部變量的地址!
}
int main()
{
char c='@';
char * p=&c;
*p='$';
printf("*p=%c\n",*p);
p=func();//指向一個可能已被釋放的地方,懸空指針
printf("c=%c\n",c);
printf("*p=%c\n",*p);//輸出可能不是'#'了
return 0;
}
#include <stdio.h>
void f1(int * p,int *q){int *t=p;p=q;q=t;}
void f2(int *p,int *q){int t=*p;*p=*q;*q=t;}
void f3(int a,int b){int t=a;a=b;b=t;}
int main()
{
int a=10,b=20;
//int t=a;a=b;b=t;
//int *p=&a,q=&b;//警告初始化將指針賦給整數,未作類型轉換;在C++中會直接報錯
//如果在一個語句中,定義多個變量時,只有第一個int是公用的。所以最好是按照以下方式來定義
//int *p=&a;
//int *q=&b;
int x,y[5],*p=&a,*q=&b;
int t=*p;*p=*q;*q=t;//一定要帶*
printf("a=%d,b=%d\n",a,b);
int m=10,n=20;
int *u=&m,*v=&n;
int *w=u;u=v;v=w;//u指向n,v指向m;而m和n并沒有變
printf("m=%d,n=%d\n",m,n);
f1(&m,&n);printf("m=%d,n=%d\n",m,n);//m,n的地址不變
f2(&m,&n);printf("m=%d,n=%d\n",m,n);//地址傳遞,通過地址可以間接的訪問原始變量
f3(m,n);printf("m=%d,n=%d\n",m,n);
//形參永遠是實參的復制品;如果是地址傳遞可以間接的訪問原始變量
return 0;
}
vi array.c //看一下,數組作為形參的時候,是真的作為數組嗎?
#include <stdio.h>
void show(double a[],int n)//a是披著數組皮的指針
{
double x=123.45
printf("sizeof a=%d\n",sizeof(a));
a=&x;
printf("*a=%g\n",*a);//由此可以看出是一個指針
}
void print(double *p,int n)
{
int i;
for (i=0;i<n;i++)
printf("%g ",p[i]);
printf("\n");
}
int main(0
{
double a[5]={1.1,2.2,3.3,4.4,5.5};
show(a,5);
print(a,5);
return 0;
}
*****************************************************************************************
指針就是保存地址的變量;地址只能由指針來保存;指針只能保存地址;
int * k;//表示k是一個變量,是int *類型
數組名只是用來保存第一個元素的地址
vi string.c
#include <stdio.h>
#include <string.h>
int main()
{
char a[100]={'h','e','\0','w','o'};
"helloworld";//const char[11]
//以上兩種方式來表示字符串
puts(a);
puts("helloworld");
char *p=a;
printf("%c\n",*p);
*p='w';
puts(a);
p="helloword";//有的編譯器會警告
printf("%c\n",*p);//在這里已經輸出為c
//*P='w';//因為前面"helloworld"已經放到只讀存儲里面,再寫的話發生段錯誤;編譯通過運行錯誤
//如何解決這個問題呢?
const char* q=NULL;//*左邊的const char表示不會通過指針修改目標的數據;
q="helloworld";//一定是安全的
//*q='w';//編譯的時候就能檢查出錯誤
p=a;//在這里也是賦值的地址
strcpy(a,"nb");
puts(p);
q=a+3;//一定是安全的
//*q='w';//在編譯的時候會報向只讀位置寫入數據
puts(q);//輸出wo
//char const * r;===>等價于const char * r;
char * const r=a+1;//表示r是一個常量;與上述兩種方式不同
puts(r);
//r=a;//編譯錯誤
*r='A';
puts(a);//輸出NA
char * str;//野指針
scanf("%s",str);//dangerous
strcpy(str,"hello");//dangerous
return 0;
}
//上例中;q="helloworld";只是把字符串的首地址(第一個字符的地址)賦給了指針;并不是賦值了整個字符串
//const地方不一樣,其含義不一樣
vi strings.c
#include <stdio.h>
int main()
{
char * names[9]={"luko","kooo","look","kogo","hehehe","kaka","lalala","wowo","eeee"};
int i;
for (i=0;i<9;i++)
printf("%s!\n",names[i]);
const int a=10,b=80,c=20,d=60,e=98,f=76;
//通過指針來對以上數據排序輸出
const int *p[6]={&a,&b,&c,&d,&e,&f};//每個元素是一個指針,int指針
int j;
for (i=0;i<6;i++){
for(j=j+1;j<6;j++{
if(*p[j]<*p[i]){
const int *t=p[j];p[j]=p[i];p[i]=t;//上面在定義指針時用的const
}
}
}
for (i=0;i<6;i++){
printf("%d ",*p[i]);
}
printf("\n");
return 0;
}
ls -t -F
ls -t
ls -F
那么我們做的程序如何能類似于ls一樣怎么加參數選項呢?
vi cmdline.c
#include <stdio.h>
int main(int argc,char * argv[])
//接受命令行字符串的個數,傳到main函數;sh會把每個字符串的開始地址存到這個數組里面
//
{
printf("argc=%d\n",argc);
int i;
//if(argv[0]!="thanks"){//由于argv[0],"thanks"體現的是地址,要想對比必須采用strcmp
if(strcmp(argv[0],"thanks")==0){
printf("請使用正版\n");
}
for (i=1;i<argc;i++){
printf("%d:%s\n",i,argv[i]);
//注意命令行都是字符串,即使顯示的數字"12","34"也是字符串。
}
return 0;
}
#include <stdio.h>
int main()
{
char c;
char * pc;
int n;
int * pn;
short s;
short * ps;
double d;
double * pd;
printf("&c=%p\n",&c);
printf("&pc=%p\n",&pc);
printf("&n=%p\n",&n);
printf("&pn=%p\n",&pn);
printf("&s=%p\n",&s);
printf("&ps=%p\n",&ps);
printf("&d=%p\n",&d);
printf("&pd=%p\n",&pd);
return 0;
}
int ** 二級指針
#include <stdio.h>
#include <ctype.h>
int str2int(const char* str,const char** q)
{
int r=0;
while(isdigit(*str)){
r=r*10+*str-'0';
++str;
}
*q=str;
return r;
}
int main()
{
const char* p=NULL;
int n=str2int("3926abxys",&p);
printf("n=%d,p=%s\n",n,p);
return 0;
}
#include <stdio.h>
void showbytes(void * addr,int bytes){//void *,沒有類型的地址
while(bytes-->0)
printf("%02x ",*(unsigned char*)addr++);//%02x,表示不夠2位用0來填充
printf("\n");
}
int main()
{
int n=1234567890;
float f=1234567890;
double d=1234567890;
short s=1234567890;
//printf("%x,%hx\n",n,s);
showbyte(&n,sizeof(n));
showbyte(&n,sizeof(f));
showbyte(&n,sizeof(d));
showbyte(&n,sizeof(s));
return 0;
}
寫一個函數用來檢查一個字符串是否是數字字符串。
isdigitstr("123"):1,isdigitstr("asfw"):0,isigitistr("12f"):0
寫一個函數用來檢查一個字符串是否是實數字符串。
isreal("12.3"):1,isreal("-45"):1,isreal("as"):0,isreal("1.w"):0
寫一個函數用來把一個字符串用指定的字符作為分隔符分割成若干個子串輸出
substr("abc:de:fghi:jk",':')輸出
abc
de
fghi
jk
寫一個函數,用來返回一個字符串中重復出現的最長字串的長度及其開始地址。
const char *p =NULL;
int len=maxsubstr("qweohiuweyowohifpw",&p)
printf("len=%d,substr=%s\n",len,p)
輸出len=3,substr=ohi
指針:類型 *(*號表示什么什么地址的意思)==》合在一起稱為一個新的類型
類型* p()===>表示一個函數
類型* p[]===>表示一個數組,每個元素是什么什么地址
類型 *p===>表示一個指針
int *p[5];//p首先是一個數組;5個元素,每個元素是int *類型
int *p(double);//p是一個函數,形參double,返回類型int *
int(*p)[5];//p是一個指針,指向5元素的int數組
函數指針---》指向函數的指針
數組指針--》指向數組的指針
vi arrayptr.c
#include <stdio.h>
int main()
{
int * a[5];
int *f(double);
int (*p)[5];
int x[5]={11,22,33,44,55};//x是一個5個元素的int數組
int m;//m是一個int變量
int *n;//n是一個指針,指向int
//n=m;//x
n=&m;//
int y[6]={12,23,34,45,56,67};
int z[8][5];
//p=x;//x
p=&x;
//只有x表示&x[0],int地址;用&x表示數組的地址;一個指針指向數組,表示的是數組的地址,而不是第一個元素的地址
//int(*)[5]類型
int i;
for (i=0;i<5;i++){
printf("%d ",(*p)[i]);
}
printf("\n");
p=&y;//X,&y的類型是int(*)[6],不一致
p=z;//z數組名表示z[0]的地址,是一個int(*)[5]的地址
return 0;
}
vi funcptr.c
#include <stdio.h>
void funcr(int n)
{
printf("你今年%d\n",n);
}
void chunge(int n)
{
printf("腰圍%d\n",n);
}
int main(int argc,char* argv[])
{
if(argc<1) return 0;
int *f(char);//f是一個函數,形參char類型,返回int*類型
int (*p)(char);//p是一個指針,指向一個形參char返回int的函數
int(*q)();q是一個指針,指向一個形參任意返回int的函數
printf("&main=%p\n,%p\n",&main,main);
//p=&main;//類型不一致
q=&main;//可以
(*q)();//一直循環執行main函數
if (argc>0)
(*q)(argc-1,argv);
//q(argc-1,argv);//等價上面;因為函數名就是函數地址
//q=func;//返回類型不一致
void(*fp)(int n);
fp=functr;
fp(18);
fp=chunge;
fp(19);
//以上運行正常
return 0;
}
//函數地址是不能進行加減運算的(不能做數學運算,無任何意義)
//函數地址意義:調用函數和賦值
//函數名實際上就是函數的地址
vi foreach.c----
#include <stdio.h>
void print(int *P){printf("%d ",*p);}
void add(int *p){*p+=10;}
void clear(int *p){*p=0;}
void fill(int *p){
static int n=0;//初始化值只用一次
*p=++n;
}
void foreach(int a[],int n,void (*fp)(int*))//理解:int a[],int n,void
{
int i;
for(i=0;i<5;i++)
{
(*fp)(a+i);//等價于fp(a+i);
}
}//為每一個元素做什么事。
void mul(int *p){*p *=10;}//*p*,這里2個*表示的意思不一樣,第一個*通過地址尋找變量,第2個*是表示乘號
int main()
{
int a[5];
int i;
foreach(a,5,fill);//for(i=0;i<5;i++) fill(a+i);
foreach(a,5,add);//for(i=0;i<5;i++) add(a+i);
for(i=0;i<5;i++){
print(a+i);
}
printf("\n");
for(i=0;i<5;i++) clear(a+i);
foreach(a,5,mul);
return 0;
}
函數指針
如何做一個通用函數(把另外一個函數作為參數傳進來)如上例
********************************************************************************
vi sort.c
#include <stdio.h>
int rule1(double lh,double rh)//要求實現左小右大
{
//return(1h>rh);
if(1h>rh)
return 1;
else
return 0;
}
//程序對排序規則的約定:不需要調整順序就返回0,需要調整順序就返回1
int rule2(double 1h,double rh){return (rh>1h);}//按照從大到小排序
#define swap(x,y){double t=x;x=y;y=t;}
void sort(double a[],int n,int (*p)(double,double))
{
int i,j;
for(i=0;i<n;i++)
for(j=j+1;j<n;j++)
if(p(a[i],a[j])==1)
swap(a[i],a[j]);
}
//int (*x(void(*p)()))(char);//x是一個函數,其形參為void(*p)();p表示的是一個指針,返回類型void,形參任意
//等價于以下
//typedef void (*T)();
//typedef int (*U)(char);
//U x(T);
void input(double a[],int n)
{
printf("請輸入10個小數:");
int i;
for(i=0;i<10;i++)
scanf("%lf",a+i);//&a[i]
}
void show(double a[],int n)
{
int i;
for(i=0;i<n;i++)
printf("%g ",a[i]);
printf("\n");
}
int main()
{
double a[10];
input(a,10);
sort(a,10,&rule1);
show(a,10);
sort(a,10,&rule2);
show(a,10);
return 0;
}
--------------------------------------------------------------------------------
通過指針來訪問結構變量
vi structptr.c
#include <stdio.h>
typedef unsigned short int uint16;
typedef struct date{
//unsigned short int year;
uint16 year;
uint16 month;
uint16 day;
}date;
void print(date const *p)//使用p可以避免通過p傳遞數值時不修改其數值
{
printf("%d年%d月%d日 ",(*p).year,(*p).month,(*p).day);//為什么*p必須要加上(),主要是因為.號優先級比較()稍微低一點
//printf("%d年%d月%d日 ",p->year,p->month,p->day);//與上行等價,簡化寫法
}
int main()
{
date a[3]={{2011,8,18},{2012,2,13},{2012,5,16}};
int i;
for(i=0;i<3;i++)
print(&a[i]);//或者print(a[i]);
return 0;
}
//在C語言中,傳遞結構變量時總是傳遞地址
通過指針訪問堆區
理解堆區:有借有還再借不難
棧區:有借無還(容易累積用完了)
malloc分配內存空間
void * malloc(size_t bytes)
memset(地址,值,字節數);意思是從某個地址開始多少字節內都設置成某個值//手工清0
void * calloc(size_t nmemb,size_t size)//會把分配的內存空間清0
void * realloc(void *ptr,size_t size)//還回舊的,申請新的;void *ptr為舊空間的地址;保證舊空間的數據會復制到新空間中,加長的空間不保證清0
內存的分配和釋放一定要配對
內存泄漏 memeory leak
vi heap.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
int b=sizeof(double);
double * p=(double *)malloc(b);//malloc返回為void,所以必須強制double *
int cnt;
printf("請輸入元素的個數:");
scanf("%d",&cnt);
int * a=calloc(cnt,sizeof(int));//分配一個數組;保證5個元素一定會初始化成0;
if (a==NULL){
printf("申請空間失敗\n");
return 1;
}
printf("p=%p,a=%p\n",p,a);
*p=123.45;
int i;
for(i=0;i<cnt;i++)
a[i]=i+10;
printf("%g\n",*p);
for(i=0;i<cnt;i++)
printf("%d ",a[i]);
printf("\n");
//釋放空間
free(p);
a=realloc(a,sizeof(int)*10);
for(i=0;i<10;i++)
printf("%d ",a[i]);
printf("\n");
a=realloc(a,0);
printf("a=%p",a);
return 0;
}
在設計的時候,一定要考慮好內存的分配和釋放。
指針本身保存的是地址