C语言/C++/数据结构牛客网刷题20200302

联合的大小,结构体大小

在32位系统下下面程序输出的是?

#include<stdio.h>
union uni
 {
     int a;
     char b;
 };
 struct str
 {
     int a;
     char b;
 };
 int main(int argc, char **argv)
 {
     printf("%d %d\n", sizeof(union uni), sizeof(struct str));
     return 0;
 }

答案:4 8

解析:

联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,
就要对齐到最大对齐数的整数倍

求结构体大小标准步骤:https://blog.csdn.net/aiqq136/article/details/111658336

计算顺序

以下程序的输出结果是()

void main()
{
	int a = 3;
	printf("%d\n", (a += a -= a * a));
}

答案:-12

解析:拆分+=  -=和括号,之后计算

a+=a-=a*a等价于a=a+(a=a-a*a),即先计算a=a-a*a,所以此时

a的值为3-3*3=-6,再计算-6+(-6)=-12赋值给a,所以a的值为-12,也就是整个表达式的值,所以应选择D。

 

定义常数

定义符号常量pi.const float pi= 3.1415926f; 这句话是否正确

答案:正确

解析:

const  表示常数,float 表示浮点型。

const float pi=3.1415926f  表示定义一个浮点型的常数,初始化为 3.1415926 凡是常数值,只能在定义的时候赋值,进行初始化,定义过以后就不能更改常数的值了。

32位编译器下输出结果为6位有效数字。不过想想不论几位,如果硬要解释这道题是对的其实也能解释的通。毕竟人家问的是这样写对不对。对!一定可以通过编译,只是丢失精度罢了

 

数组跳过

执行如下代码后输出结果为(      )

int main()
{
int a[5]  = {1, 2, 3, 4, 5};
int *ptr = (int*)(&a + 1);
printf("%d, %d", *(a + 1), *(ptr - 1));
return;
}

答案:2,5

解析:

&a + 1,其中 + 1 偏移的大小为 a 整个数组的大小,即 5 个 int 型的大小;

a + 1,其中 + 1 偏移的大小为 a 中一个元素的大小,即 1 个 int 型的大小;

 

下列关于const和#define定义常量的区别,说法不正确的有?

  • define宏是在预处理阶段展开。const常量是编译运行阶段使用

  • 宏没有类型,不做任何类型检查,仅仅是展开。const常量有具体的类型,在编译阶段会执行类型检查

  • define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。const常量会在内存中分配(可以是堆中也可以是栈中)

  • const定义和#define定义的常量在程序运行过程中只有一份拷贝

正确答案: D

解析:

A,正确,#define定义的宏是在预处理阶段进行替换的,const常量是在编译、运行阶段进行使用的。

注意是仅仅的字符串替换,并不会检查其合法性。

预处理阶段做了的任务:
1:将头文件中的内容(源文件之外的文件)插入到源文件中
2:进行了宏替换的过程(简单的字符串替换),定义和替换了由#define指令定义的符号
3:删除掉注释的过程,注释是不会带入到编译阶段
4:条件编译

B,正确,所有的宏替换只是简单的字符串替换,注意是字符串替换,所以并不会检查其合法性,而const定义的常量依然是内置类型等,所以会对其进行类型安全检查

C,正确,宏定义在程序中使用了几次在预处理阶段就会被展开几次,并不会增加内存占用,但是宏定义每展开一次,代码的长度就要发生变化(所以有利必有弊啊!),而const常量也会为其分配内存(如果是动态申请空间肯定就是堆中了)。

D,错误,const定义的常量只有一次拷贝没毛病,而define定义的变量在内存中并没有拷贝,因为所有的预处理指令都在预处理时进行了替换。

 

类型提升

申明以下变量:

char ch; int i, float f, double d;

则表达式: ch/i + (f*d – i) 的结果类型为_________

正确答案: double

解析:

基本数据类型的等级从低到高如下:char int long float double运算的时候是从低转到高的,表达式的类型会自动提升为参与表达式求值的最上级类

typeid():

如果有定义语句 int *ptr[4],typeid(ptr).name() 结果为 "A4_Pi",

请问对 int *(ptr[4]) , typeid(ptr).name() 输出是

  • PPi

  • A4_Pi

  • PA4_i

  • A4_i

正确答案: B

int *ptr[4];指数组ptr中有4个元素,每个元素都是int *类型的;

*和[]对ptr的结合性:先和[]结合,而*和int结合用来修饰ptr,所以加个括号也和之前的一样。

 

关于typeid():

在c++中,typeid用于返回指针或引用所指对象的实际类型,它是一个操作符。

可以在运行时获知变量类型名称,可以使用 typeid(变量).name()获取。

fork()

输出几个hello

#include<stdio.h>
int main()
{
    fork();
    fork();
    fork();
    printf("hello ");
    return 0;
}

正确答案: 8

解析:c语言的fork()创立一个新进程

输出8个,和小鸡下蛋差不多。
主进程=1个
主进程中分出3个子进程:
第三个:没有fork =1个
第二个:有一个fork=没有fork的子进程+本身 =2个
第一个:有两个fork的子进程=有一个fork的子进程+没有fork的子进程+本身=4个
总和:8个

#define替换##

定义宏#define DECLARE(name, type) type name##_##type##_type,
则DECLARE(val, int)替换结果为()

  • int val_int_type

  • int val_int_int

  • int name_int_int

  • int name_int_name

正确答案: A   你的答案: B (错误)

解析:

##是一种分隔连接方式,它的作用是先分隔,然后进行强制连接

“name”和第一个“_”之间被分隔了,所以预处理器会把name##_##type##_type解释成4段:“name”、“_”、“type”以及“_type”,name和type会被替换,而_type不会被替换

 

静态成员

在c/c++语言中,关于类的静态成员的不正确描述是()

  • 静态成员不属于对象,是类的共享成员

  • c++11之前,非const的静态数据成员要在类外定义和初始化

  • 静态成员函数不拥有this指针,需要通过类参数访问对象成员

  • 只有静态成员函数可以操作静态数据成员

正确答案: D   你的答案: B C D (错误)

解析:

A----------静态成员不属于对象,是类的共享成员。

B----------静态成员要在类外定义和初始化。

B显然是有问题的。静态成员变量存储在方法区,也是在类内定义和初始化的。

C----------静态成员函数不含有this指针,需要通过类参数访问对象成员。

因为静态成员函数被调用的时候,该静态成员函数所属的类很有可能还没有被初始化,所以静态成员函数是不含有this指针的。

D----------只有静态成员函数可以操作静态数据成员。

D显然也是有问题的,非静态成员函数也可以操作静态数据成员。

length,length(),size()表达式

已知表达式int m[]={0,1,2,3,4,5,6};下面哪个表达式的值与数组下标量最大值相等?

  • m. length()
  • m. length-1

  • m. length()+1

  • m. length+1

正确答案: B   你的答案: B (正确)

解析:

length --- arrays (int[], double[], String[]) ---- to know the length of the arrays

length() --- String related Object (String, StringBuilder etc)to know the length of the String  

size() --- Collection Object (ArrayList, Set etc)to know the size of the Collection

 

 length属性:数组

 length():String

 size():集合

重载的运算符 

下面是C++运算符优先级顺序, 【】内的是不能重载的运算符 

【::】                                                                              作用域
->  【.】  ++  --(后自运算)  []                                                     增量、对象、数组
!  ~  ++  --(前自运算)  +  -(正负号)  *(指针)  &(取址)  new  delete  【sizeof】 增量、内存、取反
->*  【.*】                                                                         类成员指针
*  /  %                                                                             算术
+  -                                                                                算术
<<  >>                                                                              位
< <= >= >                                                                           关系
== !=                                                                               关系
&                                                                                   位
^                                                                                   位
|                                                                                   位
&&                                                                                  逻辑
||                                                                                  逻辑
【?:】                                                                              条件
= += -= *= /= %= &= ^= |=                                                           赋值
,                                                                                   逗号

其中,
    .和.*是为保护访问成员功能而不允许被重载;
    sizeof运算对象是类型而非变量,不具备重载特征。

 

内联函数在编译时是否做参数类型检查?

  • 不做检查,和宏一样

  • 做类型检查

  • 和编译器相关

正确答案: B   你的答案: A (错误)

解析:

先说宏和函数的区别:
1. 宏做的是简单的字符串替换(注意是字符串的替换,不是其他类型参数的替换),而函数的参数的传递,参数是有数据类型的,可以是各种各样的类型.
2. 宏的参数替换是不经计算而直接处理的,而函数调用是将实参的值传递给形参,既然说是值,自然是计算得来的.
3. 宏在编译之前进行,即先用宏体替换宏名,然后再编译的,而函数显然是编译之后,在执行时,才调用的.因此,宏占用的是编译的时间,而函数占用的是执行时的时间.
4. 宏的参数是不占内存空间的,因为只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的.

5. 函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显然在宏中是没有的.

内联函数与宏的区别:

1.内联函数在运行时可调试,而宏定义不可以;
2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会; 
3.内联函数可以访问类的成员变量,宏定义则不能; 
4.在类中声明同时定义的成员函数,自动转化为内联函数。

内联函数

引入内联函数的目的是为了解决程序中函数调用的效率问题,这么说吧,程序在编译器编译的时候,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体进行替换,而对于其他的函数,都是在运行时候才被替代。这其实就是个空间代价换时间的i节省。所以内联函数一般都是1-5行的小函数。在使用内联函数时要留神:

  • 1.在内联函数内不允许使用循环语句和开关语句;
  • 2.内联函数的定义必须出现在内联函数第一次调用之前;
  • 3.类结构中所在的类说明内部定义的函数是内联函数。

 

只有当函数只有 10 行甚至更少时才将其定义为内联函数.

定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用.

优点: 当函数体比较小的时候, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联.

缺点: 滥用内联将导致程序变慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。

结论: 一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用!

另一个实用的经验准则: 内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 switch 语句从不被执行).

有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数.

#include <iostream>
 
using namespace std;

inline int Max(int x, int y)
{
   return (x > y)? x : y;
}

// 程序的主函数
int main( )
{

   cout << "Max (20,10): " << Max(20,10) << endl;
   cout << "Max (0,200): " << Max(0,200) << endl;
   cout << "Max (100,1010): " << Max(100,1010) << endl;
   return 0;
}

/*当上面的代码被编译和执行时,它会产生下列结果:

Max (20,10): 20
Max (0,200): 200
Max (100,1010): 1010*/

二进制

若k为整形,下述while循环的次数为:()

k=1000;
while (k>1)
 {   print k;
      k=k/2;
 }
  • 1000
  • 10
  • 11
  • 9

正确答案: D   你的答案: B (错误)

解析:

当k>1时继续执行k/=2;只需要将1000的二进制11 1110 1000每次右移一位,直到00 0000 0011时,计算总共右移了9位

 

空类默认产生类成员函数

C++中空类默认产生哪些类成员函数()

  • 默认构造函数

  • 析构函数

  • 拷贝构造函数

  • 赋值函数

正确答案: A B C D   你的答案: A B C (错误)

解析

如果你只是声明一个空类,不做任何事情的话,编译器会自动为你生成一个默认构造函数、一个拷贝默认构造函数、一个默认拷贝赋值操作符和一个默认析构函数。这些函数只有在第一次被调用时,才会别编译器创建。所有这些函数都是inline和public的。

定义一个空的C++类,例如

class Empty
{
}

一个空的class在C++编译器处理过后就不再为空,编译器会自动地为我们声明一些member function,一般编译过去就相当于:

class Empty
{
public:
Empty(); // 缺省构造函数//
Empty( const Empty& ); // 拷贝构造函数//
~Empty(); // 析构函数//
Empty& operator=( const Empty& ); // 赋值运算符//
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};

一般的书上好像都是前面四种:默认构造函数,拷贝构造函数,默认赋值函数以及析构函数,后面两种其实属于,但要需要注意的是,只有当你需要用到这些函数的时候,编译器才会去定义它们。

 

 

运算符顺序

若有定义语句: int year=1009 , *p=&year ;以下不能使变量 year 中的值增至 1010 的语句是

  • *p+=1;

  • (*p)++;

  • ++(*p)

  • *p++

正确答案: D   你的答案: A (错误)

解析:

++的优先级比*高,所以D中p++先执行,p指向的地址改变了,所以year的内容没有变化。

 

宏替换括号问题

执行以下代码

#define SUM(x,y) x+y

int a=3;

int b=2;

a+=a*SUM(a,b)*b;

a的值为

  • 16

  • 30

  • 33

  • 39

正确答案: A   你的答案: C (错误)

解析:

a=a+a*SUM(a,b)*b

a=a+a*a+b*b

a=3+9+4

a=16

宏替换是在预编译时将整个宏的表达式进行替换再进行编译的,举个例子讲如果你定义了如下宏:
#define fun(a) (a*a)
下面用的时候
int main()
{
int i = 5, j = 4;
int k = fun(i+j)
}

那么在预编译时首选会先将fun(i+j)替换成 i+j*i+j,即此时计算的时候会先算乘法i*j,那么k的值最终就是5+4*5+4 = 29,而不是(i+j)*(i+j)=81,当然如果你想实现后一种结果,则宏定义方法应该是下面这样的

#define fun(a) ((a)*(a))

 

printf函数计算和输出顺序

请问:printf("%d %d", *(--ptr), *(++ptr))语句中,首先进行哪个运算?

  • --ptr

  • ++ptr

  • 同时计算--ptr,++ptr

  • 随机顺序计算--ptr,++ptr

正确答案: B   你的答案: A (错误)

解析:

对于printf函数printf("%d%d\n",a,b);函数的实际输出顺序是这样的

先计算出b,然后在计算a,接着输出a,最后在输出b;
例子如下:
#include<iostream>
using namespace std;
int main()
{
 int i=3,j=5;
 printf("%d  %d\n",(i++)-(--j),j=(i+=2));
 printf("%d  %d\n",i,j);
  return 0;
}
     此题的执行过程如下;
   首先对于第一个printf先计算出j=(i+=2),这样此时i=i+2=5,j=5;
   接着计算(i++)-(--j)=(5)-(4)=1,注意在计算完之后令i++,及i=6;

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值