A静态链接库不能有同名全局函数2.(1) 请说明以下局部变量、全局变量和静态变量的区别?
B动态链接库不能有同名全局函数
C两个静态链接库的同名全局函数不能同时加载
D两个动态链接库的同名全局函数不能同时加载
分析:若两个有A.lib,B.lib,两个静态lib文件,并且全部链接,且A有全局函数fun1,fun2,B有fun2,fun3,如果在代码中调用了属于A的函数也调用属于B的函数(可以不是同名的,比如fun1和fun3),那么连接时会发生冲突(fun2);但是如果只调用了其中一个静态库中的函数,就不会发生冲突,链接器会自动丢掉那个没有被调用过的lib,而且冲突是在编译时不是在运行时。
(2) 局部变量是否可以和全局变量同名?
(1) :
从作用域看:
全局变量
具有全局作用域,全局变量只需在一个源文件中定义,就可以作用于所有的源文件,当然,在不包含该全局变量定义的源文件需要使用extern关键字再次声明这个全局变量;
静态局部变量
具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有函数可见,而静态句柄变量只对定义它的函数体始终可见;
局部变量
也是局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在的,而是只在函数执行期间存在,函数的一次调用执行结束后,局部变量被撤销,其所占用的内存也被回收;
静态全局变量
具有全局作用域,它和全局变量的区别在于如果程序包含多个文件的话,它仅作用于定义它的文件中,即被static关键字修饰过的全局变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量,且不会冲突。
从分配内存空间看:全局变量、静态局部变量、静态全局变量都是在静态存储区中分配空间的;而局部变量是在栈里分配空间的。
(2)局部变量和全局变量可以同名,在函数内引用这个变量时,会优先使用局部变量,而不是全局变量,要明确调用全局变量时,可以在这个变量名前加全局作用符:”::”。
3.请简述链表和数组的区别和优缺点。
1)数组是顺序存储的,元素在内存空间中一般是连续的;而链表元素间则是一种随机的状态,不一定是顺序连续的;
2)数组一旦显式声明后,其大小是固定的,不能动态扩充;而链表可以动态添加节点元素;
3)链表的插入删除操作简单,不需要移动元素的位置,但它不支持索引操作,要访问某个元素需要遍历链表进行查找;数组支持索引操作,可直接定位到第几个元素位置处,但数组的插入和删除操作时间和空间耗费大,一般需要移动元素的位置。
4.请找出下面程序的错误(转载)
#include <stdio.h>
struct String
{
char* buffer;
unsigned long size;
};
void resize(String* str, int size)
{
char *tempbuffer = new char[size];
if(str->buffer != 0)
{
if(size > str->size)
{
size = str->size;
}
for(int i=0; i<size; ++i)
{
tempbuffer[i] = str->buffer[i];
}
}
str->buffer = tempbuffer;
str->size = size;
}
void copy(String* a, String* b)
{
resize(a, b->size);
for(int i=0; i<b->size; ++i)
{
a->buffer[i] = b->buffer[i];
}
}
int main(int argc, char* argv[])
{
String a;
resize(&a, 3);
a.buffer[0] = 'a';
a.buffer[1] = 'b';
a.buffer[2] = 'c';
String b;
copy(&b, &a);
printf("%s %s", a.buffer, b.buffer);
}
解答:正确的代码如下:
#include <stdio.h>
#include <assert.h> //加头文件:#include<assert.h>
struct String
{
char* buffer;
unsigned long size;
};
void resize(String* str, int size)
{
assert(str != NULL); //加一句断言是否为空
char *tempbuffer = new char[size+1]; //将数组大小再加1,用于存放结束标志’/0’
if(str->buffer != NULL) //改为标准指针判断非空:!=NULL
{
if(size > str->size)
{
str->size = size; //顺序相反了
}
int i; //独立到外面来
for(i=0; i<size; ++i)
{
tempbuffer[i] = str->buffer[i];
}
tempbuffer[i] = '/0'; //加个字符数组结束符
}
str->buffer = tempbuffer;
str->size = size;
}
void copy(String* a, String* b)
{
resize(a, b->size);
for(int i=0; i<b->size; ++i)
{
a->buffer[i] = b->buffer[i];
}
}
int main(int argc, char* argv[])
{
String a;
a.buffer = new char[1]; //需分配一个空间,后面再调整大小
resize(&a, 3);
a.buffer[0] = 'a';
a.buffer[1] = 'b';
a.buffer[2] = 'c';
String b;
b.buffer = new char[1]; //需分配一个空间,后面再调整大小
copy(&b, &a);
printf("%s %s", a.buffer, b.buffer);
return 0; //加个返回值
}
5.makefile文件的作用是什么?
makefile文件保存了编译器和链接器的参数选项,还表述了所有源文件之间的关系(源代码文件需要的特定的包含文件、可执行文件要求包含的目标文件模块及库等)。创建程序make首先读取makefile文件,然后激活编译器、链接器、资源编译器和链接器以便产生最后的输出,最后输出并生成的通常是可执行文件。创建程序make利用内置的推理规则来激活编译器,以便通过对特定cpp文件的编译来产生特定的obj文件。
6.一般析构函数都要写成虚函数,请问为什么要这样做,编程给出实例说明这样做的作用?
先看实例代码:
#include <iostream>
class ASCEBase
{
public:
ASCEBase(){};
virtual ~ASCEBase(){std::cout<<"ASCEBase destructor!"<<std::endl;};
virtual void Broadcast()
{
std::cout<<"See you in another life ASCEBase!"<<std::endl;
}
};
class ASCEDerive : public ASCEBase
{
public:
ASCEDerive(){};
~ASCEDerive(){std::cout<<"ASCEDerive destructor!"<<std::endl;}
void Broadcast()
{
std::cout<<"See you in another life ASCEDerive!"<<std::endl;
}
};
int main()
{
ASCEBase *pAsce = new ASCEDerive;
pAsce->Broadcast();
delete pAsce;
system("pause");
return 0;
}
程序运行后输出是:
See you in another life ASCEDerive!
ASCEDerive destructor!
ASCEBase destructor!
这样的程序是正确的,但是,如果将基类ASCEBase析构函数前的virtual去掉,则输出结果是:
See you in another life ASCEDerive!
ASCEBase destructor!
即,派生类ASCEDerive的析构函数并没有被调用!一般情况下类的析构函数中是释放内存资源的代码,这样一来,就很可能造成内存泄漏。
因此,一般将析构函数写出虚函数,是为了当这个类用作基类时,用这个基类指针删除它的派生类的对象时,派生类的析构函数能够被调用到。当然,当一个类不打算作为基类使用时,我们一般不将其声明为虚函数,因为当类里面存在虚函数时,编译器会自动给这个类添加一个虚函数表,里面存放指向虚函数的指针,这无疑会增大类的存储空间。