1、What does the following program print ?
#include<iostream>
using namespace std;
int main()
{
int x = 2 , y , z ;
x *= (y=z=5) ; //先 z = 5 ; y = z; x = x * y = 2 * 5 = 10
cout<< x << endl ; // 输出 10
z = 3;
x == (y = z) ; cout<<x<<endl ; //先y=z=3; x与y是否相等,但是不管是否相等,x值 都不变,输出10
x = (y == z) ; cout<<x<<endl ; //先看y和z是否相等,相等返回一个布尔值 1 ,x=1 ;输出 1
x = ( y & z) ;cout<<x<<endl ; //首先y与z按位与,都是3 ,那么 x = 3, 输出3
x = (y && z);cout<<x<<endl ; //y与z进行与运算,由于y,z都不为0,都是真,则返回1,输出 1
y = 4;
x = (y |z);cout<<x<<endl ; //y|z => 4 | 3 = 7 输出7
x = (y || z); cout<<x<<endl ; // 一真返回1 ,输出1
}
2、What will be the output of the following C code ?
#define product(x) ( x * x )
int main(int argc,coust char[] *argv)
{
int i = 3, j , k;
j = product(i++); // j = i++ * i++ = 9 ; i =5
k = product(++i); // k = ++i * ++i ; k = 7*7 = 49
printf("j=%d,k=%d\n",j,k);
return 0;
}
3、下面程序的结果是什么?
char foo(void)
{
unsigned int a = 6;
int b = -20;
char c ;
(a+b>6)?(c = 1):(c=0); //unsigned int类型的数据和int类型的数据相运算后,自动转化为unsigned int类型,因此a+b的值不是-14,而是一个unsigned int 类型的数据4294967382.
return c; //返回值为1
}
(1)为了防止精度的丢失,如果有必要的话,类型总是被提升为较宽的类型。
(2)所有含有小于整型的有序类型的算数表达式在计算值之前其类型都会被转换成整型。
尽管算数转换的这些规则带给你的困扰可能多于启发,但是一般的思想就是尽可能地保留多类型表达式中涉及到的值的精度,这正是通过把不同类型提升到当前出现的最宽的类型来实现的。
4 .
找出 a b 之间最大的数字 int max = ( (a+b) + abs(a-b) )/2;
5. 不用借助中间变量进行交换 a , b
有两种方法可以选择:
一、使用差值 a = a + b; 缺点就是如果a、b都比较大的时候,会有越界的问题
b = a - b;
a = a - b;
二、使用异或 a = a ^ b; 考虑用异或的方法,不用担心越界的问题
b = a ^ b;
a = a ^ b;
6. 如何判断一段程序是由C编译程序还是由C++编译程序编译的
C++编译时定义了_cplusplus
C编译时定义了_STDC_
7.main主函数执行完毕后,是否可能会再执行一段代码?给出说明。
如果需要加入一段在main退出后执行的代码,可以使用atexit()函数注册一个函数,代码如下所示:
#include <head.h>
void fn1(void),fn2(void),fn3(void),fn4(void);
int main(int argc, const char *argv[])
{
atexit(fn1);
atexit(fn2);
atexit(fn3);
atexit(fn4);
printf("This is executed first.\n");
return 0;
}
void fn1()
{
printf("next.\n");
}
void fn2()
{
printf("executed ");
}
void fn3()
{
printf("is ");
}
void fn4()
{
printf("This ");
}
运行结果:
8. 预处理:
用预处理指令#define 声明一个常数,用以表示1年中有多少秒(忽略闰年问题)
解析:(1)#define语法的基本知识,不能以分号结束,括号的使用
(2)要懂得预处理器将为你计算常数表达式的值,因此,写出你是如何计算一年中有多少秒而不是手动的计算出实际的值,更会的有意义的。
(3)意识到这个表达式将使一个16位机的整数溢出,因此要用到长整形符号L,告诉编译器这个常数是长整形数。
如果你在表达式中用到了UL(表示无符号长整型),那么你就有一个好的起点。提高了你的第一印象。
#define SECONDS_PER_YEAR (60*60*24*365)UL
宏定义返回较小的一个:
#define MIN(A,B) ((A) <= (B) ? (A) : (B) )
#include <isdtream>
9. What does the keyword "const" means in C program? Please at least make two examples about the usage of const.
(1)可以定义const常量。(2)const可以修饰函数的参数和返回值,甚至函数的定义体。被const修饰的东西都受到强制的保护,可以预防以外的变动,能提高程序的健壮性。
10. What is the output of the following code?
sizeof的使用
#include <stdio.h>
#include <string.h>
using namespace std;
struct {
short a1;
short a2;
short a3;
}A; sizeof(A) = 6
struct {
long a1;
short a2;
}B; sizeof(B) = 8
A和B是两个结构体。在默认的情况下,为了方便对结构体内元素的访问和管理,当结构体内的元素的长度都小于处理器的位数的时候,便以结构体里面最长的数据元素为对齐单位,也就是说,结构体的长度一定是最长数据元素的整数倍。如果结构体内存在长度大于处理器位数的元素,那么就以处理器的位数为对齐单位。但是结构体内类型相同的连续元素将在连续的空间内,和数组一样。
结构体A中有3个short类型变量,各自以2个字节对齐,结构体对齐参数按默认的8字节对齐,则a1,a2,a3都取2字节对齐,sizeof(A)为6,也是2的整数倍.
B中的a1为4字节对齐的,a2为2字节对齐,结构体默认对齐参数是8,a1取4字节对齐,a2为两字节对齐,结构体大小为6,但不是4的整数倍,需要补空字节,增加到8时,符合所有的条件,则sizeof(B)=8
int main()
{
char *ss1 = "0123456789"; ss1字符串指针,指针的值为定值,so sizeof(ss1) = 4;
char ss2[] = "0123456789";
ss2为字符数组,初值未定,由填充值来定sizeof(ss2) = 11;
char ss3[100] = "0123456789";
ss3为分配了100大小的字符数组,sizeof(ss3) = 100;
int ss4[100];
ss4为整型数组,这个数组预分配了100,但是每个整型变量占4,所以sizeof(ss4) = 400;
char q1[] = "abc"; 与ss2类似,所以sizeof(q1) = 4;
char q2[] = "a\n"; \n算作一位的,所以sizeof(q2) = 3;
char *q3 = "a\n"; q3是一个字符指针,指针的大小是一个定值,所以sizeof(q3) = 4;
char *str1 = (char * )malloc(100);
char *str2 = (void *)malloc(100);
return 0;
}
11 . 求解下面程序的结果
#include<iostream>
using namespace std;
class A1
{
public :
int a;
sizeof()计算栈中分配的大小的,而静态变量存放在全局数据区的,
static int b;
所以sizeof(A1) = 4;
A1();
~A1();
};
class A2
{
public :
int a;
char c;
A2();
~A2();
};
class A3
{
public :
float a;
char c;
A3();
~A3();
};
class A4
{
public :
float a;
int b;
char c;
A4( );
~A4( );
};
class A5
{
public :
double d;
float a;
int b;
char c;
A5( );
~A5( );
};
int main( )
{
cout<<sizeof(A1)<<endl; 4
cout<<sizeof(A2)<<endl; 8
cout<<sizeof(A3)<<endl; 8
cout<<sizeof(A4)<<endl; 12
cout<<sizeof(A5)<<endl; 24
}
12. sizeof与strlen的区别
(1)sizeof操作符的结果类型是size_t,它在头文件中的typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节的大小。
(2)sizeof是算符,strlen是函数。
(3)sizeof可以用类型作为参数,strlen只能用char *做参数,且必须是以’\0‘结尾的。sizeof还可以用函数作为参数。比如:short f(); printf("%d\n",sizeof(f())); 输出的结果是2
(4)数组做sizeof的参数不退化,传递给strlen就退化为指针。
(5)大部分编译程序在编译的时候就把sizeof计算过了,是类型或是变量的长度。这就是sizeof(x)可以用来定义数组维数的原因:
char str[20] = "0123456789";
int a = strlen(str) = 10;
int b = sizeof(str) = 20;
( 6 )strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度,而不是类型占内存的大小。
( 7 )sizeof后如果是类型必须加括号,如果是变量名可以不加括号。这是因为sizeof是一个操作符,而不是个函数。
( 8 )当使用了一个结构类型或变量时,sizeof返回实际的大小。当使用一静态的空间数组时,sizeof返回全部数组的尺寸。sizeof操作符不能返回被动态分配的数组或外部的数组的尺寸。
( 9 )数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如fun(char[8])、fun(char []) 都等价于fun( char *)。在C++里传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小。如果想在函数内知道数组的大小,应该进入函数后用memcpy将数组拷贝出来,长度由另一个形参传进去。
例如:
fun( unsigned char *p1, int len)
{
unsigned char *buf = new unsigned char [len + 1];
memcpy( buf , p1 , len );
}
(10)计算结构变量的大小就必须考虑数据对齐的问题。为了使CPU存取的速度最快,C++在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫数据对齐。这样做可能会浪费一些内存,但在理论上CPU速度快了。当然,这样的设置会在读写一些特别的应用程序生成的数据文件或交换数据时带来不便。MS VC++中的对齐设定,有时候sizeof得到的与实际不等。一般在VC++中加上#pragma pack(n)的设定即可。或者要按字节序存贮,而不进行数据的对齐,可以再Options对话框中修改Advanced Compiler选项卡中的“Data Alignment”为按字节序对齐。
(11)sizeof不能用于函数类型、不完全类型或位字段。不完全类型指具有未知存储大小数据的数据类型,如未知存储大小的数组类型,未知内容的结构或联合类型,void类型等。
13. 一个空类所占的空间为1 ,多重继承的空类空间还是 1。
14. 内联函数和宏的差别是什么?
内联函数和普通函数相比可以加快程序的运行速度,因为不需要中断的调用,在编译的时候内联函数可以直接被镶嵌到目标代码中。而宏只是一个简单的替换。
内联函数要做参数类型检查,这是内联函数跟宏相比的优势。
inline是指嵌入代码,就是在调用函数的地方不是跳转,而是把代码直接写到那里去。对于短小的代码来说,inline可以带来一定的效率的提高,而且和C时代的宏函数相比,inline更安全可靠。可是这个需要是以增加空间为代价的。至于是否需要inline 函数,就需要根据你的实际情况进行取舍了。
inline一般用于如下情况:
(1)一个函数不断被重复调用。
(2)函数只有简单的几行,且函数内不包含for,while,switch语句。
一般来说,我们写的小程序没有必要定义成inline ,但是如果要完成一个工程项目,当一个简单函数被调用多次时,则应该考虑用inline。
宏是在代码处不加任何验证的简单的替代,而内联函数是将代码直接插入到调用处,而减少了普通函数调用时的资源消耗。
宏不是函数,只是在编译前(编译预处理阶段)将程序中有关字符串替换成宏体。
inline函数是函数,但在编译中不单独产生代码,而是将有关代码嵌入到调用处。
15. 指针和引用的差别
(1)非空区别。在任何情况下都不能使用指向空值的引用。一个引用必须总是指向某些对象。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高。
(2)合法性区别。在使用引用之前不需要测试他的合法性。相反,指针则应该总是被测试,防止其为空。
(3)可修改区别,指针与引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象的内容可以改变。
(4)应用区别。总的来说在以下情况下你应该使用指针:一是你考虑到存在不指向任何对象的可能(在这种情况下,你能设置指针为空),二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能够改变指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。
16. const常量赋值时,必须同时初始化
17. 函数的参数在调用和返回过程中的值,实参如果传的是指针的话很有可能会变。不是指针、如果是无返回值的,则不会改变。因为指针传的是地址,直接给地址赋值。而计算机中的地址是独一无二的,所以很有可能改变。 所以说实参可能会改变。
18.What results after run the following code?
int * ptr;
ptr = (int *)0x8000;
*ptr = 0xaabb;
解析:指针的问题
这样做会导致运行时错误,因为这种做法会给一个指针分配一个随意的地址,这是非常危险的。不管这个指针有没有被使用过,这么做都是不允许的。
19. 关于指针函数,函数指针。
(1)float(**def)[10];
def是一个二级指针,它指向的是一个一维数组的指针,数组的元素都是float。
(2)double*(*gh)[10];
gh是一个指针,它指向一个一维数组,数组元素都是double *。
(3)double(*f[10])();
f是一个数组,f有10个元素,元素都是函数的指针,指向的函数类型是没有参数 且返回double 的函数。
(4)int *(*(b)[10]);
b是一维数组 的指针。
(5) long (*fun)(int)
函数指针。
(6)int (*(*( F )(int , int)))(int)
F是一个函数指针,指向的函数的类型是有两个int参数并且返回一个函数指针的函数,返回的函数指针指向有一个int参数且返回int的函数。
20.记住数组名a作为代表数组的首地址,是一个常量指针,既然是常量当然是不容修改的。
21. int (*ptr)[] 是一个指向整型数组的指针。 数组指针。
int *ptr[] 是指针数组,ptr里面存的是地址。
22. 指针的应用:
a.一个整型数 int a;
b.一个指向整型数的指针 int * a;
c.一个指向指针的指针,它指向的指针是指向一个整型数 int **a;
d.一个有10个整型数的数组,int a[10];
e.一个有10个指针的数组,该指针是指向一个整型数。int *a[10];
f.一个指向有10个整型数数组的指针。int (*a)[10];
g.一个指向函数的指针,该函数有一个整型参数并返回一个整型数。int (*a)(int)
h.一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数。 int (*a[10])(int);
22.迷途指针
编程中很难发现的错误是迷途指针。迷途指针也叫悬浮指针、失控指针,是当对一个指针进行delete操作后——这样会释放它所指向的内存——并没有把它设置为空时产生的。而后,如果你没有重新赋值就试图再次使用该指针,引起的结果是不可预料的。程序崩溃算你走运。
总之,再删除指针后小心不要再使用它。虽然这个指针仍然指向原来的内存区域,但是编译器已经把这块内存区域分配给了其他的数据。再次使用你的指针会导致你的程序崩溃。更糟糕的情况是,程序可能表面上运行的很好,过不了几分钟就崩溃了。这被称为定时炸弹,可不是开玩笑。为了安全起见,再删除一个指针后,就把它设置为空指针(0),这样就可以消除它的危害。
当delete一个指针的时候,实际上仅是让编译器释放内存,但指针本身依然存在。这时它就是一个迷途指针。通常,如果在删除一个指针后又把它删除一次,程序就会变得非常的不稳定,任何情况都有可能发生。但是如果你只是删除了一个空指针,则什么事都不会发生,这样做非常安全。
我们在使用迷途指针或者空指针的时候是非法的,而且有可能造成程序崩溃。如果指针是空指针,尽管同样是程序的崩溃,但是它同迷途指针造成的崩溃相比是一种可以预料的崩溃。这样调试起来会方便很多。
23 C++中有了malloc /free,为什么还需要new/delete ?
malloc/free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象 的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器的控制范围之内,不能够把执行构造函数和析构函数的的任务强加于malloc/free。
因此C++语言需要完成动态内存分配和初始化工作的运算符new,以及一个能完成清理工作的运算符delete。new/delete不是库函数,而是运算符。