继承是类与类的关系。
重载是方法与方法之间的关系。
继承是一个类需要另一个类的属性和方法,所以用到继承,这是发生在两个类之间的事情。
重载是指一个方法与另一个方法的方法名相同而参数列表不相同,这样的情况叫重载,属于多态。这是发生在一个类中的事情。
一个子类继承另外一个类,可以对父类中的方法进行重写,说到这里应该可以看出,继承和重载没什么关系了。而重写却与继承有很大的关系。
因为重写一般都发生在有继承的情况下,就是子类重写父类的方法。
重载和重写有什么区别和联系:
先说联系:
都是发生在同名方法之间的事情。
区别:
重载是发生在一个类中,根据参数列表的不同来区分两个同名的方法。
重写是发生在两个又继承关系的类中,根据方法体不同来区分两个同名方法。
memcpy
开放分类: C语言函数
原型:extern void *memcpy(void *dest, void *src, unsigned int count);
用法:#include <string.h>
功能:由src所指内存区域复制count个字节到dest所指内存区域。
说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。
举例:
// memcpy.c
#include <syslib.h>
#include <string.h>
main()
{
char *s="Golden Global View";
char d[20];
clrscr();
memcpy(d,s,strlen(s));
d[strlen(s)]=0;
printf("%s",d);
getchar();
return 0;
}
strcat
原型:extern char *strcat(char *dest,char *src);
用法:#include <string.h>
功能:把src所指字符串添加到dest结尾处(覆盖dest结尾处的'\0')并添加'\0'。
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
举例:
// strcat.c
#include <syslib.h>
#include <string.h>
main()
{
char d[20]="Golden Global";
char *s=" View";
clrscr();
strcat(d,s);
printf("%s",d);
getchar();
return 0;
}
memset
在你申请了一块内存之后,
比如
int *p=NULL;
p=malloc(10*sizeof(int));//申请了10个int型内存
memset(p,0,10*sizeof(int));//全部初始化为0
memset的作用就是把你快连续的内存初始化为你给的值。
Example
/* MEMSET.C: This program uses memset to
* set the first four bytes of buffer to "*".
*/
#include <memory.h>
#include <stdio.h>
void main( void )
{
char buffer[] = "This is a test of the memset function";
printf( "Before: %s\n", buffer );
memset( buffer, '*', 4 );
printf( "After: %s\n", buffer );
}
Output
Before: This is a test of the memset function
After: **** is a test of the memset function
ftell
开放分类: 计算机语言、计算机技术、编程、程序、函数
函数名: ftell
功 能: 返回当前文件指针
用 法: long ftell(FILE *stream);
程序例:
#include <stdio.h>
int main(void)
{
FILE *stream;
stream = fopen("MYFILE.TXT", "w+");
fprintf(stream, "This is a test");
printf("The file pointer is at byte \
%ld\n", ftell(stream));
fclose(stream);
return 0;
}
fopen
开放分类: 计算机语言、计算机技术、编程、程序、函数
fopen(打开文件)
相关函数 open,fclose
表头文件 #include<stdio.h>
定义函数 FILE * fopen(const char * path,const char * mode);
函数说明 参数path字符串包含欲打开的文件路径及文件名,参数mode字符串则代表着流形态。
mode有下列几种形态字符串:
r 打开只读文件,该文件必须存在。
r+ 打开可读写的文件,该文件必须存在。
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w+ 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。
a+ 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。
上述的形态字符串都可以再加一个b字符,如rb、w+b或ab+等组合,加入b 字符用来告诉函数库打开的文件为二进制文件,而非纯文字文件。不过在POSIX系统,包含Linux都会忽略该字符。由fopen()所建立的新文件会具有S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH(0666)权限,此文件权限也会参考umask 值。
返回值
文件顺利打开后,指向该流的文件指针就会被返回。若果文件打开失败则返回NULL,并把错误代码存在errno 中。
附加说明
一般而言,开文件后会作一些文件读取或写入的动作,若开文件失败,接下来的读写动作也无法顺利进行,所以在fopen()后请作错误判断及处理。
【例程】
#include <stdlib.h>
#include <stdio.h>
#include <dir.h>
int main(void)
{
char *s;
char drive[MAXDRIVE];
char dir[MAXDIR];
char file[MAXFILE];
char ext[MAXEXT];
int flags;
s=getenv("COMSPEC"); /* get the comspec environment parameter */
flags=fnsplit(s,drive,dir,file,ext);
printf("Command processor info:\n");
if(flags & DRIVE)
printf("\tdrive: %s\n",drive);
if(flags & DIRECTORY)
printf("\tdirectory: %s\n",dir);
if(flags & FILENAME)
printf("\tfile: %s\n",file);
if(flags & EXTENSION)
printf("\textension: %s\n",ext);
return 0;
}
fseek
开放分类: 计算机语言、计算机技术、编程、程序、函数
函数名: fseek
功 能: 重定位流上的文件指针
用 法: int fseek(FILE *stream, long offset, int fromwhere);
程序例:
#include <stdio.h>
long filesize(FILE *stream);
int main(void)
{
FILE *stream;
stream = fopen("MYFILE.TXT", "w+");
fprintf(stream, "This is a test");
printf("Filesize of MYFILE.TXT is %ld bytes\n", filesize(stream));
fclose(stream);
return 0;
}
long filesize(FILE *stream)
{
long curpos, length;
curpos = ftell(stream);
fseek(stream, 0L, SEEK_END);
length = ftell(stream);
fseek(stream, curpos, SEEK_SET);
return length;
}
int fseek( FILE *stream, long offset, int origin );
第一个参数stream为文件指针,offset为偏移,比如你要从文件的第10000个字节开始读取的话,offset就应该为10000,origin 为标志是从文件开始还是末尾。
origin 的取值:
SEEK_CUR Current position of file pointer
SEEK_END End of file
SEEK_SET Beginning of file
那么fseek(fp,-size,1)中-size和1是否应理解为,从文件倒说第一个文件开始读取
fseek最后一个参数最好不要直接指定一个数值,比如1,
要使用SEEK_CUR,SEEK_END,SEEK_SET
第二个参数表示相对于第三个参数的偏移,整数表示正向偏移,负数表示负向偏移,比如
fseek(fp,-size,SEEK_CUR);
从当前位置向文件后方(比如文件有123三个数字,那么2在三的后方,3在2的前方)
fseek(fp,size,SEEK_SET);
从文件开始位置向前移动size
这里默认size是正数
fwrite
开放分类: 计算机语言、计算机技术、编程、程序、函数
函数名: fwrite
功 能: 写内容到流中
用 法:fwrite(buffer,size,count,fp);
(1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
(2)size:要读写的字节数;
(3)count:要进行读写多少个size字节的数据项;
(4)fp:文件型指针。
程序例:
#include <stdio.h>
struct mystruct
{
int i;
char ch;
};
int main(void)
{
FILE *stream;
struct mystruct s;
if ((stream = fopen("TEST.$$$", "wb")) == NULL) /* open file TEST.$$$ */
{
fprintf(stderr, "Cannot open output file.\n");
return 1;
}
s.i = 0;
s.ch = 'A';
fwrite(&s, sizeof(s), 1, stream); /* write struct s to file */
fclose(stream); /* close file */
return 0;
}
fread
开放分类: 计算机语言、计算机技术、编程、程序、函数
函数名: fread
功 能: 从一个流中读数据
用 法: int fread(void *ptr, int size, int nitems, FILE *stream);
参 数:用于接收数据的地址(字符型指针)(ptr)
单个元素的大小(size)
元素个数(nitems)
提供数据的文件指针(stream)
返回值:成功读取的元素个数
程序例:
#include <string.h>
#include <stdio.h>
int main(void)
{
FILE *stream;
char msg[] = "this is a test";
char buf[20];
if ((stream = fopen("DUMMY.FIL", "w+"))
== NULL)
{
fprintf(stderr,
"Cannot open output file.\n");
return 1;
}
/* write some data to the file */
fwrite(msg, strlen(msg)+1, 1, stream);
/* seek to the beginning of the file */
fseek(stream, SEEK_SET, 0);
/* read the data and display it */
fread(buf, 1, strlen(msg)+1, stream);
printf("%s\n", buf);
fclose(stream);
return 0;
}
FindFirstFile
void CFindFilesDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
CString c1;
CString stredit;
CString strtemp;
HANDLE hFile;
WIN32_FIND_DATA fileinfo;
DWORD errorcode = 0;
m_Edit1.GetWindowText(c1); //要熟悉控件的基本使用
if(!c1.IsEmpty())
{
hFile=FindFirstFile("f:\\*.*",&fileinfo);
while(hFile!=INVALID_HANDLE_VALUE&&errorcode!=ERROR_NO_MORE_FILES)
{
//
strtemp=fileinfo.cFileName;
stredit=stredit+"\r\n";
stredit=stredit+strtemp;
FindNextFile(hFile,&fileinfo);
errorcode=GetLastError();
}
if(errorcode==ERROR_NO_MORE_FILES)
{
m_cedit1.SetWindowText(stredit);
MessageBox("检索结束");
}
CloseHandle(hFile);
}
}
在上述代码中
FindFirstFile第一个个参数需要一个路径,但要写*.*,表示从所有文件中找到的第一个.但写f:或者f:双斜杠都返回无效句柄.
如果f:\\1.txt 则对特定文件进行搜索,返回找到的第一个
找到的文件的文件结构被记录在WIN32_FIND_DATA结构体里
FindNextFile第一个参数是FindFirstFile的返回值,可以接着上次结果继续寻找
但注意!!!!
但一个目录的文件被搜索一遍后,FindNextFile返回的WIN32_FIND_DATA结构体将重复找到最后一个找到的文件,而不会终止.
让循环结束的方法就是找上一次的报错码,然后看有没有ERROR_NO_MORE_FILES信息发生.
hFile的有效与否个人觉得还是取决于第一次的FindFirstFile结果,本程序后半段的运行主要还是errorcode!=ERROR_NO_MORE_FILES在控制.
一、 内联汇编的一般原则:
1、 自由使用通用寄存器;(EAX,EBX,ECX和EDX)
2、 其它寄存器利用堆栈保留,使用,最后恢复;
一般的像下面这样:
__asm{
push ebp
push esp
……….//使用EBP和ESP
pop esp
pop ebp
}
二、 内联汇编__asm可以单独使用:
例如:__asm mov eax,anyval1
__asm mov ebx,anyval2
三、 函数返回值可以直接放到eax中,可以不理会警告
例如: int anyfun(……/*anyparm*/)
{
int irtn; //函数返回值
…… //函数语句
__asm mov eax,irtn //代替return irtn;但编译器会发出警告,可以不理它
}
四、 内联汇编不区分大小写,语法同普通汇编
例如:__asm{
mov eax,ebx
MOV EAX,EBX //同上一句
}
注意:C++的变量还是区分大小写的
五、 内联汇编尽量使用__asm或_asm关键字,而不用标准C++的asm关键字(这个是微软说的)
以上只是一些关于内联汇编的补充,我将出一系列围绕内联汇编的文章,下面接着上一次的话题详细一点地讲一下MMX指令的调用
1、 MMX指令集简介:
[数据传输指令]
movq //传输64位整数
movd //传输32位整数
[数据打包转换指令]
PACKSSWB //Pack words into bytes with signed saturation.
PACKSSDW //Pack doublewords into words with signed saturation.
PACKUSWB //Pack words into bytes with unsigned saturation.
PUNPCKHBW //Unpack high-order bytes.
PUNPCKHWD //Unpack high-order words.
PUNPCKHDQ //Unpack high-order doublewords.
PUNPCKLBW //Unpack low-order bytes.
PUNPCKLWD //Unpack low-order words.
PUNPCKLDQ //Unpack low-order doublewords.
注:这一组指令我没有具体用过,不知道是干什么的,请高手赐教!小弟先谢了!
[ 算术指令]
PADDB
PADDW
PADDD
PADDSB
PADDSW
PADDUSB
PADDUSW
PSUBB
PSUBW
PSUBD
PSUBSB
PSUBSW
PSUBUSB
PSUBUSW
PMULHW
PMULLW
PMADDWD
[ 比较指令]
PCMPEQB Compare packed bytes for equal.
PCMPEQW Compare packed words for equal.
PCMPEQD Compare packed doublewords for equal.
PCMPGTB Compare packed signed byte integers for greater than.
PCMPGTW Compare packed signed word integers for greater than.
PCMPGTD Compare packed signed doubleword integers for greater than.
这组指令用于成组比较数据
[ 位逻辑指令]
PAND Bitwise logical AND.
PANDN Bitwise logical AND NOT.
POR Bitwise logical OR.
PXOR Bitwise logical exclusive OR.
这组指令与AND,XOR基本相同,都是按位进行逻辑运算。
[ 移位和循环移位指令]
PSLLW //Shift packed words left logical.
PSLLD //Shift packed doublewords left logical.
PSLLQ //Shift packed quadword left logical.
PSRLW //Shift packed words right logical.
PSRLD //Shift packed doublewords right logical.
PSRLQ //Shift packed quadword right logical.
PSRAW //Shift packed words right arithmetic.
PSRAD //Shift packed doublewords right arithmetic.
[ 状态管理指令]
EMMS //Empty MMX state.
在VC中要求所有的MMX指令调用完毕后都要调用这个指令清空
例如:__asm{
…..MMX 语句
EMMS //清空状态
}
以上是所有的MMX指令,你可以测试使用其中的指令,他的工作原理就是单指令,多数据
2、 使用MMX指令集的注意事项
由于在CPU内部,FPU寄存器和MMX寄存器是同一组寄存器,所以在同时引用上面寄存器时要注意正确的状态转换,具体做法以后在探讨。你只要先记住不能简单的混合以上两种指令集即可。
每次调用之前要先检测cpu是否支持MMX指令集,以免发生异常。具体做法看下列示例:
mov EAX, 1 ; request for feature flags
CPUID ; 0Fh, 0A2h CPUID instruction
test EDX, 00800000h ; Is IA MMX technology bit (Bit 23 of EDX)
; in feature flags set?
jnz MMX_Technology_Found
这段代码来自Intel的参考手册,所以你可以放心的使用。
3、 下面用一段示例代码来说明一下怎样用MMX指令 __int8 i8_a[2][16]; //字节操作数,两组,每组16个
__int16 i16_a[8]; //字操作数
__int32 i32_a[4];
__int64 i64_a[2];
i64_a[0]=0;
i64_a[1]=0;
i32_a[0]=1000;
i32_a[1]=1000;
i32_a[2]=3;
i32_a[3]=4;
i16_a[0]=10;
i16_a[1]=20;
i16_a[2]=30;
i16_a[3]=40;
i16_a[4]=50;
i16_a[5]=60;
i16_a[6]=70;
i16_a[7]=80;
i8_a[0][0]=1;
i8_a[0][1]=1;
i8_a[0][2]=1;
i8_a[0][3]=1;
i8_a[0][4]=1;
i8_a[0][5]=1;
i8_a[0][6]=1;
i8_a[0][7]=1;
i8_a[0][8]=1;
i8_a[0][9]=1;
i8_a[0][10]=1;
i8_a[0][11]=1;
i8_a[0][12]=1;
i8_a[0][13]=1;
i8_a[0][14]=1;
i8_a[0][15]=1;
i8_a[1][0]=2;
i8_a[1][1]=2;
i8_a[1][2]=2;
i8_a[1][3]=2;
i8_a[1][4]=2;
i8_a[1][5]=2;
i8_a[1][6]=2;
i8_a[1][7]=2;
i8_a[1][8]=2;
i8_a[1][9]=2;
i8_a[1][10]=2;
i8_a[1][11]=2;
i8_a[1][12]=2;
i8_a[1][13]=2;
i8_a[1][14]=2;
i8_a[1][15]=2;
__asm{
movq mm1,[i64_a]
movq mm2,[i64_a]
movq mm2, [i32_a+8]
psubd mm2, [i32_a]
movq [i32_a],mm2
movq mm1,[i16_a]
paddsw mm1,[i16_a+8]
movq [i16_a],mm1
movq mm1,[i8_a]
movq mm2,[i8_a+8]
paddb mm1,[i8_a+16]
paddb mm2,[i8_a+24]
movq [i8_a],mm1
movq [i8_a+8],mm2
emms //最后清除MMX状态寄存器,正确返回给系统
}
你可以通过设置断点,和watch的方法来观察寄存器以及变量的变化情况,这里只是引用了一部分的指令,那么最引人注意的是对i16_a、i8_a、以及i32_a数组的操作,我是随便对他们进行了算术运算,大家可以看到我加两组字节数组数据时只用了两条加指令,这是普通的指令集望尘莫及的。这就是单指令,多数据的魅力。同时你也可以看到对64位整数的操作也简单多了。但是要注意的是,MMX指令集里好像没有提供除法操作。所以你需借助算法来实现。另外要补充的是MMX寄存器是从MM0-MM7命名的一组64位寄存器。
名称:sscanf() - 从一个字符串中读进与指定格式相符的数据. 函数原型:Int sscanf( string str, string fmt, mixed var1, mixed var2 ... ); int scanf( const char *format [,argument]... ); 说明:sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。 其中的format可以是一个或多个 {%[*] [width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | 非%符号} 注:1、 * 亦可用于格式中, (即 %*d 和 %*s) 加了星号 (*) 表示跳过此数据不读入. (也就是不把此数据读入参数中) 2、{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d。 3、width表示读取宽度。 4、{h | l | I64 | L}:参数的size,通常h表示单字节size,I表示2字节 size,L表示4字节size(double例外),l64表示8字节size。 5、type :这就很多了,就是%s,%d之类。 6、特别的:%*[width] [{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值 支持集合操作: %[a-z] 表示匹配a到z中任意字符,贪婪性(尽可能多的匹配) %[aB'] 匹配a、B、'中一员,贪婪性 %[^a] 匹配非a的任意字符,贪婪性
1. 常见用法。 char buf[512] = ; sscanf("123456 ", "%s", buf); printf("%s\n", buf); 结果为:123456 2. 取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。 sscanf("123456 ", "%4s", buf); printf("%s\n", buf); 结果为:1234 3. 取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。 sscanf("123456 abcdedf", "%[^ ]", buf); printf("%s\n", buf); 结果为:123456 4. 取仅包含指定字符集的字符串。如在下例中,取仅包含1到9和小写字母的字符串。 sscanf("123456abcdedfBCDEF", "%[1-9a-z]", buf); printf("%s\n", buf); 结果为:123456abcdedf 5. 取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。 sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf); printf("%s\n", buf); 结果为:123456abcdedf 6、给定一个字符串iios/12DDWDFF@122,获取 / 和 @ 之间的字符串,先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中 sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf); printf("%s\n", buf); 结果为:12DDWDFF 7、给定一个字符串““hello, world”,仅保留world。(注意:“,”之后有一空格) sscanf(“hello, world”, "%*s%s", buf); printf("%s\n", buf); 结果为:world %*s表示第一个匹配到的%s被过滤掉,即hello被过滤了 如果没有空格则结果为NULL。 sscanf的功能很类似于正则表达式, 但却没有正则表达式强大,所以如果对于比较复杂的字符串处理,建议使用正则表达式. //------------------------------------------------------- sscanf,表示从字符串中格式化输入 上面表示从str中,输入数字给x,就是32700 久以前,我以为c没有自己的split string函数,后来我发现了sscanf;一直以来,我以为sscanf只能以空格来界定字符串,现在我发现我错了。 sscanf是一个运行时函数,原形很简单: int sscanf( const char *buffer, const char *format [, argument ] ... ); 它强大的功能体现在对format的支持上。 我以前用它来分隔类似这样的字符串2006:03:18: int a, b, c; sscanf("2006:03:18", "%d:%d:%d", a, b, c); 以及2006:03:18 - 2006:04:18: char sztime1[16] = "", sztime2[16] = ""; sscanf("2006:03:18 - 2006:04:18", "%s - %s", sztime1, sztime2); 但是后来,我需要处理2006:03:18-2006:04:18 仅仅是取消了‘-’两边的空格,却打破了%s对字符串的界定。 我需要重新设计一个函数来处理这样的情况?这并不复杂,但是,为了使所有的代码都有统一的风格,我需要改动很多地方,把已有的sscanf替换成我自己的分割函数。我以为我肯定需要这样做,并伴随着对sscanf的强烈不满而入睡;一觉醒来,发现其实不必。 format-type中有%[]这样的type field。如果读取的字符串,不是以空格来分隔的话,就可以使用%[]。 %[]类似于一个正则表达式。[a-z]表示读取a-z的所有字符,[^a-z]表示读取除a-z以外的所有字符。 所以那个问题也就迎刃而解了: sscanf("2006:03:18 - 2006:04:18", "%[0-9,:] - %[0-9,:]", sztime1, sztime2); 在softmse (Jake) 的问题贴http://community.csdn.net/Expert/topic/4843/4843294.xml?temp=.4321558中 ,周星星给出了一个很cool的sscanf用例,而后通过学习,发现sscanf真棒,现做一总结。 原问题: iios/12DDWDFF@122 获取/和@之间的字符串怎么做 C程序里面有什么函数吗? 周星星的代码: #include <stdio.h> int main() { const char* s = "iios/12DDWDFF@122"; char buf[20]; sscanf( s, "%*[^/]/%[^@]", buf ); printf( "%s\n", buf ); return 0; } 结果为:12DDWDFF sscanf与scanf类似,都是用于输入的,只是后者以屏幕(stdin)为输入源,前者以固定字符串为输入源。 函数原型: int scanf( const char *format [,argument]... ); 其中的format可以是一个或多个 {%[*] [width] [{h | l | I64 | L}]type | ' ' | '\t' | '\n' | 非%符号}, 注:{a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d。 width:宽度,一般可以忽略,用法如: const char sourceStr[] = "hello, world"; char buf[10] = ; sscanf(sourceStr, "%5s", buf); //%5s,只取5个字符 cout << buf<< endl; 结果为:hello {h | l | I64 | L}:参数的size,通常h表示单字节size,I表示2字节 size,L表示4字节size(double例外),l64表示8字节size。 type :这就很多了,就是%s,%d之类。 特别的: %*[width] [{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值。如: const char sourceStr[] = "hello, world"; char buf[10] = ; sscanf(sourceStr, "%*s%s", buf); //%*s表示第一个匹配到的%s被过滤掉,即hello被过滤了 cout << buf<< endl; 结果为:world 支持集合操作: %[a-z] 表示匹配a到z中任意字符,贪婪性(尽可能多的匹配) %[aB'] 匹配a、B、'中一员,贪婪性 %[^a] 匹配非a的任意字符,贪婪性 是不是感觉眼熟了啊,不错,这和正则表达式很相似,而且仍然支持过滤,即可以有%*[a-z].如: 星星大哥例子回顾: const char* s = "iios/12DDWDFF@122"; char buf[20]; sscanf( s, "%*[^/]/%[^@]", buf ); printf( "%s\n", buf ); 先将 "iios/"过滤掉,再将非'@'的一串内容送到buf中,cool.得到结果。 PS: 向星星大哥和softmse (Jake) 致谢了,有了你们才有它啊
|