1,extern “C”
extern “C” 包含双重含义,从字面上即可得到:
- 首先,被它修饰的目标是“extern”的;
- 其次,被它修饰的目标是“C”的。
- 被extern "C"限定的函数或变量是extern类型的。
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语句:
extern int a;
**仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。**变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。
通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。 - 在C++源文件中的语句前面加上extern “C”,表明它按照类C的编译和连接规约来编译和连接,而不是C++的编译的连接规约。
extern “C”{}的作用,主要还是解决C++代码中调用C代码的问题。
2,变量的关键字
对于题目:
为了提高程序的运行速度,在函数中对于整型或指针可以使用什么类型的变量?
- auto
- register
- static
- extern
对于这个问题,首先要搞清楚这四个关键字的含义:
- auto 变量类型推演,
- register 建议编译器将该变量放入cpu,存取速度远远高于对内存的存取速度,因此,使用cpu的寄存器可提高执行速率
- static 静态变量
- extern 声明变量,常用于多文件需要使用同一变量时
因此,这个问题应该是register。
3,指针问题
对于题目:
main()
{ char *p="abcdefgh",*r;
long *q;
q=(long*)p;
q++;
r=(char*)q;
printf("%s\n",r);
}
最后的输出应该是什么?
我们从题目可以得知,我们将char类型的指针转换成了long类型的指针,因为char是一个字节,long是4个字节,因此,:
q++
一次相当于移动了4个char
因此,最后的输出结果为efgh
4,边界对齐问题
对于问题:
在一个16位的机器,以下结构由于边界对齐浪费了多少空间?
struct{
char a;
int b;
char c;
}
在这道题目中,机器是16位机器,char类型占1个字节,int型占2个字节。
对于结构体中的字节对其规则:
- 数据成员自对齐,即数据成员起始地址为数据类型长度的整数倍,如该题int型b只能从0,2,4…等地址处开始放;
- 结构体总长度是结构体中有效对齐值的整数倍,有效对齐值,如该题没明确指出,则为最长数据成员(int b)长度的整数倍;
因此,对于这道题,规则1,int b从2处开始放,此时结构体总长度为(1 + 1 + 2 + 1 = 5);再根据规则2,结构体总长度应为2的整数倍,故应为6。下图中描红的两个字节表示被浪费的。
5,数组与指针
对于一个数组而言,其数组名就是该数组的首地址。
- 数组名+1:是加数组 元素大小的字节数
- 数组名&取地址加1:是加整个数组大小的字节数
输出结果:
6,大端模式和小端模式
对于题目:
某计算机存储器按字节变址,采用小端方式存放数据。假定编译器规定int 型和 short 型长度分别为 32 位和 16 位,并且数据按边界对齐存储。某 C 语言程序段如下:
struct
{
int a;
char b;
short c;
}record;
record.a = 273;
若record
变量的首地址为 0xC008
,则地址 0xC008
中内容及 record.c
的地址是多少?
- 大端模式:数据低位放在内存高位;
- 小端模式:数据低位放在内存低位;
对于这一题,将a从十进制转换成十六进制,则:
a = 273 = 0x0000 0111(32位)
一个字节存储8位,因此0x0000 0111分为0x00,0x00,0x01,0x11四个字节来储存。其中0x11是数据的低位,根据小端模式应该存在地址的地位,也就是0xC008。
而就对齐规则而言,简单来说,对于int型而言,起始地址为4的倍数;对于char类型而言,起始地址为任意字节皆可;对于short类型而言,起始地址为2的倍数。
7,字符串常量问题
对于问题:
对于下面的代码
char* s1 = "Hello world";
char s2[] = "Hello world";
s1[2] = 'E'; // 1
s2[2] = 'E'; // 2
*(s1 + 2) = 'E'; // 3
*(s2 + 2) = 'E'; // 4
对于这个问题,指针指向字符串时,字符串是常量,存储在常量区;而指针存储在栈区,不能对该字符串进行修改。
char *s1 = "hello,world"
,在内存中s1指针变量存储在栈区,而“hello, world”存储在常量区,常量是不能被改变的.
因此,s1存储在常量区,1和3都是非法的。
严格意义上说,s1的赋值语句应该是:
const char* s1 = "Hello world";
8,C++基础知识点
- 静态局部变量存储在静态存储区,而局部变量存储在堆栈区,确切的说是栈区。
- 局部变量和全局变量是可以重名的,而且一般来说在某个函数体内,局部变量是会覆盖全局变量的。
- 内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。
- C++不是一种类型安全的语言。 C++而言,我们可以把0作为false,非零作为true。一个函数就算是bool类型的,我们还是可以返回int类型,并且自动把0转化成false,非零转化成true。相比之下java不能把int类型转化成bool类型。所以java是一种类型安全的语言,而C++并不是。
9,析构问题
对于问题:
如果有一个类是 myClass , 关于下面代码会有什么问题?
myClass::~myClass(){
delete this;
this = NULL;
}
在实验上:
此处代码可以看出this指针内容为const,是一个指针常量,不允许进行修改,故编译不成功。
如果将代码修改为:
~MyClass() { delete this; }
此时可以通过编译,但是会导致堆栈溢出,因为delete this
会调用析构函数,而析构函数又调用delete this
,陷入死循环。
10,空类自动生成的函数
声明一个空类之后,编译器就会自动的为他声明:
- 一个copy构造函数
- 一个copy assignment操作符
- 一个析构函数。
此外如果没有声明任何构造函数,编译器也会自动声明:
- 一个default构造函数。
所有这些函数都是public且inline的。
class Empty
{
public:
Empty(); //缺省构造函数
Empty(const Empty &rhs); //拷贝构造函数
~Empty(); //析构函数
Empty& operator=(const Empty &rhs); //赋值运算符
Empty* operator&(); //取址运算符
const Empty* operator&() const; //取址运算符(const版本)
};