1. a和&a有什么区别?
#include "stdio.h"
int main()
{
int a[5] = { 1,2,3,4,5 };
int *ptr = (int *)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
- a 在这里代表是的数组首元素的地址即 a[0]的首地址,其值为 0x005efda0。
- &a 代表的是数组的首地址,其值为 0x005efda0。
- a与&a值相同,代表的含义不同。
- a+1 的值是 0x005efda0+1*sizeof(int),等于 0x005efda4。
- &a+1 的值是(0x005efda0 +(sizeof(a)= 5*sizeof(int))= 0x005efdb4 {0xcccccccc, 0xcb626a90, 0x005efdd0, 0x0029200e, 0x00000001}
2.区别以下指针类型?
int *p[10];
int (*p)[10];
int *p(int);
int (*p)(int);
- int *p[10]表示指针数组,强调数组概念,是一个数组,数组大小为10,数组内每个元素都是指向int类型的指针变量。
- int (*p)[10]表示数组指针,强调是指针,只有一个变量,是指针类型,不过指向的是一个int类型的数组,这个数组大小是10。
- int *p(int)是函数声明,函数名是p,参数是int类型的,返回值是int *类型的。
- int (*p)(int)是函数指针,强调是指针,该指针指向的函数具有int类型参数,并且返回值是int类型的
3. 常量指针和指针常量区别?(有待看书确认,primer p56)
- 常量指针定义:又叫常指针(常量的指针),即这是个指向常量的指针,说明指针的值(地址)是常量,而不是地址指向的值。可以写作int const *p或const int *p
- 指针常量定义:指针是个常量,必须初始化,一旦初始化完成,它的值(也就是存放在指针中的地址)就不能在改变了,即不能中途改变指向,如int *const p
4.strlen和sizeof区别?
- sizeof 是运算符,结果在编译时获。参数可以是任何数据的类型或者数据。
- strlen 是函数,结果在运行时获得。参数只能是字符指针且结尾是'\0'的字符串。
- sizeof指针得到的是本指针的大小,sizeof引用得到的是引用所指向变量的大小。
int main(int argc, char const *argv[]){
//ziseof 会把从主函数中传进来的字符数组当作是指针来处理。指针的大小又是由机器来决定,而不是人为的来决定的。
const char* str = "name";
sizeof(str); // 取的是指针str的长度,64位计算机下 是8。
strlen(str); // 取的是这个字符串的长度,不包含结尾的 \0。大小是4
//--------------------------------------------------------
char str[] = "Hello World";
char str1[100] = "Hello World";
cout << strlen(str) << endl; //10。测量的是字符的实际长度,以'\0'结束 且不包含'\0'
cout << sizeof(str) << endl; //11。测量的是字符的分配大小,如果未分配大小,则遇到'\0' 结束(包含'\0' ,也就是strlen测量的长度加1)
cout << sizeof(str1) << endl; //100。如果已经分配内存大小,返回的就是分配的内存大小。
return 0;
}
5.变量声明和定义区别?
- 定义 = 声明 + 初始化(就是给一个值)
- 声明仅仅是把变量的声明的位置及类型提供给编译器,并不分配内存空间;
- 定义要在定义的地方为其分配存储空间
- 相同变量可以在多处声明(外部变量extern),但只能在一处定义。
- extern声明不是定义,不分配存储空间。extern告诉编译器变量在其他地方定义了。
- 例如:extern int i; //声明,不是定义 ; int i; //声明,也是定义
6.宏定义和typedef区别?
- 宏 主要用于定义常量及书写复杂的内容;typedef主要用于定义类型别名。
- 宏 替换发生在编译之前,在预处理阶段完成替换,之后被替换的文本参与编译,属于文本插入替换;typedef是编译的一部分。
- 宏 不是语句,不在在最后加分号;typedef是语句,要加分号标识结束。
-
示例1: 先定义: typedef char* PSTR;
-
然后: int mystrcmp(const PSTR, const PSTR);
-
const PSTR 实际上相当于const char*吗?不是的,它实际上相当于char* const。
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换。
-
示例2: 下述两者效果不同
-
typedef (int*) pINT; // pINT a,b;的效果同int *a; int *b;表示定义了两个整型指针变量。typedef具有一定的封装性
-
#define pINT2 int* //pINT2 a,b;的效果同int *a, b;表示定义了一个整型指针变量a和整型变量b。
-
-
7.既然有了malloc/free,C++中为什么还需要new/delete呢?直接用malloc/free不好吗?
- 在对非基本数据类型的对象使用的时候,对象创建的时候还需要执行构造函数,销毁的时候要执行析构函数。而malloc/free是库函数,是已经编译的代码,所以不能把构造函数和析构函数的功能强加给malloc/free,所以new/delete是必不可少的。
- malloc仅仅分配内存空间,free仅仅回收空间,不具备调用构造函数和析构函数功能,用malloc分配空间存储类的对象存在风险;new和delete除了分配回收功能外,还会调用构造函数和析构函数。
- new: 1.调用名为operator new的标准库函数分配足够空间,2.调用相关对象的构造函数,返回指向构造后的对象的指针。
- delete:1.对指针所指对象运行析构函数;2. 调用名为operator delete的标准库函数释放该对象所用内存
- malloc和free是标准库函数,支持覆盖; new和delete是运算符,支持重载。
- new是封装了malloc,直接free不会报错,但是这只是释放内存,而不会析构对象。
8.你觉得堆快一点还是栈快一点?
- 栈由系统分配,速度快,不会有碎片,是一块连续的内存区域,大小是操作系统预定好的. 一般默认是4M。栈有静态分配和动态分配,静态分配由编译器完成(如局部变量分配),动态分配由alloc函数分配,但栈的动态分配的资源由编译器进行释放,无需程序员实现。
- 堆由程序员分配,速度慢,且会有碎片,是不连续的内存区域(因为系统是用链表来存储空闲内存地址,自然不是连续的),堆大小受限于计算机系统中有效的虚拟内存,一般是 1G - 4G。堆都是动态分配。
9.指针和引用的区别
- 指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是原变量的别名
- 指针可以有多级,引用只有一级
- 指针可以为空,引用不能为NULL且在定义时必须初始化 且 在初始化之后不可再改变。
- 当把指针作为参数进行传递时,也是将实参的一个拷贝传递给形参,两者指向的地址相同,但不是同一个变量,在函数中改变这个变量的指向不影响实参,而引用却可以。
- 引用本质是一个指针,同样会占4字节内存;指针是具体变量,需要占用存储空间(,具体情况还要具体分析)。
- 类对象作为参数传递的时候使用引用。需要返回函数内局部变量的内存的时候用指针。使用指针传参需要开辟内存,用完要记得释放指针,不然会内存泄漏。而返回局部变量的引用是没有意义的
void test(int *p)
{
int a=1;
p=&a;
cout<<p<<" "<<*p<<endl;
}
int main(void)
{
int *p=NULL;
test(p);
if(p==NULL)
cout<<"指针p为NULL"<<endl;
return 0;
}
//运行结果为:
//0x22ff44 1
//指针p为NULL
void testPTR(int* p) {
int a = 12;
p = &a;
}
void testREFF(int& p) {
int a = 12;
p = a;
}
void main()
{
int a = 10;
int* b = &a;
testPTR(b);//改变指针指向,但是没改变指针的所指的内容
cout << a << endl;// 10
cout << *b << endl;// 10
a = 10;
testREFF(a);
cout << a << endl;//12
}
10.结构体内存对齐问题?
- 结构体内成员按照声明顺序存储,第一个成员地址和整个结构体地址相同。
- 未特殊说明时,按结构体中size最大的成员对齐(若有double成员,按8字节对齐。)
11.在main执行之前和之后执行的代码可能是什么?
main函数执行之前,主要就是初始化系统相关资源:
- 设置栈指针
- 初始化静态
static
变量和global
全局变量,即.data
段的内容 - 将未初始化部分的全局变量赋初值:数值型
short
,int
,long
等为0
,bool
为FALSE
,指针为NULL
等等,即.bss
段的内容 - 全局对象初始化,在
main
之前调用构造函数,这是可能会执行前的一些代码 - 将参数
argc
,argv
等传递给main
函数,然后才真正运行main
函数 __attribute__((constructor))
main函数执行之后:
- 全局对象的析构函数会在main函数之后执行;
- 可以用
atexit
注册一个函数,它会在main 之后执行; __attribute__((destructor))