第十八章-C语言进阶(一)

指针强化

  1. 指针也是一种变量,占有内存空间,用来保存内存地址
    1. 即指针也是一种变量,分配内存空间,内存空间的大小由编译器决定,比如:VS编译器下指针是4个字节
  2. 同一种编译器下,所有的指针类型占用的内存空间都一样,跟指针的类型无关
    1. 即:int *p;doub *p; char *p,char **p;这4个p占用内存大小都是4个字节(vs编译器而言)
  3. 那么指针的数据类型有什么用呢?
    1. 指针的数据类型是指这个指针指向的内存中存放的数据是什么类型。
    2. 指针步长(p++),根据所指内存存储的数据类型来确定,每次++,加几个字节
  4. 指针变量和他指向的内存块
    1. 修改指针的值,只会改变指针的指向,并不会改变指针所指内存块中的数据;p = 0x1111;
    2. *p = a;修改指针指向内存中存储的值,并不会改变指针变量的值。
  5. *p操作内存
    1. 在指针声明时,*号表示所声明的变量为指针
    2. 在指针使用时,*号表示 操作 指针所指向的内存空间中的值
    3. *p相当于通过地址(p变量的值)找到一块内存;然后操作内存
    4. *p放在等号的左边赋值(给内存赋值)
    5. *p放在等号的右边取值(从内存获取值)
  6. 字符串一级指针内存模型

     char buf[20]= "aaaa"; 
       char buf2[] = "bbbb";
       char *p1 = "111111";
       char *p2 = malloc(100); 
       strcpy(p2, "3333");
    
    1. 常量区中分配存放4个字符串:“aaaa”、“bbbb”、“111111”、“3333”
    2. 栈区:
      1. 先给buf分配20个字节的内存,内存中将常量“aaaa”拷贝进去
      2. 再给buf2分配5个字节的内存,内存中将常量“bbbb”拷贝进去
      3. 给指针p1分配4个字节,用于存储常量区“111111”的存储地址
      4. 给指针p2分配4个字节,用于存储堆区100个字节内存的首地址
    3. 堆区
      1. 通过malloc在堆区分配100个字节的内存
      2. 通过strcpy,将“3333”常量拷贝到堆区中。

通过递归的方式逆向排序

  1. 正常的逆序(采用两头排序)

     //逆序
     int  inverse(char *str1){
     	int length;
     	char *p1;
     	char *p2;
     	if (str1 == NULL) return -1;
     	length = strlen(str1);
     	p1 = str1;
     	//两头堵:从2头开始比较
     	p2 = str1 + length - 1;
     	while (p1<p2)
     	{
     		char c = *p1;
     		*p1 = *p2;
     		*p2 = c;
     		++p1;
     		--p2;
     	}
     }
    
  2. 递归和全局变量:把逆序的结果存入全局变量

     //采用全局变量来接收递归的结果
     char g_buf[1000];
        
     void inverse1(char *p){
     	//递归结束的异常条件
     	if (p == NULL) return; 
     	//递归结束条件
     	if (*p == '\0') return;
        
     	//注意此时没有执行打印,而是执行了函数调用,本质是让p+1所指向的地址入栈,即让字符串的而每一个地址入栈。
     	inverse1(p+1);
     	//printf("%c",*p);
     	//让字符串的每一个地址入栈
     	//这个函数会将p指针指向的内容拷贝g_buf,但是每次使用一次会覆盖上一次
     	//strncpy(g_buf, p, 1);
     	//这个函数会将p指针指向的内容拷贝g_buf,而且每次使用一次是拼接上一次拷贝的后面
     	strncat(g_buf, p, 1);
     }
    
  3. 递归和非全局变量:递归指针做函数参数

     void inverse2(char *p,char *bufresult){
     	if (p == NULL) return;
        	
     	if (*p == '\0') return;
        
     	inverse2(p + 1,bufresult);
     	strncat(bufresult, p, 1);
     }
    
  4. 主函数调用

     int main(){
    
     	char buf[] = "abcdefg";
     	//最常用方法
     	{
     		int length = strlen(buf);
     		inverse(buf);
     		printf("buf:%s\n", buf);
     	}
     	//采用递归方法逆序
     	//采用全局变量来接收逆序结果
     	{
     		memset(g_buf, 0, sizeof(g_buf));
     		inverse1(buf);
     		printf("g_buf:%s\n", g_buf);
     	}
        	
        
     	//采用局部变量来接收递归的结果
     	{
     		char mybuf[1024] = { 0 };
     		inverse2(buf, mybuf);
     		printf("mybuf:%s\n", mybuf);
     	}
     	getchar();
     	return 0;
     }
    

一级指针易错模型分析

  1. 不断修改指针变量的值
    1. 释放修改后的堆指针

       //释放的内存不对
       char *getKeyByValue(char **keyvaluebuf, char *keybuf){
       	int i = 0;
       	char *a = (char *)malloc(50);
       	printf("%x", a);
       	for (; **keyvaluebuf != '\0'; i++)
       	{
       		*a++ = *(*keyvaluebuf)++;
       	}
       	//此时的a,并不是指向开始分配的堆的首地址了,所以释放不对
       	free(a);
       	return;
       }
      
    2. 仍然使用修改后的指针打印原值

       void copy_str_err(char *from, char *to)
       {
       	for (; *from != '\0'; from++, to++)
       	{
       		*to = *from;
       	}
       	*to = '\0';
       	//此时的to,跟from已经被++改变了,因此肯定打印不出来原来的字符串!!!
       	printf("to:%s", to);
       	printf("from:%s", from);
       }
      
  2. 局部变量的作用域

     //功能:将x,y字符串,存入到一个数组中
     char *str_cnct(char *x, char* y)     /*简化算法*/
     {
     	char str3[80];
     	char *z = str3; 	/*指针z指向数组str3*/
     	//存x
     	while (*z++ = *x++);
     	z--;	               /*去掉串尾结束标志\0*/
     	//存y
     	while (*z++ = *y++);
     	z = str3;		 /*将str3地址赋给指针变量z*/
     	//错误:由于str3是局部变量,当该函数运行完毕后,str3将会被释放,指向的内存空间的数据也会被释放。
     	return(z);
     }
    
    

const 总结

const int a;  //
int const b;
    
const char *c;
char * const d;
const char * const  e ;
  1. 第一个第二个意思一样 代表一个常整形数
  2. 第三个 c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
  3. 第四个 d 常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)
  4. 第五个 e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)

二级指针

  1. 二级指针做输入和输出模型
    1. 指针做输入:主调函数内部分配内存
    2. 指针做输出:被调函数内部分配内存。
     //指针做输出:背调函数分配内存
     //求文件中两段话的长度
     int getMem(char **myp1, int *mylen1, char **myp2, int *mylen2)
     {
         char *tmp1 = NULL;
         char *tmp2 = NULL;
         tmp1 = (char *)malloc(100);
         if (tmp1 == NULL)
         {
             return -1;
         }
         strcpy(tmp1, "abcdefg");
         *mylen1 = strlen(tmp1);
            
         *myp1 = tmp1; //间接修改实参p1的值
            
         tmp2 = (char *)malloc(100);
         if (tmp2 == NULL)
         {
             return -2;
         }
         strcpy(tmp2, "11122233333");
         *mylen2 = strlen(tmp2);
            
         *myp2 = tmp2; //间接修改实参p2的值
         return 0;
     }
        
     int getMem_Free(char **myp1)
     {
            
         char *tmp = NULL;
         if (myp1 == NULL)
         {
             return -1;
         }
         tmp = *myp1;
         free(tmp);  // 释放完指针变量所指的内存空间
         *myp1 = NULL;  //把实参修改成NULL
         return 0;
     }
        
        
     void main()
     {
         char  *p1 = NULL;
         int len1 = 0;
            
         char *p2 = NULL;
         int len2 = 0;
            
         int ret = 0;
            
         //中指:如果想要在被调函数中修改主调函数的数据,那就需要传给背调函数地址
         //因此,如果被调函数原来是整型,那么就是取地址,一级指针;如果被调函数原来是指针,那么取地址,就是二级指针!!
         ret  = getMem(&p1, &len1, &p2, &len2 );
            
         printf("p1: %s \n", p1);
         printf("p2: %s \n", p2);
            
         getMem_Free(&p1);
         getMem_Free(&p2);
            
         system("pause");
         return ;
     }
    
Table of Contents