sizeof是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。
与strlen的区别:
一、sizeof
是运算符,确切的说是一个编译时运算符,参数可以是数组、指针、类型、对象、函数等。用于统计类型或者变量所占的内存字节数。由于在编译时计算,因此sizeof不能用来返回动态分配的内存空间的大小。
二、strlen()
是C标准库中的字符串函数,要在运行时才能计算。参数必须是字符型指针(char*), 且必须是以'\0'结尾的。它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符'\0'。返回的长度大小不包括'\0'。
三、实例
1、char *str = "hello";
strlen(str); //它的值是5,因为hello这个字符串有5个字符
sizeof(str); //它的值是4,因为char *是一个指针类型,它占4个字节。
sizeof("hello"); //它的值是5,是因为hello有5个字符,每一个字符占1个字节。
2、int a[2] = {0};
sizeof(a); //它的值是8,因为a中有2个int型变量,每个int型占4个字节,所以8字节
strlen(a) //a相当于一个指针,但是strlen只能接受char*类型,所以编译时出错
3、char arr[10] = "Hello";
int len_one = strlen(arr);
int len_two =sizeof(arr);
cout << len_one <<" and " << len_two << endl;
输出结果为:5 and 10
strlen只关心存储的数据内容,不关心空间的大小和类型。
sizeof返回定义arr数组时,编译器为其分配的数组空间大小,不关心里面存了多少数据(10x1)。
4、char * parr =new char[10];
int len_one = strlen(parr);
int len_two = sizeof(parr);
int len_three = sizeof(*parr);
cout << len_one <<" and " << len_two << " and " <<len_three << endl;
输出结果:3 and 4 and 1
第一个输出结果3实际上每次运行可能不一样,这取决于parr里面存了什么(从parr[0]开始直到遇到第一个'\0'结束);
第二个结果实际上本意是想计算parr所指向的动态内存空间的大小,但是事与愿违,sizeof认为parr是个字符指针,因此返回的是该指针所占的空间(指针的存储用的是长整型,所以为4)
第三个结果,由于*parr所代表的是parr所指的地址空间存放的字符,所以长度为1。
面试题:定义一个空的数据类型,里面没有任何成员变量和成员函数,对该类型求sizeof,得到的结果是多少?
答案:是1,为什么不是0?空类型的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明该类型的实例时,它必须在内存中占有一定的空间,否则无法使用这些实例(也就不能求sizeof了),至于占用多少内存,由编译器决定,Visual Studio中每个空类型的实例占用1字节的空间。
扩展1:如果在该类型中添加一个构造函数和析构函数,再求sizeof,得到的结果是多少?
答案:还是1。调用构造函数和析构函数只需要知道函数的地址即可,而这些地址只与类型相关,而与类型的实例无关,编译器也不会因为这两个函数而在实例内添加任何额外的信息。
注:不管添加的是构造函数还是析构函数还是其它任何类型的函数,都是这个结果。
扩展2:那如果把析构函数标记为虚函数呢?
答案:C++的编译器一旦发现一个类型中有虚函数,就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针,在32位机器上,一个指针占4字节空间,因此求sizeof得到4;如果是64位则为8。测试用例:
[cpp] view plain copy
1. #include <stdio.h>
2.
3. struct nullType { };
4.
5. struct type1
6. {
7. type1() {}
8. ~type1() {}
9. int print() { printf("Alexia"); return 0; }
10. };
11.
12. struct type2
13. {
14. type2() {}
15. virtual ~type2() {}
16. };
17.
18. int main()
19. {
20. printf("sizeof(nullType) = %d\n", sizeof(nullType));
21. printf("sizeof(type1) = %d\n", sizeof(type1));
22. printf("sizeof(type2) = %d\n", sizeof(type2));
23.
24. return 0;
25. }
代码的优化,给出下一段代码,请做出最好的优化
intf(int n) {
if(n<=4)
return n*n;
else {
return f(n-4)*f(n-1) -f(n-2)*f(n-2);
}
}
无非是将递归转化为循环,防止重复计算中间值,跟斐波那契数列f(n)=f(n-1)+f(n-2)一样,解决方式也一样,就是利用几个临时变量保存中间值,然后每次循环都更新临时变量即可。过程没啥好说的,直接给出代码即可。
intf2(int n) {
int first = 1;
int second = 4;
int third = 9;
int fourth = 16;
if(n<=4)
return n*n;
for(int i = 5; i <= n; ++i) {
int tmp = fourth * first - third * third;
first = second;
second = third;
third = fourth;
fourth = tmp;
}
return fourth;
}
1、局部变量能否和全局变量重名?
答:能,局部会屏蔽全局。要用全局变量,需要使用 "::"
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。
2、如何引用一个已经定义过的全局变量?
答:可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。
3、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
答:可以,在不同的C文件中以static形式来声明同名全局变量。前提是只能有一个C文件中对此变量赋初值,连接才不会出错
4、语句for( ;1 ;)有什么问题?它是什么意思?
答:和while(1)相同。
5、do……while和while……do有什么区别?
答:前一个循环一遍再判断,后一个判断以后再循环
6、请写出下列代码的输出内容
[cpp] view plain copy
1. #include <stdio.h>
2. main()
3. {
4. int a,b,c,d;
5. a=10;
6. b=a++;
7. c=++a;
8. d=10*a++;
9. printf( "b,c,d:%d,%d,%d",b,c,d);
10. return 0;
11. }
答:10,12,120
7、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
答:static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
(1)用static定义的全局变量,构成静态的全局变量,若是一个源程序由多个源文件组成,一般的全局变量在各个源文件中都是有效的,而用static修饰的全局变量只在定义该变量的源文件中是有效的,因此static限制了全局变量的作用范围。
(2)普通局部变量在所在的函数每次调用的时候都会被重新分配存储空间,函数结束后,就会回收该存储空间。而用static修饰的局部变量不会,它的值始终保持着。
(3)static函数与普通函数作用域不同,它仅作用于定义它的源文件中。
(4)储存方式:程序的局部变量存在于(堆栈)中,全局变量存在于(静态区/全局区)中,动态申请(new)数据存在于(堆)中。
8.对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?
c用宏定义, #define S(a,b) a*b
c++用inline
引入内联函数的目的是为了解决程序中函数调用的效率问题。内联函数是指用inline关键字修饰的函数。任何在类的说明部分定义的
函数都会被自动的认为是内联函数。
它与一般函数所不同之处只在于函数调用的处理。
内联函数必须是和函数体声明在一起才有效。像这样的申明Inline Tablefunction(int I)是没有效果的,编译器只是把函数作为普通的函数声明,
我们必须定义函数体。
Inline tablefunction(int I) {return I*I}; 这样我们才算定义了一个内联函数。我们可以把它作为一般的函数一样调用。
但是执行速度确比一般函数的执行速度要快。
(C#有无内联函数:C#就没有头文件,哪来的内联外联?要说内联,应该全都是内联,因为所有函数什么的都定义在一个文件里。存在内联函数,但要注意,在何处内联代码的决定完全由CLR做出,我们无法使用像C++中inline这样的关键字来控制那些方法是内联的。)
static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
答:
1) 全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
2) 从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
3) static函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
综上所述:
static全局变量与普通的全局变量有什么区别:
static全局变量只初使化一次,防止在其他文件单元中被引用;
static局部变量和普通局部变量有什么区别:
static局部变量只被初始化一次,下一次依据上一次结果值;
static函数与普通函数有什么区别:
static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
什么是预编译?何时需要预编译?
什么是预编译:
预编译又称为预处理 , 是做些代码文本的替换工作。
处理以# 开头的指令 , 比如拷贝 #include 包含的文件代码,#define 宏定义的替换 , 条件编译等,就是为编译做的预备工作的阶段。
主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
C 编译系统在对程序进行通常的编译之前,首先进行预处理。
c 提供的预处理功能主要有以下三种:
1 )宏定义
2 )文件包含
3 )条件编译
何时需要预编译:
总是使用不经常改动的大型代码体。
程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个“预编译头”
const关键字
const是constant的简写,只要一个变量前面用const来修饰,就意味着该变量里的数据可以被访问,不能被修改。也就是说const意味着“只读”readonly
规则:const离谁近,谁就不能被修改;
const修饰一个变量,一定要给这个变量初始化值,若不初始化,后面就无法初始化。
本质:const在谁后面谁就不可以修改,const在最前面则将其后移一位,二者等效。
const关键字作用:
- 为给读你代码的人传达非常有用的信息,声明一个参数为常量是为了告诉用户这个参数的应用目 的;
- 通过给优化器一些附加信息,使关键字const也许能产生更紧凑的代码;
- 合理使用关键字const可以使编译器很自然的保护那些不希望被修改的参数,防止无意的代码修改,可以减少bug的出现;
const关键字应用
- 欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了;
- 对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
- 在一个函数声明中,const可以修饰形参表明他是一个输入参数,在函数内部不可以改变其值;
- 对于类的成员函数,有时候必须指定其为const类型,表明其是一个常函数,不能修改类的成员变量;
- 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。