牛客C++个人笔记2021-07-17(个人笔记强迫学习,看不惯出门右转)
学习内容:
1//下面描述正确的是
1 int *p1 = new int[10];
2 int *p2 = new int[10] ();
答案:p1申请的空间里的值是随机值,p2申请的空间里的值已经初始化
解析: 在C++primer(第5版)中关于new的讨论有:
1、new当个对象
new在自由空间分配内存,但其无法为其分配的对象命名,因次是无名的,分配之后返回一个指向该对象的指针。
int *pi = new int; // pi指向一个动态分配的,未初始化的无名对象
此new表达式在自由空间构造一个int类型对象,并返回指向该对象的指针。
默认情况下,动态分配的对象是默认初始化的,这意味着内置类型或组合类型的对象的值是无定义的,而类类型对象将用默认构造函数进行初始化。
2、new(多个对象)数组
new分配的对象,不管单个对象还是多个对象的分配,都是默认初始化。但可以对数组进行值初始化,方法就是:在大小之后添加一对空括号。
int *pia = new int[10]; // 10个未初始化int
int *pia2 = new int[10] (); // 10个值初始化为0的int
2 \下面的程序可以从0…n-1中随机等概率的输出m个不重复的数。这里我们假设n远大于m :
knuth(int n, int m)
{
srand((unsigned int)time(0));
for (int i = 0; i < n; i++) {
if ( ????) {
cout << i << endl;
( ????);
}
}
}
答案:rand()%(n-i)<m m–
解析:
由这个for循环循环n次,且在满足条件时才输出i,可知,输出m个不同值的要求已满足,因为每次输出的都是i值,而i值每次都是不一样的,m–保证了程序在输出了m个值后就停止循环。
在i=0时,rand()%(n-i)的取值范围为0到n-1,共n个数,此时要输出0只需要rand()%(n-i)小于m,故i=0被输出的概率为m/n;
在i=1时,rand()%(n-i)的取值范围为0到n-2,共n-1个数,若i=0没有被输出,则m–未被执行,此时i=1被输出的概率为m/(n-1),若i=0已经被输出了,则m变为m-1,此时i=1被输出的概率为(m-1)/(n-1);由概率论的知识,可知此时i=1被输出的概率为
P=(1-m/n)(m/(n-1))+m/n((m-1)/(n-1))=m/n;以此类推,可知每个数被输出的概率都为m/n。
3 //以下prim函数的功能是分解质因数。括号内的内容应该为?(重点是掌握质数的性质)
void prim(int m, int n)
{
if (m >= n)
{
while ( ????) n++;
(????);
prim(m, n);
cout << n << endl;
}
}
答案:m%n m/=n
解析: n从2开始
第一处为m%n,代表取余。当余数是0的时候表示除尽,跳出while循环,即找出一个质因数。此时一个质因数即为n
然后 m/=n 即让m除去这个质因数,然后再进入求新m质因数的递归。
举例:m=6,n=2
m>n;
m%n=0,跳出while,n没有加1。此时m=6,n=2
m/=n,此时m=3,n=2 (2为一个质因数)
递归prim(m, n),即prim(3, 2);
m>n;
m%n=1,n++,此时m=3,n=3,继续while循环
m%n=0,跳出while循环,此时 m=3,n=3 (3为另一个质因数)
m/=n,此时m=1,n=3
递归 prim(m, n),即 prim(1, 3);
不满足条件(m > n),返回上层
输出质因数 n=3
输出质因数 n = 2
4 // 函数外部访问x等于什么?
enum string{
x1,
x2,
x3=10,
x4,
x5,
} x;`
答案:0
解析:全局变量时初始化为0,局部变量时初始化为随机值。
5 // 请问p1+5= 什么?p2+5= 什么?
unsigned char *p1;
unsigned long *p2;
p1=(unsigned char *)0x801000;
p2=(unsigned long *)0x810000;
答案:801005 810014
解析:p1指向字符型,一次移动一个字符型,1个字节;p1+5后移5个字节,16进制表示为5;
p2指向长整型,一次移动一个长整型,4个字节,p2+5后移20字节,16进制表示为14。
{ char每次移动1个字节;short移动2个字节 ;int , long ,float移动4个字节 ;double移动8个字节}
6 // 在32位机器中,如下代码:
void example(char acWelcome[]){
printf("%d",sizeof(acWelcome));
return;
}
void main(){
char acWelcome[]="Welcome to Huawei Test";
example(acWelcome);
return;
}
的输出是?
答案:4
解析: char str[20]=“0123456789”;
int a=strlen(str); //a=10; >>>> strlen 计算字符串的长度,以结束符 0x00 为字符串结束。
int b=sizeof(str); //而b=20; >>>> sizeof 计算的则是分配的数组 str[20] 所占的内存空间的大小,不受里面存储的内容改变。
上面是对静态数组处理的结果,如果是对指针,结果就不一样了
char* ss = “0123456789”;
sizeof(ss) 结果 4 ===》ss是指向字符串常量的字符指针,sizeof 获得的是一个指针的之所占的空间,应该是长整型的,所以是4。
sizeof(*ss) 结果 1 ===》*ss是第一个字符 其实就是获得了字符串的第一位’0’ 所占的内存空间,是char类型的,占了 1 位。
strlen(ss)= 10 >>>> 如果要获得这个字符串的长度,则一定要使用 strlen 。
附加:
char 1个字节
16位编译器
32位编译器
64位编译器
7 // 处理a.html文件时,以下哪行伪代码可能导致内存越界或者抛出异常()
int totalBlank = 0;
int blankNum = 0;
int taglen = page.taglst.size();
A for(int i = 1; i < taglen-1; ++i)
{
//check blank
B while(page.taglst[i] == "<br>" && i < taglen)
{
C ++totalBlank;
D ++i;
}
E if(totalBlank > 10)
F blankNum += totalBlank;
G totalBlank = 0;
}
注意:以上代码中taglen是html文件中存在元素的个数,a.html中taglen的值是15,page.taglst[i]取的是a.html中的元素,例如page.taglst[1]的值是
a.html的文件如下:
答案:B
解析:跟while条件判断顺序有关。若先执行比较语句page.taglst[i] == “
”,则此时可能已经超出了数组的范围,即内存越界了。所以应该先进行i的大小判断,若此时条件不成立,也就不会再执行后面的其他并列条件了(&&)。
8 //
void Func(char str_arg[100])
{
printf("%d\n",sizeof(str_arg));
}
int main(void)
{
char str[]="Hello";
printf("%d\n",sizeof(str));
printf("%d\n",strlen(str));
char*p=str;
printf("%d\n",sizeof(p));
Func(str);
}
32位系统下下面程序的输出结果为多少?
答案:6544
解析:对字符串进行sizeof操作的时候,会把字符串的结束符"\0"计算进去的,进行strlen操作求字符串的长度的时候,不计算\0的。
数组作为函数参数传递的时候,已经退化为指针了,Func函数的参数str_arg只是表示一个指针,char 指针32位为4字节。
str是复合类型数组char[6],维度6是其类型的一部分,sizeof取其 维度*sizeof(char),故为6;
strlen 求c类型string 的长度,不含尾部的'\0',故为5;
p只是个指针,32位机上为4;
c++中不允许隐式的数组拷贝,所以Func的参数会被隐式地转为char*,故为4;
9 // 下面程序运行后的结果为:
char str[] = "glad to test something";
char *p = str;
p++;
int *p1 = reinterpret_cast<int *>(p);
p1++;
p = reinterpret_cast<char *>(p1);
printf("result is %s\n", p);
答案:result is to test something
解析: 该题的关键是要认清楚强制类型转换后指针的类型。
p的类型为char *,p++后p指向str数组的第2个元素即字母“l”的位置。
p1的类型为int *,p1++后p1指向的位置增加4个字节,指向str数组中的第6个元素即字母“t”的位置。
因此最后p的内容为“to test something”。
10 // 设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
C c;
void main()
{
A*pa=new A();
B b;
static D d;
delete pa;
}
答案:ABDC
解析: 首先一个个的分析:
对于类A, 是建立在堆上的对象指针pa, 通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的,需要程序员手动调用 delete 来释放。
对于类B, 是建立在栈上的对象b(main函数结束就释放)在该方法执行结束就会由系统自动通过调用析构方法将其空间释放。
对类C ,在静态存储区创建了一个对象c ,程序结束时候释放
对类D,也是在静态存储区建立对象d,但是局部变量,程序结束时候释放.
析构函数调用顺序:
先调用A的析构,因为delete pa . A
再释放栈上的对象b, B
关键看CD的顺序.之所以是先释放 D 在释放 C的原因是, 程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反
c是全局对象,对它的初始化是在main函数之前,所以析构时候要放在最后.
也就是先析构d局部对象,然后再析构c 。
11 // 若char是一字节,int是4字节,指针类型是4字节,代码如下:
class CTest
{
public:
CTest():m_chData(‘\0’),m_nData(0)
{
}
virtual void mem_fun(){}
private:
char m_chData;
int m_nData;
static char s_chData;
};
char CTest::s_chData=’\0’;
问:
(1)若按4字节对齐sizeof(CTest)的值是多少?
(2)若按1字节对齐sizeof(CTest)的值是多少?
请选择正确的答案。
答案:12 9
解析:构造函数、成员函数(非虚函数时)和静态函数不占空间,成员变量、 虚函数 占空间。
在类中,如果什么都没有,则类占用1个字节,一旦类中有其他的占用空间成员,则这1个字节就不在计算之内,如一个类只有一个int则占用4字节而不是5字节。
如果只有成员函数,则还是只占用1个字节,因为类函数不占用空间
虚函数因为存在一个虚函数表,需要4个字节,数据成员对象如果为指针则为4字节,注意有字节对齐,如果为13字节,则进位到16字节空间。
sizeof的本质是得到某个类型的大小,确切的来说就是当创建这个类型的一个对象(或变量)的时候,需要为它分配的空间的大小。而类也可以理解为类似于int、float这样的一种类型,当类中出现static成员变量的时候,static成员变量是存储在静态区当中的,它是一个共享的量,因此,在为这个类创建一个实例对象的时候,是无需再为static成员变量分配空间的,所以,这个类的实例对象所需要分配的空间是要排除static成员变量的,于是,当sizeof计算类的大小的时候会忽略static成员变量的大小 。
4(虚表指针)+1(char )+3(对齐补位)+4(int) 。
12 // 写出下面程序的输出结果:
class A
{
public:
void FuncA()
{
printf( "FuncA called\n" );
}
virtual void FuncB()
{
printf( "FuncB called\n" );
}
};
class B : public A
{
public:
void FuncA()
{
A::FuncA();
printf( "FuncAB called\n" );
}
virtual void FuncB()
{
printf( "FuncBB called\n" );
}
};
void main( void )
{
B b;
A *pa;
pa = &b;
A *pa2 = new A;
pa->FuncA(); ( 3)
pa->FuncB(); ( 4)
pa2->FuncA(); ( 5)
pa2->FuncB();
delete pa2;
}
答案:
FuncA called
FuncBB called
FuncA called
FuncB called
解析: 本题考查知识点:父类指针指向子类实例对象,调用普通重写方法时,会调用父类中的方法。而调用被子类重写虚函数时,会调用子类中的方法。
再次说明了,子类中被重写的虚函数的运行方式是动态绑定的,与当前指向类实例的父类指针类型无关,仅和类实例对象本身有关。
A *pa;
pa = &b;
A *pa2 = newA;
pa->FuncA(); ( 3)//pa=&b动态绑定但是FuncA不是虚函数,所以FuncA called
pa->FuncB(); ( 4)//FuncB是虚函数所以调用B中FuncB,FuncBB called
pa2->FuncA(); ( 5)//pa2是A类指针,不涉及虚函数,调用的都是A中函数,所以FuncA called FuncB called
pa2->FuncB()
13 // In the main() function, after ModifyString(text) is called, what’s the value of ‘text’?
int FindSubString( char* pch )
{
int count = 0;
char * p1 = pch;
while ( *p1 != '\0' )
{
if ( *p1 == p1[1] - 1 )
{
p1++;
count++;
}else {
break;
}
}
int count2 = count;
while ( *p1 != '\0' )
{
if ( *p1 == p1[1] + 1 )
{
p1++;
count2--;
}else {
break;
}
}
if ( count2 == 0 )
return(count);
return(0);
}
void ModifyString( char* pText )
{
char * p1 = pText;
char * p2 = p1;
while ( *p1 != '\0' )
{
int count = FindSubString( p1 );
if ( count > 0 )
{
*p2++ = *p1;
sprintf( p2, "%i", count );
while ( *p2 != '\0' )
{
p2++;
}
p1 += count + count + 1;
}else {
*p2++ = *p1++;
}
}
}
void main( void )
{
char text[32] = "XYBCDCBABABA";
ModifyString( text );
printf( text );
}
答案:XYBCDCBA1BAA
解析:(主要自己可以动笔画一画,代码流程很容易弄懂);
indSubString() 函数就是要找到一个先递增再递减且递增和递减的数量相等的回文序列,例如: ABCDCBA ,先是 后一项 = 前一项 ASCII 码 +1 , 后是 后一项 = 前一项 ASCII 码 -1 ,才能返回回文子串的长度,否则返回 0 。
ModifyString() 函数不断寻找上述类型的子串,如果不满足条件,就
*p2++ = *p1++;
当遇到 ABABA 中前一个 ABA 的时候,满足回文子串要求,此时 p1 指向 A BABA , p2 指向 ABABA ; sprintf 重定向修改 ABABA , B 变为 1 ,且跟随一个 ‘\0’ (该函数自动产生的) , 此时,字符串变为 A1‘\0’BA 。
经过 while ( *p2 != ‘\0’ ) 循环之后, p2 指向 A1‘\0’BA , p1 += count + count + 1 之后, p1 指向 A1‘\0’BA 。此时字符串已经被改动,之前的 ABABA 已经不存在,变为 A1‘\0’BA 。
再次进入 while ( *p1 != ‘\0’ ) 循环之后,只能执行 else 部分的命令, p1 指向 p2 指向的元素的后一个,不断将 p1 指向的元素传给 p2 所指向的位置,将原数据覆盖。所以, A1‘\0’BA ,依次变为 A1BBA 、 A1BAA 。即最终结果为 XYBCDCBA1BAA 。
14 // 如下代码输出结果是什么?
#include<stdio.h>
char *myString()
{
char buffer[6] = {0};
char *s = "Hello World!";
for (int i = 0; i < sizeof(buffer) - 1; i++)
{
buffer[i] = *(s + i);
}
return buffer;
}
int main(int argc, char **argv)
{
printf("%s\n", myString());
return 0;
}
答案:结果未知
解析: 函数char *myString()中没有使用new或者malloc分配内存,所有buffer数组的内存区域在栈区
随着char *myString()的结束,栈区内存释放,字符数组也就不存在了,所以会产生野指针,输出结果未知