C零基础自学笔记<6>

第六部分

指针(重点)

本部分中的内容有些绕,且需要一些其他学科(组成原理和操作系统)的知识

为数据库,JAVA,C++,数据结构打基础

------------------------------------------------------

指针和指针变量的关系

指针就是地址,地址就是指针

地址就是内存单元的编号

故,指针只是一个值

指针变量是存放地址的变量

指针和指针变量是两个不同的概念

但要注意

通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样

热身程序

热身程序中的代码,只讲语法,不追究实际意义

程序一

//本代码只讲语法,不追究实际意义
#include <stdio.h>

int main()
{
	int * p;//p是变量的名字,int*表示p变量存放的是int

    /*
    int * p 不表示定义了一个名字叫做*p的变量
    int * p 中p是变量名,p变量的数据类型是int *类型
           所谓int * 类型,实际就是存放int变量地址的类型
	int p表示p只能存放整数*/

	int i=3; 
	p=&i; //ok  存放int类型变量的地址
	p=i; //error,因为类型不一致,p只能存放int类型变量的地址,不能够存放int变量的值
	p=55;//error ,原因同上
	return 0;
}

程序二

//本代码只讲语法,不追究实际意义
#include <stdio.h>

int main()
{
	int * p;
	int i=3;
	int j;
	p=&i; 
	/*
	①p存放了i的地址,因此p指向i
	②p不是i,i也不是p,更准确的说,修改p的值不影响i的值,修改i的值也不影响p的值
	③如果一个指针变量指向了某个普通变量,则
	     *指针变量就完全等同与普通变量
		 例子:
		    如果p是个指针变量,并且p存放了普通变量i的地址
			则p指向了普通变量i
			*p 就完全等同与 i
			或者说:  在所有出现*p的地方都可以替换为i
				 	  在所有出现i的地方都可以替换为*p
		   *p 就是以p的内容为地址的变量
	*/
	j=*p;  //等价于j=i;
	printf("i=%d,j=%d,*p=%d\n",i,j,*p);

	return 0;
}
//i=3,j=3,*p=3


指针的重要性

表示一些复杂的数据结构

快速的传递数据

使函数返回一个以上的值

能直接访问硬件

能够方便的处理字符串

是理解面向对象语言中引用的基础

总结:指针是c语言的灵魂

指针的定义

地址就是内存单元的编号

        从0开始的非负整数

        范围:2的32次方(B)=的30次方*2的平方(GB)=4(GB)  即4G或[0到4G-1]

指针就是地址,地址就是指针

        指针变量就是存放内存单元编号的变量,或者说指针变量就是存放地址的变量

        但要注意

        通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样

        指针的本质就是一个操作受限(四则运算中只能减,其余不行)的非负整数

指针的分类

①基本类型指针

例:

//热身程序中的程序二
#include <stdio.h>

int main()
{
	int * p;
	int i=3;
	int j;
	p=&i; 
	
	j=*p; 
	printf("i=%d,j=%d,*p=%d\n",i,j,*p);

	return 0;
}
//i=3,j=3,*p=3


常见错误

错误一

#include <stdio.h>

int main()
{
	int * p;
	int i=5;
	* p=i; //本行错误,
	printf("%d\n",*p);

	return 0;
}
//出错


错误二

#include <stdio.h>

int main()
{
	int i=5;
	int *p;
	int *q;
	p=&i;
	*q=p; //error,两个类型不一致
	*q=*p;//error
	p=q;//error, q为垃圾值,q赋值给p,p也变为垃圾直
	printf("%d\n",*q);
	/*q的空间是属于本程序的,所以本程序可以读写q的内容,
	      但若q内部是垃圾值,则本程序不能读写*p的内容
		  *q所代表的内存单元的控制权限并没有分配给本程序
		  所以运行到printf时,会立即出错
	*/
	return 0;
}
//出错


经典指针程序:互换两个数字

第一版:原方法

//可以互换
#include <stdio.h>

int main()
{
	int a=3;
	int b=5;
	int t;
	t=a;
	a=b;
	b=t;
	printf("a=%d,b=%d\n",a,b);
	return 0;
} 
//结果 a=5,b=3

第二版函数

//无法互换
#include <stdio.h>
void huhuan(int a,int b) 
{
	int t;
	t=a;
	a=b;
	b=t;
	return;

}
int main()
{
	int a=3;
	int b=5;
	huhuan(a,b);
	printf("a=%d,b=%d\n",a,b);
	return 0;
}
//a=3,b=5 
//数字未进行互换,改变的只是形参的值,跟实参无关
//且void执行完后,形参释放,故只能输出实参的值

第三版指针原程序

//无法互换
#include <stdio.h>
void huhuan2(int *p,int *q) 
{
	int * t;//写int t是错误的,类型不一致,若要互换p和q值,必须为int *。
	t=p;
	p=q;
	q=t;
	return;

}
int main()
{
	int a=3;
	int b=5;
	huhuan2(&a,&b);
	//写*p,*q是错误的,因为该值在void中定义,并未留在main中定义
	//写a,b也是错误的
	printf("a=%d,b=%d\n",a,b);
	return 0;
}
//a=3,b=5
//只换了地址,地址内的内容仍没有变化,形参的改变改不了实参的值

 第四版指针修改程序

//可以互换
#include <stdio.h>
void huhuan3(int *p,int *q) 
{
	int  t; //若要互换*p和*q的值,t必须定义为int,写int * t是错误的,类型不一致
	t=*p;//p是int *类型,*p是int型
	*p=*q;
	*q=t;
	return;

}
int main()
{
	int a=3;
	int b=5;
	huhuan3(&a,&b);
	printf("a=%d,b=%d\n",a,b);
	return 0;
}
//a=5,b=3

第五版指针加声明程序

指针加声明,形参名可以写成下列的样子,


//与第四版相比,互换了void和main的位置,加了声明
//可以互换
#include <stdio.h>
void huhuan3(int *,int*);
int main()
{
	int a=3;
	int b=5;
	huhuan3(&a,&b);
	printf("a=%d,b=%d\n",a,b);
	return 0;
}
void huhuan3(int *p,int *q) 
{
	int  t; //若要互换*p和*q的值,t必须定义为int,写int * t是错误的,类型不一致
	t=*p;//p是int *类型,*p是int型
	*p=*q;
	*q=t;
	return;

}

//a=5,b=3

星号的含义

*号:

        ①乘法

        ②定义指针变量

                int * p ;  //定义了一个名字叫p的变量,int *表示p只能存放int的地址

        ③指针运算符

                        该运算符放在已经定义好的指针变量的前面

                        如果p是一个已经定义好的指针变量

                        则*p表示:已p的内容为地址的变量

复习前面的内容

与互换连个数字代码基本一致,可依据自身情况选择性查看

基本类型指针

//基本类型指针1
#include <stdio.h>

int main()
{
	int * p;//等价于int *p,也等价于int* p; 但最好还是写第一个
	
	int i=5;
	char ch='A';//必须用符号框起来,否则会认为A是一个变量
	p=&i;//ok *p以p的内容为地址的变量
	*p=99;
	/*
	p=&ch;//error 字符变量地址
	p=ch;//error 
	p=5;//error p存放指针的地址,故错误
	*/
	printf("i=%d,*p=%d\n",i,*p);
   
    return 0;
}
//i=99,*p=99

//基本类型指针2
#include <stdio.h>
void swap_1(int i,int j) //i,j形参  i,j和 a,b为不同的变量
{
	int t;
	t=i;
	i=j;
	j=t;

}

int main()
{
	int a=3;
	int b=5;
	swap_1(a,b);  //a,b实参
    printf("a=%d,b=%d\n",a,b);
    return 0;
}
//a=3,b=5 未进行互换  
//检测实参和形参是否是同一个变量
#include <stdio.h>
void f(int i)
{
	i=99;//实参i和形参i不是一个变量

}

int main()
{
	int i=6;
    printf("i=%d\n",i);
	f(i);
	printf("i=%d\n",i);
    return 0;
}
//i=6   i=6 

//基本类型指针2修改一版
#include <stdio.h>
void swap_2(int * p,int * q)//形参名字是p和q,接收实参数据的是p和q,而不是*p和*q
{
	int * t;//不可写int t,与p q类型不一致
	t=p;
	p=q;
	q=t;

}

int main()
{
	int a=3;
	int b=5;
	swap_2(&a,&b);//实参只能发送给变量,故上面发给p或q,而不是*p或*q
	//不可写为(a,b),发送过去的是地址,pq存放的是整型地址而非整型变量
    printf("a=%d,b=%d\n",a,b);
    return 0;
}
//a=3,b=5 未进行互换   实参和形参为不同变量,形参的交换与实参没有关系
//基本类型指针2修改二版
#include <stdio.h>
void swap_3(int * p,int * q)//形参名字是p和q,接收实参数据的是p和q,而不是*p和*q
{
	int  t;//不可写int * t,与*p *q类型不一致
	t=*p;
	*p=*q;
	*q=t;

}

int main()
{
	int a=3;
	int b=5;
	swap_3(&a,&b);//实参只能发送给变量,故上面发给p或q,而不是*p或*q
	//不可写为(a,b),发送过去的是地址,pq存放的是整型地址而非整型变量
    printf("a=%d,b=%d\n",a,b);
    return 0;
}
//a=5,b=3

通过指针为什么可以使被调函数修改主调函数多

函数返回一个值

//不使用指针, 只能改变一个值 ,效率降低
#include <stdio.h>
int f(int i,int j)
{
	return 100;
	//return 88; error 返回100后,本行不会执行
}
int main()
{
	int  a=3,b=5;
	a=f(a,b);
	b=f(a,b);
    printf("a=%d,b=%d\n",a,b);
}
//a=100,b=100

指针使函数返回一个以上的值


#include <stdio.h>
int f(int i,int j)
{
	return 100;
}
void g(int * p,int * q)
{
	*p=1;
	*q=2;
}
int main()
{
	int  a=3,b=5;
	g(&a,&b);
    printf("a=%d,b=%d\n",a,b);
}
//a=1,b=2

 如何通过被调函数修改主调函数普通变量的值

    ①实参必须为该普通变量的地址()

    ②形参必须为指针变量

    ③在被调函数中通过

                *形参名=...

            的方式就可以修改主调函数相关变量的值

  一个和多个原理是一致的,若要修改多个,就将多个普通变量的地址发送过去

②指针和数组

本章打好基础,今后学数据结构的链表就能搞清楚以及其他语言里有关容器的内容

指针和一维数组(重点)

        ▶一维数组名

                一维数组名是个指针常量

                它存放的是一维数组第一个元素的地址

#include <stdio.h>

int main()
{
    int a[5];//a是数组名,5是数组元素的个数,元素就是变量 a[0]-a[4]
	//int a[3][4];  3行4列 a[0][0]是第一个元素 a[i][j]即第i+1行j+1列
	int b[5];
	//a=b;   error ,a是常量,不可赋给常量
    printf("%#X\n",&a[0]);
	printf("%#X\n",a);
   
   return 0;
}
/*输出结果
0X33A2B9D0
0X33A2B9D0*/

        ▶下标和指针的关系

                如果p是个指针变量,则

                        p[i]永久等价于*(p+i)

确定一个一维数组需要几个参数[如果一个函数要处理一个一维数组,则需要接收该数组的哪些信息]

需要两个参数:
数组第一个元素的地址
数组的长度

①代码

//确定一个一维数组需要两个参数,缺一不可
#include <stdio.h>
//函数可以输出任何一个一维数组的内容
void f(int * pArr,int len)//字符串汇总\0可作为结束标记,但数组中存放的任何一个值都可能为有效值,故确定一个数组需要两个信息
{
	int i;
	for(i=0;i<len;++i)
		printf("%d  ",*(pArr+i));//*pArr  *(pArr+1)  *(pArr+2)
	printf("\n");
}

int main()
{
	int a[5]={1,2,3,4,5};
	int b[6]={-1,-2,-3,4,5,-6};
	int c[100]={1,99,22,33};//c语言中,没有赋值,会默认为0
	f(a,5);//a是int *
	f(b,6);
	f(c,100);  
   return 0;
}
/*输出结果
1  2  3  4  5  
-1  -2  -3  4  5  -6  
1  99  22  33  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  
*/

 ②代码

//一定要明白,void中的pArr[3]和两个输出中的a[3]是同一个变量
#include <stdio.h>
void f(int * pArr,int len)
{
	pArr[3]=88;
	
}
int main()
{
	int a[6]={1,2,3,4,5,6};
	printf("%d\n",a[3]);//结果4
	f(a,6);
	printf("%d\n",a[3]);//结果88
	printf("%d\n",&a[3]);//结果为418916956,a[3]的地址
 
   return 0;
}
/*输出结果
4
88
418916956
*/

  代码③


#include <stdio.h>
void f(int * pArr,int len)
{
	int i;
	for(i=0;i<len;++i)
		printf("%d  ",* (pArr+i));//* (pArr+i)等价于pArr[i]等价于b[i]也等价于* (b+i)
	printf("\n");
	
}
int main()
{
	int a[6]={1,2,3,4,5,6};
	int b[6]={-1,-2,-3,4,5,-6};
	int c[100]={1,99,22,33};
	f(b,6);

 
   return 0;
}
/*输出结果
-1  -2  -3  4  5  -6  
*/

 复习上节课知识


#include <stdio.h>
void OutArr(int * pArr,int len)
{
	pArr[2]=10;//pArr[2] == * (pArr+2) == a[2] == *(a+2) 

}
int main()
{
	int a[5]={1,2,3,4,5};
	
	//a=&a[2];//error a为指针,为常量,值不可改变
	printf("%#X,%#X\n",a,&a[0]); //a=&a[0]   a!=a[0]
	printf("%d\n",a[2]);
	OutArr(a,5);
	printf("%d\n",a[2]);
   return 0;
}
/*输出结果
0X5A7E6F80,0X5A7E6F80
3
10

*/

#include <stdio.h>
void OutArr(int * pArr,int len)
{
	//pArr[2]=10;//pArr[2] == * (pArr+2) == a[2] == *(a+2) 
	int i;
	for(i=0;i<len;++i)
		printf("%d\n",pArr[i]);

}
int main()
{
	int a[5]={1,2,3,4,5};
	
	//a=&a[2];//error a为指针,为常量,值不可改变
	//printf("%#X,%#X\n",a,&a[0]); //a=&a[0]   a!=a[0]
	//printf("%d\n",a[2]);
	OutArr(a,5);
	//printf("%d\n",a[2]);
   return 0;
}
/*输出结果
1
2
3
4
5

*/

//要想改变 一个变量的值,需发送这个变量的地址
#include <stdio.h>
void f(int * p)
{
	*p=10;//i=10

}
int main()
{
	int i=5;
	f(&i);
	printf("i=%d\n",i);
	
	
   return 0;
}
/*输出结果
i=10
*/

      ▶指针变量的运算

指针变量不能相加,不能相乘 不能相除,只能减

如果两个指针变量指向的是同一块连续空间中的不同存储单元

则这两个指针变量才可以相减


#include <stdio.h>

int main()
{
	int i=5;
	int j=10;
	int * p=&i;
	int * q=&j;
	int a[5];
	p=&a[1];
	q=&a[4];
	printf("p和q所指向的单元相隔%d个单元\n",q-p);
	
	
   return 0;
}
/*输出结果
p和q所指向的单元相隔3个单元

*/

 一个指针变量到底占几个字节(非重点)

预备知识:sizeof(数据类型)

      功能:返回值就是该数据类型所占的字节数

      例子:sizeof(int)=4  sizeof(char)=1   sizeof(double)=8

   sizeof(变量名)

     功能:返回值是该变量所占的字节数

假设p指向char类型变量    1B

假设q指向int类型变量     4B

假设r指向double类型变量  8B

p q r 本身所占的字节数是否一样?  是一样的

#include <stdio.h>

int main()
{
	char ch='A';
	int i=99;
	double x=66.6;
	char * p=&ch;
	int * q=&i;
	double * r=&x;
	printf("%d %d %d\n",sizeof(p),sizeof(q),sizeof(r));
  
    return 0;
}

//4 4 4

总结:

一个指针变量,无论它指向的变量占几个字节,该指针变量本身占四个字节

一个变量的地址使用该变量首字节的地址来表示

专题

动态分内存分配[重点难点]

▶传统数组的缺点

1.数组长度必须事先制定,且只能是常整数,不能是变量

例子:

        int a[5];//OK

        int len =5;int a[len];  //error

2.传统形式定义的数组,该数组的内存,程序员无法手动释放

        在一个函数运行期间,系统为该函数中数组所分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放  
3.数组长度一旦定义,其长度不能再更改 

        (或者说数组的长度不能在函数运行的过程中,动态的扩充或缩小)

4.A函数定义的数组,在A函数运行期间可以被其它函数使用,但A函数运行完毕之后,A函数中的数组将无法再被其它函数使用

传统方式定义的数组不能跨函数使用


//传统数组的缺陷
#include <stdio.h>
void g(int * pArr,int len)
{
	pArr[2]=88;//pArr[2]== a[2]
}
void f()
{
	int a[5]={1,2,3,4,5};//20个字节的存储空间,程序员无法手动变成释放它,它只能在本函数运行完毕时,由系统自动释放
	g(a,5);
	printf("%d\n",a[2]);

}

int main()
{
	f();
  
    return 0;
}

▶为什么需要动态分配内存

        动态数组很好的解决了传统数组的这4个缺陷

        传统数组也叫静态数组

▶动态内存分配举例_ 动态数组的构造

malloc的使用

malloc(200);
char *       200个变量
int *        50 个变量
double *     25个变量
//malloc第一种用法
//无实际含义
//malloc是memory(内存)allocate(分配)的缩写
#include <stdio.h>
#include <malloc.h>

int main()
{
    int i=5;//分配了4个字节 静态分配
	int * p=(int *)malloc(4);//强制转换为int
   /*
   		1.要使用malloc函数,必须添加malloc.h这个头文件
		2.malloc函数只有一个形参,并且形参是整型
		3.malloc(4)中的4表示请求系统为本程序分配4个字节
		4.malloc函数只能返回第一个字节的地址
		5.int * p=(int *)malloc(4);本行分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
   		6.p本身所占的内存是静态分配的,p所指向的内存是动态分配的
   */
	*p=5;//*p代表的就是一个int变量,只不过*p这个整型变量的内存分配方式和“int i=5;”中i变量的分配方式不同
	free(p);//free(p)表示把所指向的内存给释放掉,p本身内存是静态的,不能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时自动释放
	printf("喵喵喵\n");
    return 0;
}
//malloc第二种用法
#include <stdio.h>
#include <malloc.h>
void f(int *q) //q是p的一份拷贝/副本
{
	//*p=200;//error,f中没有p变量
	//q=200;//error ,q为指针类型,只能存放int类型变量的地址。200只是个整数
	//**q=200; //error q存放四个字节的地址 *q代表这四个字节
	*q=200;
	//free(q);//error,把q所指向的内存释放掉,则在最后的printf中的*p无法再使用

}
int main()
{
	int * p =(int *)malloc(sizeof(int));//sizeof(int)返回值是int所占的字节数
	*p=10;//以p的内容为地址的变量
	printf("%d\n",*p);//10
	f(p);
	printf("%d\n",*p);//200
  
    return 0;
}
/*
10
200
*/
//动态一维数组实例
#include <stdio.h>
#include <malloc.h>
int main()
{
	int a[5];//如果int占4个字节的话,则本数组总共包含有20个字节,每四个字节被当做了一个int变量
    int len;
	int *pArr;
	int i;
	printf("请输入你要存放的元素的个数:");
	scanf("%d",&len);//5
	pArr=(int *)malloc(4 * len);//类似于 int pArr[len];
   //上一行,动态构造了一个一维数组,这一维数组的长度是len,该组的数组名是pArr,该数组每个元素是int类型
    
	//对一维数组进行操作
	for(i=0;i<len;++i)
		scanf("%d",&pArr[i]);
	
	//对一维数组进行输出
	printf("一维数组的内容是:");
	for(i=0;i<len;++i)
		printf("%d\n",&pArr[i]);
	free(pArr);//释放掉动态分配的数组
	return 0;
}
/*
请输入你要存放的元素的个数:5
1 2 3 4 5
一维数组的内容是:1
2
3
4
5
*/
//复习
# include <stdio.h>
# include <malloc.h>
//可以成功修改11行动态分配的四个字节的内容
void f(int *q)//发送p 写int *
{
	*q=10;

}
void g(int **p)//发送p的地址 写int **
{

}
int main(void)
{
	int *p=(int *)malloc(4); //11行
	//*p=10;
	
	printf("*p=%d\n",*p);//垃圾数字
	//f(p); //ok
	g(&p);//p是int *类型,&p是int **
	printf("*p=%d\n",*p);//
	return 0;

}

▶静态内存和动态内存的比较

静态内存是由系统自动分配,由系统自动释放

静态内存是在栈分配的(栈内容在数据结构课程中)

动态内存是由程序员手动分配,手动释放

动态内存是在堆分配的

▶跨函数使用内存的问题

 先看⑤多级指针内容再回到这个部分

//静态变量不能跨函数使用  此程序可运行,但不可这样写
#include <stdio.h>

void f(int **q)//q也是个指针变量,无论q是什么类型的指针变量,都只占用4个字节
{
	int i=5;
	//*q等价于p q和**q都不等价于p
	//*q=i;//error 因为这样写等价于p=i,所以是错误的
	*q=&i;//p=&i;
}

int main(void)
{
	int *p;//13行
	f(&p);
	printf("%d\n",*p);//本语句语法没有问题,但逻辑上有问题,静态空间在栈里面分配,函数中止时会释放
	return 0;

}
//运行结果
//5

//动态内存可以跨函数使用  
#include <stdio.h>
#include <malloc.h>
void f(int **q)//q也是个指针变量,无论q是什么类型的指针变量,都只占用4个字节
{
	*q=(int *)malloc(sizeof(int));//6行   sizeof(数据类型)返回值是该数据类型所占的字节数
	               //等价于p=(int *)malloc(sizeof(int));
	//q=5;//若不赋值,是垃圾值
	//*q=5;//p=5;
	**q=5;//*p=5;
}
int main(void)
{
	int *p;
	f(&p);
	printf("%d\n",*p);//指向6行,动态分配是在堆里面分配,在堆中,函数终止(出栈)时不会释放
	return 0;

}
//运行结果
//5
//计算机二级题目
下列程序中,能够通过调用函数fun,使main函数中的指针变量p指向一个合法的整型单元的是(C)
A)main()
 {  int *p;
    fun(p);//p值未改
    ...
  }
  int fun (int *p)
  { int s;
      p=&s;
  }
B)main()
 {  int *p;
    fun(&p);
  ...
  }
  int fun (int **q)
  { int s;
      *q=&s;//s地址可发送给p,但执行完后变量不存在
  }
C)#include<stdlib.h>
  main()
{
	int *p;
	fun(&p);
	...
}
int fun (int **q)
{ 
	*q=(int *)malloc(4);//*q是p,
}
D)#include<stdlib.h>
  main()
{
	int *p;
	fun(p);
	...
}
int fun (int *p)
{ 
	p=(int *)malloc(sizeof(int));
}

指针和二维数组(难点)

③指针和函数

④指针和结构体

结构体有助于学习JAVA中的类

下一章

⑤多级指针

#include <stdio.h>

int main()
{
	int  i=10;
	int * p=&i;
    int ** q= &p;
	int *** r=&q;
	//r=&p;  //error 因为r是int ***类型,r只是存放int ***类型变量的地址
	printf("i= %d\n",***r);
    return 0;
}
// 运行结果   
//i= 10
变量ipqr
存放101000H2000H3000H
地址1000H2000H3000H4000H
类型intint * int **int ***
用r表示***r**r*rr

//掌握多级指针,为跨函数使用内存打基础
void f(int **q)
{
	//q是p的地址  *q就是p

}
void g()
{
	int i=10;
	int * p=&i;  
	g(&p); //p是int * 类型,&p是int **类型

}
int main(void)
{
	g();
	return 0;

}

——第六部分完——
下一篇从结构体开始

  • 33
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值