简答题.docx
《简答题.docx》由会员分享,可在线阅读,更多相关《简答题.docx(9页珍藏版)》请在冰豆网上搜索。
![简答题.docx](https://file1.bdocx.com/fileroot1/2022-12/31/93c00a07-f4cd-4dab-a6c4-3baebfeae14b/93c00a07-f4cd-4dab-a6c4-3baebfeae14b1.gif)
简答题
1.符号常量和常变量的区别
符号常量用#define来定义,例如#definePI3.14;
常变量用const来定义,例如constdoublePI=3.14。
请注意符号常量和常变量的区别:
1)符号常量只是用一个字符串代替一个符号,在预编译时把所有符号常量替换为所指定的字符串,它没有类型,在内存中并不存在以符号常量命名的存储单元。
2)常变量具有变量的特征,它具有类型,在内存中存在着以它命名的存储单元,可以用sizeof运算符计算出其长度,它与一般变量唯一的不同是常变量的值在程序运行期间不能改变。
constintic=20;
//…
ic=40;//这样是不可以的,编译时是无法通过,因为我们不能对const修饰的ic重新赋值的。
//这样我们的程序就会更早更容易发现问题了。
//**************
有了const修饰的ic我们不称它为变量,而称符号常量,代表着20这个数。
这就是const的作用。
ic是不能在它处重新赋新值了。
认识了const作用之后,另外,我们还要知道格式的写法。
有两种:
constintic=20;与intconstic=20;。
它们是完全相同的。
这一点我们是要清楚。
总之,你务必要记住const与int哪个写前都不影响语义。
有了这个概念后,我们来看这两个家伙:
constint*pi与intconst*pi,按你的逻辑看,它们的语义有不同吗?
呵呵,你只要记住一点,int与const哪个放前哪个放后都是一样的,就好比constintic;与intconstic;一样。
也就是说,它们是相同的。
好了,我们现在已经搞定一个“双包胎”的问题。
那么int*constpi与前两个式子又有什么不同呢?
我下面就来具体分析它们的格式与语义吧!
2constint*pi的语义
我先来说说constint*pi是什么作用(当然intconst*pi也是一样的,前面我们说过,它们实际是一样的)。
看下面的例子:
//*************代码开始***************
inti1=30;
inti2=40;
constint*pi=&i1;
pi=&i2; //4.注意这里,pi可以在任意时候重新赋值一个新内存地址
i2=80; //5.想想看:
这里能用*pi=80;来代替吗?
当然不能
printf(“%d”,*pi); //6.输出是80
//*************代码结束***************
语义分析:
看出来了没有啊,pi的值是可以被修改的。
即它可以重新指向另一个地址的,但是,不能通过*pi来修改i2的值。
这个规则符合我们前面所讲的逻辑吗?
当然符合了!
首先const 修饰的是整个*pi(注意,我写的是*pi而不是pi)。
所以*pi是常量,是不能被赋值的(虽然pi所指的i2是变量,不是常量)。
其次,pi前并没有用const修饰,所以pi是指针变量,能被赋值重新指向另一内存地址的。
你可能会疑问:
那我又如何用const来修饰pi呢?
其实,你注意到int*constpi中const的位置就大概可以明白了。
请记住,通过格式看语义。
哈哈,你可能已经看出了规律吧?
那下面的一节也就没必要看下去了。
不过我还得继续我的战斗!
3再看int*constpi
确实,int*constpi与前面的intconst*pi会很容易给混淆的。
注意:
前面一句的const是写在pi前和*号后的,而不是写在*pi前的。
很显然,它是修饰限定pi的。
我先让你看例子:
//*************代码开始***************
inti1=30;
inti2=40;
int*constpi=&i1;
//pi=&i2; 4.注意这里,pi不能再这样重新赋值了,即不能再指向另一个新地址。
//所以我已经注释了它。
i1=80; //5.想想看:
这里能用*pi=80;来代替吗?
可以,这里可以通过*pi修改i1的值。
//请自行与前面一个例子比较。
printf(“%d”,*pi); //6.输出是80
//***************代码结束*********************
语义分析:
看了这段代码,你明白了什么?
有没有发现pi值是不能重新赋值修改了。
它只能永远指向初始化时的内存地址了。
相反,这次你可以通过*pi来修改i1的值了。
与前一个例子对照一下吧!
看以下的两点分析
1)pi因为有了const的修饰,所以只是一个指针常量:
也就是说pi值是不可修改的(即pi不可以重新指向i2这个变量了)(看第4行)。
2)整个*pi的前面没有const的修饰。
也就是说,*pi是变量而不是常量,所以我们可以通过*pi来修改它所指内存i1的值(看5行的注释)
总之一句话,这次的pi是一个指向int变量类型数据的指针常量。
我最后总结两句:
1)如果const修饰在*pi前则不能改的是*pi(即不能类似这样:
*pi=50;赋值)而不是指pi.
2)如果const是直接写在pi前则pi不能改(即不能类似这样:
pi=&i;赋值)。
请你务必先记住这两点,相信你一定不会再被它们给搞糊了。
现在再看这两个申明语句intconst*pi和int*constpi时,呵呵,你会头昏脑胀还是很轻松惬意?
它们各自申明的pi分别能修改什么,不能修改什么?
再问问自己,把你的理解告诉我吧,可以发帖也可以发到我的邮箱(我的邮箱yyf977@)!
我一定会答复的。
3) 补充三种情况。
这里,我再补充以下三种情况。
其实只要上面的语义搞清楚了,这三种情况也就已经被包含了。
不过作为三种具体的形式,我还是简单提一下吧!
情况一:
int*pi指针指向constinti常量的情况
//**********begin*****************
constinti1=40;
int*pi;
pi=&i1; //这样可以吗?
不行,VC下是编译错。
//constint类型的i1的地址是不能赋值给指向int类型地址的指针pi的。
否则pi岂不是能修改i1的值了吗!
pi=(int*)&i1; // 这样可以吗?
强制类型转换可是C所支持的。
//VC下编译通过,但是仍不能通过*pi=80来修改i1的值。
去试试吧!
看看具体的怎样。
//***********end***************
情况二:
constint*pi指针指向constinti1的情况
//*********begin****************
constinti1=40;
constint*pi;
pi=&i1;//两个类型相同,可以这样赋值。
很显然,i1的值无论是通过pi还是i1都不能修改的。
//*********end*****************
情况三:
用constint*constpi申明的指针
//***********begin****************
inti
constint*constpi=&i;//你能想象pi能够作什么操作吗?
pi值不能改,也不能通过pi修改i的值。
因为不管是*pi还是pi都是const的。
2.循环中break语句和continue语句有什么不同
continue
表示结束本循环的continue以后部分的代码执行,并转到下一个循环周期继续执行循环。
可以通过加判断条件实现一部分循环的跳过。
break
表示结束本循环,执行循环以后的代码,循环在此处结束了。
可以加判断语句实现循环达到要求时结束循环跳出。
(如果是循环的嵌套,则只跳出当前循环)
3.函数声明和函数定义的比较
函数声明:
返回类型函数名(参数类型1参数名1,·····,参数类型n参数名n);
如:
intfun(inta,intb);
voiddisplay();
floatfn1(floatx);
函数定义:
返回类型函数名(参数类型1参数名1,·····,参数类型n参数名n)
{
函数体······
}
如:
intfun(inta,intb)
{intc;
c=a+b;
returnc;}
在程序中,如果main()函数在前,必须在mian()中写函数声明;如果函数在main()前面,可以不在main()中写函数声明!
C语言编译系统是由上往下编译的.一般被调函数放在主调函数后面的话,前面就该有声明.不然C由上往下的编译系统将无法识别。
正如变量必须先声明后使用一样,函数也必须在被调用之前先声明,否则无法调用!
函数的声明可以与定义分离,要注意的是一个函数只能被定义一次,但可以声明多次。
函数声明由函数返回类型、函数名和形参列表组成。
形参列表必须包括形参类型,但是不必对形参命名。
这三个元素被称为函数原型,函数原型描述了函数的接口。
定义函数的程序员提供函数原型,使用函数的程序员就只需要对函数原型编辑即可。
【返回类型】函数名(参数1类型参数1,参数2类型参数2,……);
函数声明中的形参名往往被忽略,如果声明中提供了形参的名字,也只是用作辅助文档。
另外要注意函数声明是一个语句,后面不可漏分号!
例子:
#include
intadd(intx,inty);//函数声明语句
main()
{
inta,b,c;
c=add(a,b);
printf("%d",c);
}
intadd(intx,inty)//函数定义
{intz;z=x+y;returnz;}
4.递归和递推
5.结构体和共用体
一、结构体(struct)
1.定义:
结构体是由一系列具有相同数据类型或不同数据类型的数据构成的数据集合。
2.例子:
structstudent
{
intnum;
charname[20];
};
注意不要忽略最后的分号
3.定义结构体类型变量的方法:
strcut studentstudent1,student2;
4.大小:
(1)空结构体的大小为1byte.
(2)结构体的大小要是最严格(也就是占据内存最多的)的那个变量的大小的整数倍。
如:
structstudent
{
intnum;
charname;
};
占据的内存空间为8个byte。
structstudent
{
intnum;
charname;
charmark;
};
大小也是8个Byte。
按内存对齐方式说,应该是12个字节,所以这里有些不明白。
二、共用体(union)
1.定义:
union维护足够的空间来放置多个数据成员中的“一种”,而不是为每一个数据成员配置 空 间,在union中所有的数据成员公用一个空间,同一时间只能存储其中的一个数据成员,所有的成员具有相同的起始地址。
举例:
uniondata
{
inti;
charch;
floatf;
}
大小为4个字节。
2.引用方式:
不能引用共用体的变量,只能引用变量中的成员。
如a.i,a.ch.
6.strlen和sizeof的区别
sizeof与strlen是有着本质的区别,sizeof是求数据类型所占的空间大小,而strlen是求字符串的长度,字符串以/0结尾。
区别如下:
(1)sizeof是一个C语言中的一个单目运算符,而strlen是一个函数,用来计算字符串的长度。
(2)sizeof求的是数据类型所占空间的大小,而strlen是求字符串的长度
实例1:
printf("char=%d/n",sizeof(char)); //1
printf("char*=%d/n",sizeof(char*));//4
printf("int=%d/n",sizeof(int));//4
printf("int*=%d/n",sizeof(int*));//4
printf("long=%d/n",sizeof(long));//4
printf("long*=%d/n",sizeof(long*));//4
printf("double=%d/n",sizeof(double));//8
printf("double*=%d/n",sizeof(double*));//4
可以看到,char占1个字节,int占4个字节,long点4个字节,而double占8个字节。
但char*,int*,long*,double*都占4个字节的空间。
这是为什么呢?
在C语言中,char,int,long,double这些基本数据类型的长度是由编译器本身决定的。
而char*,int*,long*,double*这些都是指针,回想一下,指针就是地址呀,所以里面放的都是地址,而地址的长度当前是由地址总线的位数决定的,现在的计算机一般都是32位的地址总线,也就占4个字节。
实例2:
chara[]="hello";
charb[]={'h','e','l','l','o'};
strlen(a),strlen(b)的值分别是多少?
前面分析过,strlen是求字符串的长度,字符串有个默认的结束符/0,这个结束符是在定义字符串的时候系统自动加上去的,就像定义数组a一样。
数组a定义了一个字符串,数组b定义了一个字符数组。
因此,strlen(a)=5,而strlen(b)的长度就不确定的,因为strlen找不到结束符。
下面是网上的一个比较经典的例子,分析一下:
char*c="abcdef";
chard[]="abcdef";
chare[]={'a','b','c','d','e','f'};
printf("%d%d/n",sizeof(c),strlen(c));
printf("%d%d/n",sizeof(d),strlen(d));
printf("%d%d/n",sizeof(e),strlen(e));
输出的结果是:
46
76
614
分析一下:
第一行定义c为一个字符指针变量,指向常量字符串,c里面存放的是字符串的首地址。
第二行定义d为一个字符数组,以字符串的形式给这个字符数组赋值。
第三行定义的也是个字符数组,以单个元素的形式赋值。
当以字符串赋值时,"abcdef",结尾自动加一个"/0".
strlen(c)遇到/0就会结束,求的是字符串的长度,为6.
sizeof(c)求的是类型空间大小,在前面说过,指针型所点的空间大小是4个字节,系统地址总线长度为32位时。
strlen(d)也是一样,字符串赋值,自动添加/0,求字符串的长度当然是6.
sizeof(d)是求这个数组所占空间的大小,即数组所占内存空间的字节数,应该为7.
sizeof(e),数组e以单个元素赋值,没有/0结束符,所以所占空间的大小为6个字节。
strlen(e),去找/0结尾的字符串的长度,由于找不到/0,所以返回的值是一个不确定的值。
static全局变量和普通全局变量,static局部变量和普通局部变量,static函数和普通函数的区别
1普通全局变量前面加上static就成了static全局变量,他们的存储方式都是一样,存在静态存储方式。
区别在于作用范围,普通全局变量作用域是整个源程序,当一个程序有多个源文件组成时候,普通全局变量在每个源文件中都是有效的,而静态全局变量则限制了作用域,只在定义该变量的源文件中有效。
在同一个源程序中的其他源文件不能使用。
由于静态全局变量的作用域限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
static全局变量与普通全局变量区别static全局变量只初始化一次,不能别其他源文件引用。
static局部变量与普通局部变量区别:
static局部变量只初始化一次,下次引用的时候以上次的值。
、
static函数与不同函数的区别:
static函数在内存中只保存一份,普通函数被调用中维持一份拷贝。
2引用一个已经定义过的全局变量
要在需要引用的文件中加上extern,或者直接extern头文件
如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个编写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。
3.全局变量可不可以定义在可被多个.C文件包含的头文件中?
为什么?
答:
可以,在不同的c文件中,以static形式来声明同名全局变量。
可前提是其中只能有一个c文件对此变量赋处值,此时连接不会出错。
7.当指针指向二维数组时,行指针和列指针
8.形参和实参