C语言再学习 -- 详解C++/C 面试题 1
参看:《高质量C++ C编程指南》.林锐
对这篇文章记忆犹新,因为之前找工作面试的时候,遇到过一家公司就是用的这套面试题。现在就结合考查的知识点和我总结完 C 语言再学习后的深入理解,来详细的讲讲我对这篇文章的总结。
一、请填写BOOL , float,指针变量 与“零值”比较的if语句。(10分)
提示:这里“零值”可以是0, 0.0 , FALSE或者“空指针”。例如int变量n与“零值”
比较的 if 语句为:
if ( n == 0 )
if ( n != 0 )
以此类推。
1、请写出BOOL flag与“零值”比较的if语句:
标准答案:
if ( flag )
if ( !flag )
如下写法均属不良风格,不得分。
if (flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)
2、请写出 float x与“零值”比较的if语句:
标准答案示例:
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
不可将浮点变量用“ ==”或“! =”与数字比较,应该设法转化成“ >=”或“ <=”此类形式。
如下是错误的写法,不得分。
if (x == 0.0)
if (x != 0.0)
3、请写出 char *p与“零值”比较的if语句:
标准答案:
if (p == NULL)
if (p != NULL)
如下写法均属不良风格,不得分。
if (p == 0)
if (p != 0)
if (p)
if (!)
解答:
1、根据布尔类型的语义,零值为“假”(记为 FALSE),任何非零值都是“真”(记为TRUE)。
/usr/include/curses.h文件/* X/Open and SVr4 specify that curses implements 'bool'. However, C++ may also* implement it. If so, we must use the C++ compiler's type to avoid conflict* with other interfaces.** A further complication is that <stdbool.h> may declare 'bool' to be a* different type, such as an enum which is not necessarily compatible with* C++. If we have <stdbool.h>, make 'bool' a macro, so users may #undef it.* Otherwise, let it remain a typedef to avoid conflicts with other #define's.* In either case, make a typedef for NCURSES_BOOL which can be used if needed* from either C or C++.*/#undef TRUE
#define TRUE 1#undef FALSE
#define FALSE 0
2、在浮点数比较中不能使用 < 和 >,千万要留意,无论是 float 还是 double 类型的变量,都有精度限制。所以一定要避免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。
请写出 float x 与“零值”比较的 if 语句
const float EPSINON = 0.000001;
if ((x >= - EPSINON) && (x <= EPSINON)
#include <stdio.h>
#include <curses.h>int main (void)
{char *p = NULL;if (p != (void*)0){printf ("11111111\n");}printf ("22222222\n");return 0;
}
输出结果:
22222222
#include <stdio.h>
#include <string.h>
int main (void)
{ char ptr1[] = "beijing"; char ptr2[] = "hello world"; if (strlen (ptr1) - strlen (ptr2) >= 0) { printf ("1111111111\n"); } printf ("2222222222\n"); return 0;
}
输出结果:
1111111111
2222222222
#include <stdio.h>
#include <string.h>
int main (void)
{ char ptr1[] = "beijing"; char ptr2[] = "hello world"; if (strlen (ptr1) >= strlen (ptr2)) { printf ("1111111111\n"); } printf ("2222222222\n"); return 0;
}
输出结果:
2222222222
二、以下为Windows NT下的32位C++程序,请计算sizeof的值(10分)
{
请计算
sizeof( str ) = 4
}
char str[] = “Hello” ;
char *p = str ;
int n = 10;
请计算
sizeof (str ) = 6
sizeof ( p ) = 4
sizeof ( n ) = 4
void *p = malloc( 100 );
请计算
sizeof ( p ) = 4
解答:
记住这两句话:
参数传递数组永远都是传递指向数组首元素的指针。
三、简答题(25分)
1、头文件中的ifndef/define/endif干什么用?
2、#include <filename.h>和#include “filename.h”有什么区别?
对于#include “filename.h” ,编译器从用户的工作路径开始搜索 filename.h
3、const有什么用途?(请至少说明两种)
( 2) const 可以修饰函数的参数、返回值,甚至函数的定义体。被 const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
4、在C++程序中调用被C编译器编译后的函数,为什么要加extern “C”声明?
5、请简述以下两个for循环的优缺点
// 第一个
for (i=0; i<N; i++)
{
if (condition)
DoSomething();
else
DoOtherthing();
}
缺点:多执行了 N-1 次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。
// 第二个
if (condition)
{
for (i=0; i<N; i++)
DoSomething();
}
else
{
for (i=0; i<N; i++)
DoOtherthing();
}
缺点:程序不简洁
解答:
#ifndef 标识符
程序段1
#else
程序段2
#endif
它的作用是,若标识符未被定义则编译程序段1,否则编译程序段2。
一般地,当某文件包含几个头文件,而且每个头文件都可能定义了相同的宏,使用#ifndef可以防止该宏重复定义。
//头文件卫士
#ifndef __THINGS_H__
#define __THINGS_H__
#endif
2、 参看:C语言再学习 -- C 预处理器
#include <filename.h> 文件名放在尖括号中
在UNIX系统中,尖括号告诉预处理器在一个或多个标准系统目录中寻找文件。
如: #include <stdio.h>
查看:
ls /usr/include
ls kernel/include
#include "filename.h" 文件名放在双引号中
在UNIX系统中,双引号告诉预处理器现在当前目录(或文件名中指定的其他目录)中寻找文件,然后在标准位置寻找文件。
如: #include "hot.h" #include "/usr/buffer/p.h"
3、参看:C语言再学习 -- 关键字const
const 修饰类型
1、const 修饰一般常量
2、const修饰指针、数组
3、const 修饰函数的形参和返回值
4、const 修饰常对象
5、const 修饰常引用
6、const 修饰类的成员变量
7、const 修饰类的成员函数
const 作用
1)可以定义 const 常量,具有不可变性。
2)便于进行类型检查,使编译器对处理内容有更多了解,消除一些隐患。
3)可以避免意义模糊的数字出现,同样可以很方便进行参数的调整和修改。同宏定义一样,可以做到不变则已,一变都变。
4)可以保护被修改的东西,防止意外的修改,增强程序的健壮性。
5)可以节省空间,避免不必要的内存分配。
6)为函数重载提供了一个参考
7)提高效率
4、参看:C语言再学习 -- 存储类型关键字
C 程序中,不允许出现类型不同的同名变量。而C++程序中 却允许出现重载。重载的定义:同一个作用域,函数名相同,参数表不同的函数构成重载关系。因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,来解决名字匹配问题。简单来说就是,extern “C”这个声明的真实目的是为了实现C++与C及其它语言的混合编程。
5、参看:C语言再学习 -- 循环语句
第一个程序比第二个程序多执行了 N-1 次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果 N 非常大,最好采用第二个程序的写法,可以提高效率。如果 N 非常小,两者效率差别并不明显,采用第一个程序的写法比较好,因为程序更加简洁。
四、有关内存的思考题(20分)
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
请问运行 Test 函数会有什么样的结果?
答:程序崩溃。
因为 GetMemory 并不能传递动态内存,Test 函数中的 str 一直都是 NULL。、
char *GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行 Test 函数会有什么样的结果?
答:可能是乱码。
因为 GetMemory 返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。
Void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行 Test 函数会有什么样的结果?
答:能够输出 hello
内存泄漏
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}
请问运行 Test 函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预料,非常危险。
因为 free(str);之后, str 成为野指针,if(str != NULL)语句不起作用。
解答:
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量, static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意多少的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
参看:C语言再学习 -- 再论内存管理
1、出现 段错误(核心已转储)
参看:C语言再学习 -- 再论数组和指针
#include <stdio.h>
#include <stdlib.h>
#include <string.h>void GetMemory( char *p )
{p = (char *) malloc( 100 );
}
void Test( void )
{char *str = NULL;GetMemory( str );strcpy( str, "hello world" );puts (str);
}int main (void)
{Test ();return 0;
}
输出结果:
段错误 (核心已转储)
解决方法:
第一种方法:使用二级指针作为函数的形式参数,可以让被调用函数使用其他函数的指针类型存储区
第二种方法:使用返回值
char *strcpy(char *strDest, const char *strSrc); { assert((strDest!=NULL) && (strSrc !=NULL)); // 2分 char *address = strDest; // 2分 while( (*strDest++ = * strSrc++) != ‘\0’ ) // 2分 NULL ; return address ; // 2分 } ( 2) strcpy 能把 strSrc 的内容复制到 strDest,为什么还要 char * 类型的返回值?
答:为了实现链式表达式。 // 2 分 例如 int length = strlen( strcpy( strDest, “hello world”) );
解答:
参看:C语言再学习 -- 字符串和字符串函数
1、功能实现函数:
#include <stdio.h>
#include <string.h>
#define WORDS "best"
#define SIZE 40 int main (void)
{ char *orig = WORDS; char copy[SIZE] = "Be the best that you can be."; char *ps; puts (orig); puts (copy); ps = strcpy (copy + 7 , orig); puts (copy); puts (ps); return 0;
}
输出结果:
best
Be the best that you can be.
Be the best
best
六、编写类 String 的构造函数、析构函数和赋值函数( 25 分)
已知类 String 的原型为:
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String & operate =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};
请编写 String 的上述 4 个函数。
char *strcpy(char *dest, const char *src)
{ char *tmp = dest; while ((*dest++ = *src++) != '\0') /* nothing */; return tmp;
}
2、char*类型,它返回的是第一个参数的值,即一个字符的地址
五、编写 strcpy 函数( 10 分)
已知 strcpy 函数的原型是 char *strcpy(char *strDest, const char *strSrc); 其中 strDest 是目的字符串, strSrc 是源字符串。 ( 1)不调用 C++/C 的字符串库函数,请编写函数 strcpy
#include <stdio.h>
#include <stdlib.h>
#include <string.h> void Test(void)
{char *str = (char *) malloc(100);strcpy(str, "hello");puts (str);free(str);if(str != NULL){strcpy (str, "world");puts (str);}
}int main (void)
{Test ();return 0;
}
输出结果:
hello
world
#include <stdio.h>
#include <stdlib.h>
#include <string.h> void GetMemory (char **p, int num)
{*p = (char *)malloc(num*sizeof (char));
}
void Test (void)
{char *str = NULL;GetMemory (&str, 100000);strcpy (str, "hello");puts (str);free (str); //释放str = NULL;
}int main (void)
{while (1){Test ();}return 0;
}
输出结果:
hello
hello
。。。
4、结果难料
//循环多次执行后
#include <stdio.h>
#include <stdlib.h>
#include <string.h> void GetMemory (char **p, int num)
{*p = (char *)malloc(num*sizeof (char));
}
void Test (void)
{char *str = NULL;GetMemory (&str, 100000);strcpy (str, "hello");puts (str);
}int main (void)
{while (1){Test ();}return 0;
}
输出结果:
hello
hello
。。。
hello
hello
段错误 (核心已转储)
解决方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> char *GetMemory (char* p_str)
{char *p = p_str;p = "hello world";return p;
}
void Test( void )
{char *str = NULL;str = GetMemory(str);puts (str);
}int main (void)
{Test ();return 0;
}
输出结果:
hello world
3、段错误(核心已转储)
#include <stdio.h>
#include <stdlib.h>
#include <string.h> char *GetMemory( void )
{char p[] = "hello world";return p;
}
void Test( void )
{char *str = NULL;str = GetMemory();puts (str);
}int main (void)
{Test ();return 0;
}
输出结果:
警告: 函数返回局部变量的地址 [默认启用]
解决方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* fa(char* p) //主要还是指针的问题
{ p=(char* )malloc(100); return p;
}
int main()
{ char* str=NULL;//这块没问题的 str = fa(str); strcpy(str,"hello"); printf("%s\n",str); free(str); str=NULL; return 0;
}
输出结果:
hello
2、警告: 函数返回局部变量的地址 [默认启用]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fa(char** p) //主要还是指针的问题
{ *p=(char* )malloc(100); if(*p) { return; }
}
int main()
{ char* str=NULL;//这块没问题的 fa(&str); strcpy(str,"hello"); printf("%s\n",str); free(str); str=NULL; return 0;
}
输出结果:
hello
C语言再学习 -- 详解C++/C 面试题 1相关推荐
- C语言再学习 -- 详解C++/C 面试题 2
(经典)C语言测试:想成为嵌入式程序员应知道的0x10个基本问题. 参看:嵌入式程序员面试问题集锦 1.用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define ...
- 一文弄懂元学习 (Meta Learing)(附代码实战)《繁凡的深度学习笔记》第 15 章 元学习详解 (上)万字中文综述
<繁凡的深度学习笔记>第 15 章 元学习详解 (上)万字中文综述(DL笔记整理系列) 3043331995@qq.com https://fanfansann.blog.csdn.net ...
- C语言再学习--关键字
如需转载请注明出处:https://blog.csdn.net/qq_29350001/article/details/53021879 C语言一共有32个关键字,如下表所示: 关键字 说明 auto ...
- C语言再学习 -- 再论数组和指针
之前有总结指针数组,但是现在看来总结的太简单了.好多重要的知识点都是一带而过的.本想在后面添加后来想想算了,还是再写一篇文章来详细介绍数组和指针这对冤家吧. 之前总结的,参看:C语言再学习 -- 数组 ...
- C语言再学习 -- 关键字volatile
上周确实事情挺多的,年会.公司聚餐,一到过年就有忙不完的事分心.还好C语言再学习总结的已经差不多了,年前也不展开别的了,接下来这十几天.总结几篇典型的面试题吧. 言归正传,接下来看看关键字 volat ...
- C语言再学习 -- 关键字const
const 关键字其实我们并不陌生,之前有讲过const修饰数组和指针.现在来详细介绍这个关键字. 参看:[C/C++和指针]著名的<const的思考> 一.const 介绍 1.cons ...
- C语言再学习 -- Linux下find命令用法
参看:linux下find(文件查找)命令的用法总结 linux下查找文件的命令有两个:locate 和 find 首先说一下locate: 这个命名是对其生成的数据库进行遍历(生成数据库的命令:uo ...
- C语言再学习 -- C 预处理器
gcc/cc xxx.c 可以编译链接C源程序生成一个可执行文件 a.out 整个过程中可以划分为以下的4步流程: (1)预处理/预编译: 主要用于包含头文件的扩展,以及执行宏替换等 //加上 -E ...
- 【C语言】函数详解(入门到进阶)
目录 前言 一.什么是函数 二.函数的构成 三.函数的调用和声明 四.函数的参数 五.函数的递归 总结 写在后面 前言 最近帮家里的小朋友整理一些学习C语言的知识点 有整体入门基础文章--[C语言]拯 ...
最新文章
- ArcGIS的许可文件问题
- 【C 语言】二级指针作为输出 ( 指针输入 | 指针输出 | 二级指针 作为 函数形参 使用示例 )
- Windows 7 部署 Android 开发环境傻瓜式教程(Eclipse+ADT)
- 常考数据结构与算法:单链表的排序
- 递归 || 递归的相关实例练习
- 最大公约数gcd和Win32版本实现
- git-stash用法小结
- 什么是机器人的五点校正法_Epson机器人原点校准命令及方法(详细解释指令)
- python k线合成_手把手教你写一个Python版的K线合成函数
- View-屏幕坐标 Content-网页(内容)坐标 mScrollX和mScrollY-屏幕坐标偏移
- NYOJ 305 表达式求值 (字符串处理)
- python3 单例模式_当python,单例模式,多例模式,一次初始化遇到一起
- 公交车宜配备逃生绳索
- python中函数包括_python中有哪些函数
- 斯皮尔曼相关(spearman)系数法
- python学习(四):犹如鸿雁一般的Flask,小小框架有着无限可能
- 串行异步通信_什么是并行传输、串行传输、异步传输?
- 神州战神电脑关闭触摸板
- java pdf添加透明水印,PDF怎么加透明水印?
- OCJP(1Z0-851) 模拟题分析(三)