答案:
#include
using namespace std;
void swap1(int p, int q)
{
int temp;
temp = p;
p = q;
q = temp;
}
void swap2(int *p, int *q)
{
int *temp;
*temp = *p;
*p = *q;
*q = *temp;
}
void swap3(int *p, int *q)
{
int *temp;
temp = p;
p = q;
q = temp;
}
void swap4(int *p, int *q)
{
int temp;
temp = *p;
*p = *q;
*q = temp;
}
void swap5(int &p, int &q)
{
int temp;
temp = p;
p = q;
q = temp;
}
int main()
{
int a=1, b=2;
// swap1(a,b);
// swap2(&a, &b);
// swap3(&a, &b);
// swap4(&a, &b);
// swap5(a,b);
cout << a << " " << b << endl;
return 0;
}
解析:
这道题考察函数参数传递、值传递、指针传递(地址传递)、引用传递。
int *temp; *temp = *p;
是不符合逻辑的一段代码,int *temp新建了一个指针(但没有分配内存)。 * temp= * p不是指向而是拷贝。把 * p所指向的内存里的值(也就是实参a的值)拷贝到 * temp所指向内存里了。但是int *temp不是不分配内存吗?的确不分配,于是系统在拷贝时临时给了一个随机地址,让它存值。分配的随机地址是个“意外”,且函数结束后不收回,造成内存泄露。那么swap2到底能否实现两数交换吗?这要视编译器而定,在vscode可以通过测试,但是在更加“严格”的编译器如vs2023,这段代码会报错。int *temp; temp = p;
int * temp新建了一个指针(但没有分配内存)。temp=p是指向而不是拷贝。temp指向了 * p所指向的地址(也就是a)。函数swap3不能实现两数的交换,这是因为函数体内只是指针的变化,而对地址中的值却没有改变。答案:swap4函数和swap5函数
#include
using namespace std;
int main()
{
int a[3];
a[0] = 0;
a[1] = 1;
a[2] = 2;
int *p, *q;
p = a;
q = &a[2];
cout << a[q - p] << '\n';
return 0;
}
解析:
首先,定义了一个整型数组a,它有3个元素。然后,将数组a的第一个元素赋值为0,第二个元素赋值为1,第三个元素赋值为2。
接下来,定义了两个整型指针变量p和q。将指针变量p指向数组a的首地址,即a[0]的位置。将指针变量q指向数组a的第三个元素的地址,即a[2]的位置。
然后,使用cout语句输出a[q - p]的值。这里的[q - p]表示指针q和指针p之间的偏移量,即q指向的元素在数组a中的位置与p指向的元素在数组a中的位置之间的差值。由于q指向a[2],p指向a[0],所以[q - p]的值为2-0=2。因此,输出a[2]的值,即2
答案:2
#include
using namespace std;
class A {
public:
int _a;
A()
{
_a = 1;
}
void print()
{
printf("%d", _a);
}
};
class B :public A {
public:
int _a;
B()
{
_a = 2;
}
};
int main()
{
B b;
b.print();
printf("%d", b._a);
return 0;
}
A.22
B.11
C.12
D.21
解析:
B类中的_a把A类中_a的覆盖掉了。在构造B类时,先调用A类的构造函数。所以A类的_a是1,而B类的_a是2。
答案:C
A.函数的形参在函数未调用时预分配存储空间
B.若函数的定义出现在主函数之前,则可以不必再说明
C.若一个函数没有return语句,则什么值都不返回
D.一般来说,函数的形参和实参的类型应该一致
解析:
A:错误的,调用到实参才会分配空间。
B:函数需要在它被调用之前被声明,这个跟main()函数无关。
C:错误的,在主函数main中可以不写return语句,因为编译器会隐式返回0;但是在一
般函数中没return语句是不行的。
D:正确的。
答案:D
答案:
void (*f)() // 函数指针
void * f() // 函数返回指针
const int * // const指针
int * const // 指向const的指针
const int * const // 指向const的const指针
(1)float(**def)[10];
(2)double * ( * gh)[10];
(3)double( * f[10])();
(4)int * (( * b)[10]);
(5)Long ( * fun)(int)
(6)Int (*(*F)(int,int))(int)
答案:
(1)float(* * def)[10];
def是一个二级指针,它指向的是一个一维数组的指针,数组的元素都是float。
(2)double*(*gh)[10];
gh是一个指针,它指向一个一维数组,数组元素都是double *。
(3)double(*f[10])();
f是一个数组,f有10个元素,元素都是函数的指针,指向的函数类型是没有参数且返回double的函数。
(4)int * (( * b)[10]);
就跟“int * (b)[10]”是一样的,是一维数组的指针。
(5)Long ( fun)(int)
函数指针。
(6)Int ( * (*F)(int,int))(int)
F是一个函数的指针,指向的函数的类型是有两个int参数并且返回一个函数指针的函
数,返回的函数指针指向有一个int参数且返回int的函数。
#include
#include
using namespace std;
int main()
{
int v[2][10] = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
};
int (*a)[10] = v; // 数组指针
cout << **a << endl;
cout << **(a+1) << endl;
cout << *(*a+1) << endl;
cout << *(a[0]+1) << endl;
cout << *(a[1]) << endl;
return 0;
}
解析:
本题定义一个指针指向一个10个int元素的数组。a+1表明a指针向后移动
1*sizeof(数组大小);a+1后共向后移动40个字节。*a+1仅针对这一行向后移动4个字节,如下
图所示。
答案:1 11 2 2 11
答案:
数组指针和指针数组都涉及到指向数组的指针,但它们的概念和用法略有不同。
1.数组指针
数组指针是指向数组的指针,它可以看作是一个一维数组,每个元素是指向数组中元素的指针。例如,int (*p)[3]就是一个指向包含3个整数的数组的指针。使用数组指针时,可以通过解引用指针来访问数组元素,也可以使用指针算术运算来访问数组元素。
示例代码:
int arr[2][3] = {{1,2,3},{4,5,6}};
int (*p)[3] = arr; // p指向arr数组
cout << **p << endl; // 输出1
cout << *(*p + 1) << endl; // 输出
2.指针数组
指针数组是一个数组,其中每个元素都是一个指针。例如,int *arr[3]就是一个包含3个指向整数的指针的数组。使用指针数组时,可以通过下标访问数组元素,也可以使用指针解引用来访问指针指向的值。
示例代码:
int a = 1, b = 2, c = 3;
int *arr[3] = {&a, &b, &c}; // arr数组包含3个指向整数的指针
cout << *arr[0] << endl; // 输出1
cout << *(arr[1] + 1) << endl; // 错误,arr[1]是一个指针,不能进行指针算术运算
**总结:**一句话(不够准确)来说就是,数组指针是指针指向了数组,指针数组是数组里面存指针
#include
using namespace std;
int main()
{
int a[] = {1, 2, 3, 4, 5};
int *ptr = (int*)(&a + 1);
printf("%d %d", *(a+1), *(ptr - 1));
return 0;
}
解析:
第一个结果好理解,是正常的指针运算。
第二个的确是5。首先a表示一个1行5列数组,在内存中表示为一个5个元素的序列。int* ptr=(int*)(&a+1)的意思是,指向a数组的第6个元素(尽管这个元素不存在)。那么显然,(ptr-1)所指向的数据就是a数组的第5个元素——5。
答案:2 5
答案:
通常,如果在删除一个指针后又把它删除了一次,程序就会变得非常不稳定,任何情况都有可能发生。但是如果你只是删除了一个空指针,则什么事都不会发生,这样做非常安全。
使用迷途指针或空指针(如MyPtr=0)是非法的,而且有可能造成程序崩溃。如果指针是空指针,尽管同样是崩溃,但它同迷途指针造成的崩溃相比是一种可预料的崩溃。这样调试起来会方便得多。
答案:
malloc与free是C++/C语言的标准库函数
,new/delete是C++的运算符
。它们都可用于申请动态内存和释放内存。然而,new和delete提供了更多的功能和便利性,优势如下:
答案:
(1)This指针本质是一个函数参数,只是编译器隐藏起形式的,语法层面上的参数。this只能在成员函数中使用,全局函数、静态函数都不能使用this。实际上,成员函数默认第一个参数为T* const this。如:
class A {
public:
int func(int p) { }
};
其中,func的原型在编译器看来应该是:
int func(A* const this, int p);
(2)this在成员函数的开始前构造,在成员的结束后清除。这个生命周期同任何一个函数的参数是一样的,没有任何区别。当调用一个类的成员函数时,编译器将类的指针作为函数的this参数传递进去。如:
A a;
a.func(10);
此处,编译器将会编译成:
A::func(&a, 10);
看起来和静态函数没差别,不过,区别还是有的。编译器通常会对this指针做一些优化,因此,this指针的传递效率比较高,如VC通常是通过ecx寄存器传递this参数的。
(3)this指针并不占用对象的空间。
this相当于非静态成员函数的一个隐函的参数,不占对象的空间。它跟对象之间没有包含关系,只是当前调用函数的对象被它指向而已。
所有成员函数的参数,不管是不是隐含的,都不会占用对象的空间,只会占用参数传递时的栈空间,或者直接占用一个寄存器。
(4)this指针是什么时候创建的?
this在成员函数的开始执行前构造,在成员的执行结束后清除。
但是如果class或者struct里面没有方法的话,它们是没有构造函数的,只能当做C的struct使用。采用TYPE xx的方式定义的话,在栈里分配内存,这时候this指针的值就是这块内存的地址。采用new方式创建对象的话,在堆里分配内存,new操作符通过eax返回分配的地址,然后设置给指针变量。之后去调用构造函数(如果有构造函数的话),这时将这个内存块的地址传给ecx。
(5)this指针存放在何处?堆、栈、还是其他?
this指针会因编译器不同而有不同的放置位置。可能是堆、栈,也可能是寄存器。
(6)this指针是如何传递给类中的函数的?绑定?还是在函数参数的首参数就是this指针?那么,this指针又是如何找到“类实例后函数”的?
大多数编译器通过ecx寄存器传递this指针。事实上,这也是一个潜规则。一般来说,不
同编译器都会遵从一致的传参规则,否则不同编译器产生的obj就无法匹配了。