c和指针

100 阅读15分钟

一、指针表达式

首先此处有个例程:此程序打印一下变量的数值,打印变量所分配的内存地址

int main()
{
        //abc这些变量都存在内存里 
        int a=100;//a4字节 
        int b=90;
        float c=3.14;//c是分配4个字节
        double d=2.2;//d是分配8个字节 
        char c1='a',c2='b';//char是分配一个字节 
        printf("a=%d\n",a);
        printf("b=%d\n",b);
        printf("c=%f\n",c);

        printf("通过打印地址可以计算出每个数据类型占据的字节位数"); 
        printf("pa=%d\n",&a);//打印a的地址-2293324
        printf("pb=%d\n",&b);//打印b的地址-2293320
        printf("pc=%d\n",&c);//打印b的地址-2293316
        printf("pd=%d\n",&d);//打印b的地址-2293304

        return  0;	
}

使用指针间接访问变量的值以及修改变量的值

int main()
{

        int a=100;//a4字节 
        printf("a=%d\n",a); //100 

        int *e;//e是一个指针,因为有*号,不可以保存普通的数据,是用来保存地址的 
        //e只能保存int类型的指针 ,而且一个指针永远占用4个字节 

        //e=200;//如果这样尝试赋值会报错
        e=&a;//e保存a的地址
        printf("a通过指针e间接访问a的数值\n"); 
    printf("a=%d\n",*e);//使用指针访问a并打印100

    printf("------地址打印------\n"); 
        printf("a_address=%d\n",&a);//通过取地址符打印a的地址
        printf("e-a_address=%d\n",e);//通过指针e打印a的地址
        printf("e_selfAddress=%d\n",&e);//其实e也是有自己的地址的 

        printf("----指针修改变量值-----\n"); 
        *e=88;//修改变量a的值
        printf("a=%d\n",a); //88
        printf("a=%d\n",*e);//88
        return  0;	
}

pa是指地址, * pa是指取这个 地址的内容 则&a是变量a的地址,*&a就是解引用a的值(即变量a)

int main ()
{
        int a=100;
        int *d=&a;//指针b指向变量a 
        printf("a=%d\n",a);//a=100
        printf("a=%d\n",*d); //a=100


        *d=10-*d;//后面*b是指a的值即10-100=-90 
        printf("a=%d\n",a);//a=-90

        //b=10-*b;//则会报错 (只有地址才可以赋值给变量b)

        //&a是变量a的地址,*&a就是a
        *&a=25;//对a的值进行修改 
        printf("a=%d\n",a);//a=25 

        printf("a的地址:%d\n",&a);//成功打印出a的地址
        printf("a的地址:%d\n",d);//实际打印的是a的地址
        printf("指针d本身的地址:%d\n",&d);//实际打印的是d本身的地址

 } 

使用指针的指针间接访问变量的值(**pa)

int main ()
{
        int a=100;
        int *b=&a;	//定义一个指针b 
        int **c; //c是指针的指针(指向指针的指针) 
        c=&b; //c里面存放的是b的地址,b里面又是a的地址 

        //由于c指b的地址,*c指b地址里面的内容(而b的内容是存放 a的地址)
        //此时要想通过c间接访问a就再加一个*号 
        **c= 48;//对a的数值进行修改 
        printf("a=%d\n",**c);// 48
        printf("a=%d\n",a);// 48
 } 

G

*cp+1:
1、先运算*cp,再进行+1操作
2、首先是取的对应变量的值,然后再对这个数值进行+1操作
*(cp+1)
先运算cp+1(即指针后移一个),
再运算*(cp+1)(即取下一个变量所对应的数值)

在进行指针运算的时候打印结果可能不和自己预想的一样,主要原因在于在给变量分配内存地址时可能顺序发生了错乱,所以此时可以去通过打印每个变量的地址去判断自己的程序是否正确。

 #include<stdio.h>
 char c1,c2,c3;
 char *cp;
 void setup()
 {
        c1='a';//ASCII码值97
        c2='b';//98
        c3='c';//99
        cp=&c1;//cp存放c1的地址
        printf("----三个变量内存地址如下---\n");
        printf("c1=%d\n",&c1);
        printf("c2=%d\n",&c2);
        printf("c3=%d\n",&c3);
        //三个变量地址如下 
        //c1(97):***77
        //c2(98):***76
        //c3(99):***78 
 }
 int main() 
 {
        setup();//调用函数 
        printf("c1=%d ,c2=%d ,c3=%d\n",c1,c2,c3);//97,98,99
        printf("---下面1,2地址是相同的--- \n");
        printf("%d\n",&c1);//这是c1的 地址
        printf("%d\n",cp);//这是cp中所保存的地址(c1的)
        printf("%d\n",&cp);//这是 cp自身所分配的地址
        printf("-------------------------\n") ;
        printf("%d\n",*cp);//这是间接访问cp所指向的c1的字符-->97
        printf("%d\n",*cp+5);//这是先取cp指向的c1字符的ascii码,然后ascii值再+5-->102
        printf("%d\n",cp);//cp此时指向的地址(还是c1),得以证明上面只是对最终结果进行了加法操作,实际的指针指向变量并没有发生变化
        //下面之所以没有打印c2('b')的ascii码 是因为在变量分配内存顺序的时候是c2,c1,c3 
        printf("%d\n",*(cp+1));//cp原来指向c1('a'字符),指针+1,指向下一个字符c3('c')-->99 	 

 }

++cp:指针指向的变量地址发生了变化(指向了下一个地址) cp++:当前使用cp仍然还是指向原来的变量,在第二次使用cp时才指向下一个地址 *++cp:指针先指向下一个地址(新的变量),再取对应地址上变量的值

 #include<stdio.h>
 char c1,c2,c3;
 char *cp;
 void setup()
 {
        c1='a';//97
        c2='b';//98
        c3='c';//99
        cp=&c1; 
 }
 int main() 
 {
        setup();
    //c1(97):地址***77
        //c2(98):地址***76
        //c3(99):地址***78
    printf("%d\n",cp);//这是指针指向变量的地址-->*****77 
        printf("%d\n",++cp);//cp里面是c1的地址,前++是在执行以前指向了下一个字符,此时会打印下一个(c3)的地址-->***78

    printf("---------------\n");
    //为了防止上面对下面产生影响,在重新调用一下setup()
        setup(); 
        printf("%d\n",cp++);//后++ 是在print运行完以后才指向下一个字符(故输出的还是c1的地址)-->***77
        printf("cp++=%d\n",cp);//打印一下后++指针指向的位置(指向了下一个)
        printf("----------------\n");
        setup();//重新加载函数 
        printf("%d\n",cp);//此时指针cp指向于c1 
        printf("%d\n",*++cp);//cp原来指向c1,先运算++cp指针地址发生变化指向了下一个字符(c3),然后在取变量的值 
        printf("%d\n",cp);//打印一下cp此时的地址(c3) 	 
 }

*++cp:指针先指向下一个地址(新的变量),再取对应地址上变量的值 cp++:本次使用指针cp时,指针位置没有发生变化,下次再调用时指针位置指向了下一个地址 ++ * cp:先取值,再对结果+1 ( * cp)++:先取值打印,下次打印cp的值后数值+1,但是指针实际指向的位置没有发生变化 ++ * ++cp:指针先指向下一个地址,在取值,然后再对结果+1 ++*cp++:cp++指针指向了下一个地址(但下次才起作用),取值(cp本来指向的变量),再对数值进行+1操作,,下次在打印cp地址和 *cp的值时均是下个地址的变量

#include<stdio.h>
 char c1,c2,c3;
 char *cp;
 void setup()
 {
        c1='a';//97
        c2='b';//98
        c3='c';//99
        cp=&c1; 
 }
 int main() 
 {
        setup();
        printf("---地址打印----\n");
        printf("c1=%d\n",&c1);//这是c1的 地址
        printf("c1=%d\n",cp);//这是cp中所保存的地址
        printf("c2=%d\n",&c2);
        printf("c3=%d\n",&c3);
        //三个变量地址如下 
    //c1(97):***77
    //c2(98):***76
    //c3(99):***78
    printf("-------1--------\n");
    printf("%d\n",cp);//此时指针指向于c1 
        printf("%d\n",*++cp);//cp原来指向c1,先运算++cp指向了下一个字符(c3),然后再间接引用取得数值-->99 
        printf("%d\n",cp);//再次验证地址也的确指向了c3
        printf("-------2--------\n"); 
        setup();
    printf("%d\n",cp);//先看一下此时cp指向的地址 
    printf("%d\n",*cp++);//输出97,由于cp++下一次执行才有效,所以实际打印的还是原来的地址的内容 
    printf("下一次打印的数据:%d\n",*cp);//此时打印的是c3
    printf("下一次打印的地址:%d\n",cp); //此时指针指向的地址已经从c1指向了c3,得以验证
    printf("-------3--------\n");
        setup();
        printf("%d\n",cp);//先看一下此时cp指向的地址 
        printf("%d\n",++*cp);//先执行了*cp取了第一个变量的ascll码,然后ascll码又加了1 -->98
        printf("%d\n",cp);//虽然上面打印时数字发生了变化,但是实际的指针指向地址并没有发生变化
        printf("-------4--------\n");
        setup();
        printf("%d\n",cp);//先看一下此时cp指向的地址 
        printf("%d\n",(*cp)++);//因为先执行解引用,执行完之后才对其+1操作,所以此时打印的仍然是cp所指向的第一个变量的值(97)
        printf("%d \n",*cp);//cp指向的地址没有发生变化,但是*cp解引用的值却是+1
        printf("%d\n",cp);//得以已验证指针没有移动 
        printf("-------5--------\n");
        setup();

        //此处由于编译器分配地址的原因,打印结果为100:
        //因为分配地址的顺序是c1=97,c3=99,c2=98 
        //所以在出现不一样的结果的时候,可以尝试打印一下变量的地址   
        printf("%d\n",cp);//先看一下此时cp指向的地址(c1)
        printf("%d\n",++*++cp);//执行指针加1操作(指向c3),此时解引用(c3的值99),然后又对99这个数字进行了+1变成了100 
    printf("%d\n",cp);//此时可以顺便检验一下指针cp指向的地址指向C3

    printf("-------6--------\n");
    setup();
        printf("%d\n",cp);//查看此时的地址(c1) 
        printf("%d\n",++*cp++);//cp++下次才起作用,先取出c1的值97,又对97这个数值进行了+1变成了98(打印结果)
        printf("%d\n",cp);//得以证明上一步的cp++起了作用,指针已经指向了下一个变量(c3) 
        printf("%d\n",*cp);//打印c3的ascii码值99 
 }

二、实例

1、计算字符串长度 使用原有的库函数计算字符串长度

 #include<stdio.h>
 #include<string.h>
 int main()
{
        char *name="abcd gates";
        int length=strlen(name); 
        printf("长度为:%d\n",length);//字符串长度是10	
}

写一个类似计算字符串长度的函数

#include<stdio.h>
#include<string.h>
 //参数是一个字符指针,开始时指向了第一个字符,直到遇到'\0'就算结束 
 //c语言的字符串在结束时默认有一个'\0' ,但是不会计算到字符串长度中 
 int my_strlen(char *string) 
 {
        int length=0;//记录长度 
        //string指向字符串第一个字符,从字符串第一个字符开始遍历
        //只要不到'\0'(字符串结束标志)就继续遍历 
        for(;*string!='\0';string++)//或者简化为for(;*string++!='\0';)
        {
                length++;//字符串长度+1 
        }
        return length;
 }
 int main()
 {
        //用字符指针指向一个字符串,指向的是第一个字符 
        char *name="abcd gates";
        int len=strlen(name);//这是使用库函数测得字符串长度 
        printf("库函数测得字符串长度为:%d\n",len);//10
        printf("----------------------\n"); 
        char *str="hello";//定义新的字符指针存放字符串 
        int my_len=my_strlen(str);//调用写的计算字符串长度的函数 
        printf("新的字符串长度:%d\n",my_len);//5 
 }

上面计算字符串长度的函数中for循环也可替换为while

  while(*string!='\0')
  {
	length++;
	string++;	
  }	
 // 或者简化为 
   while(*string++!='\0')
	{
		length++;	
	}	

使用下标形式也可以,代码如下:

#include<stdio.h>
#include<string.h>
int mystrlen(char *str)//参数值指针类型 
{
        int count=0;//计数使用 
        int i=0;//下标 
        while(str[i]!='\0')//直到最后一个字符是'\0'才结束运行 
        {
                count++;
                i++;
        }
        return count;//返回计数长度 

}

int main()
{
        char *m="abcdef ghijk";//这是字符串内容 
        int a=mystrlen(m);//传递的是字符串的首地址 
        int b=strlen(m);//使用官方函数直接计算字符串长度 
        printf("字符串的长度是(mystrlen)=%d\n",a);//12
        printf("字符串的长度是(strlen)=%d\n",b);//12
}

2、在一组字符串中看是否能查找到一个字符 方法一:

#include<stdio.h>
#include<string.h>
int my_find(char *arr,int ch,int len)
{
        int i=0; 
        for(;i<len;i++)
        {
                if(arr[i]==ch)
                {
                        return i;
                }
        } 
        return 0;//没有找到 
} 
int main()
{
        char *a="how are you!";//这是字符串内容
        char ch;//这是需要查询的字母
        int str_len=strlen(a);//计算字符串的长度 
        int result;//这是查询的结果,如果查询到会返回1,查不到返回0 
        scanf("%c",&ch);//输入要查询的字符

        result=my_find(a,ch,str_len);//传递参数 

        if(result==0)
                printf("您要查找的字符不存在") ;
        else
                printf("您查找的字符存在,下标为:%d\n",result);	
 }

方法二:减少传递参数的个数

#include<stdio.h>
#include<string.h>
int my_find(char *arr,int ch)
{	int i=0;
        for(;*arr++!='\0';)
        {
                i++;//下标累加 
                if(*arr==ch)
                {
                        return i;
                }
        } 
        return 0;//返回0表示没有找到 
} 
int main()
{
        char *a="how are you!";//这是字符串内容
        char ch;//这是需要查询的字母
        int str_len=strlen(a);//计算字符串的长度 
        int result;//这是查询的结果,如果查询到会返回1,查不到返回0 
        scanf("%c",&ch);//输入要查询的字符

        result=my_find(a,ch);//传递参数 

        if(result==0)
                printf("您要查找的字符不存在") ;
        else
                printf("您查找的字符存在,下标为:%d\n",result);	
 }  

方法三:将for循环换为while循环

#include<stdio.h>
#include<string.h>
int my_find(char *arr,int ch)
{	
        int i=0;
        while(*arr!='\0')
        {	
                if(*arr++==ch)
                {
                        return i;	
                }
                i++;
        } 
        return 0;

} 
int main()
{
        char *a="how are you!";//这是字符串内容
        char ch;//这是需要查询的字母
        int str_len=strlen(a);//计算字符串的长度 
        int result;//这是查询的结果,如果查询到会返回1,查不到返回0 
        scanf("%c",&ch);//输入要查询的字符

        result=my_find(a,ch);//传递参数 

        if(result==0)
                printf("您要查找的字符不存在") ;
        else
                printf("您查找的字符存在,下标为:%d\n",result);	
 }

方法四:使用指针的指针

  #include<stdio.h>
 //在一组字符串中查找一个字符,如果找到返回1;反之返回0 
 //参数1是一个指针的指针 
 int find_char(char **stringss,char value)
 {
        char *string;
        //第一个while循环遍历数组的每个元素 
        while((string=*stringss)!=NULL)
        //把*string指针放入前面的指针里面(即数组每个元素的地址),NULL是最后一个元素 
        {
                stringss++;//指针后移(遍历数组下一个元素地址)
                //这个while遍历每个元素中字符串的字符,由于string指针存放了字符串首地址
                //*string则是取了对应地址的内容 
                while(*string!='\0')
                //每个指针字符串也是以‘\0’结尾,遍历到'\0'表示这个字符串结束
                {
                        if(*string == value)//value='K'(想要搜索的字母 ) 
                                return 1;//如果匹配到了则返回1 
                        string++;//在字符串中指针后移指向此字符串中下一个字符 
                } 

        }
        return 0;//没有匹配到相关字母  
 }
 int main()
 {

        //数组strs里有五个字符指针,每一个字符指针都指向一个字符串  
        //数组的名称就是指针 
        char *strs[5];//这是一个指针数组,里面每个都指向字符串的首地址
        //先准备几个字符指针,其实每个字符串后面会自动添加'\0' 
        char *s1="A string";
        char *s2="Another";
        char *s3="Third";
        char *s4="Last";
        char *s5=NULL;//当遍历到这里的时候表示数组结束了
        //将字符指针放入数组中 
        strs[0]=s1; 
        strs[1]=s2;
        strs[2]=s3;
        strs[3]=s4;
        strs[4]=s5;//是个null指针或直接写strs[4]=NULL 
        //其实数组里面存放的是上面每个字符指针的地址,
        //*strs[i]是找到了每个下标的地址,**strs[i]才是开始取下标中的内容 

        //参数1:strs是一个指针的指针 
        //find_char(strs,'K');//在里面寻找有木有这个字符 ,数组名strs就是一个指针,可以直接传递
        if( find_char(strs,'K'))//如果找到之后函数返回1则为真
        {
                printf("找到了"); 
        }
        else
        {
                printf("没找到");
        } 	
 }

三、算数运算:只限于两种

(1) 指针加或减一个整数---->也就是指针所指向的内存地址发生变化(即指向的变量发生变化),指针地址前移或者后移 char:指针+1,地址+1 int:指针+1,地址+4 float:指针+1,地址+4 指针+1,所加的地址是对应数据类型的字节数大小 (2)指针-指针 :是查看两个指针之间相差几个

a.对数组操作时是最好的,因为对数组操作变量在分配地址时是连续的,不会出现跳跃地址的现象 b.如果使用普通变量,在修改和编写过程中,分配地址经常出现不连续的现象,观察起来不是那么直观明显

 #include<stdio.h>
 int  main()
 {
        //a,b,c,d 的ascii码值为97,98,99,100 
        char c1='a',c2='b',c3='c',c4='d';
        float f1=1.31,f2=2.23,f3=3.33;
        char *pc1;
        char *pc2;
        //接下来用来打印一下 c1,c2,c3,c4在内存中的地址(为了下面的操作做准备) 
        printf("%d\n",&c1);
        printf("%d\n",&c2);
        printf("%d\n",&c3);
        printf("%d\n",&c4);
        //经过地址打印结果可知
        //c1=****95
        //c2=****94
        //c3=****93
        //c4=****92
        //如果所分配内存先后顺序不是连续的或者不符合上面的地址顺序,下面所打印信息也是不唯一的
        //所以具体的打印结果根据此时分配内存而决定,但是整个思路是没问题的 
        printf("----------\n");
        pc1=&c2;//把c2所在的地址给pc1
        pc1++;//此时指针发生后移指向了c1变量
        printf("pc1_address=%d\n",pc1);//打印一下此时pc1所指向的地址(c1的)
        printf("*pc1=%d\n",*pc1);//打印指针 所指向变量的值(c1=a的ascii) 
        printf("***************\n");
        pc2=&c3;
        pc2-=1;//此时应该指向 c4变量
        printf("pc2_address=%d\n",pc2);//打印一下此时pc4所指向的地址
        printf("*pc2=%d\n",*pc2);//打印指针所指向变量的值 
        printf("-----------\n");


        //打印一下float变量的地址 
        printf("f1=%d\n",&f1);
        printf("f2=%d\n",&f2);
        printf("f3=%d\n",&f3);
        //f1=****304
        //f2=****300
        //f3=****298
        float *pf;
        pf=&f1;
        pf-=2;//此时指针-2,但是地址-8(float是4个字节)
        printf("*pf=%f\n",*pf);//打印数值(f3的值3.3000)
        printf("pf_address=%d\n",pf);//此时指针指向f3,打印的地址也是f3的地址 	
 }

四、指针和函数

1、在数组中查找一个元素 函数的返回值是指针 int *find_init(…)

#include<stdio.h>
 //返回值是指针 ,指针是个地址 
 int *find_init(int key,int arr[],int len)
 {
        //遍历数组中的数
         int i; 
        for(i=0;i<len;i++)
        {
                if(arr[i]==key)
                {
                        return &arr[i];//此处返回的是一个 地址 
                }
        } 
        return NULL;//没有找到则返回NULL 
 }
 int main()
 {
        int a[10]={1,3,5,7,9,2,4,6,8,10};

        int *result;//定义一个指针存放函数的返回值 

        result=find_init(6,a,10);//函数返回结果是一个指针(指针是一个地址) 

        if(result==NULL)//注意不是*result
        {
                printf("没有找到\n"); 
        } 
        else
        {
                printf("找到了:%d \n",*result); //可以找到这个数
                *result=*result*10;//也可以通过指针去修改这个数(这个数扩大了10倍) 
        } 
        printf("打印一下修改后的数组\n");
        int i;
        for(i=0;i<10;i++)
        {
                printf("a[i]=%d\n",a[i]);
        } 
 }

2、交换两个变量的值 函数的参数是指针 void swap(int *x,int *y) 例程:交换两个变量的值 更多请点击c语言中交换两个变量的值

#include<stdio.h>
void swap(int *x,int *y)
{
        int temp;
        temp=*x;//参数1的值先给中间变量temp
        *x=*y;//参数2的值给参数1
        *y=temp;//再把中间变量的值给参数2 
} 
int main()
{
        int a=10,b=5;
        printf("交换前:a=%d,b=%d\n",a,b);
        swap(&a,&b);
        printf("交换后:a=%d,b=%d\n",a,b);
        return 0;

}