C/C++笔试及面试中一些需要注意的细节问题

strlen() 与 sizeof() 的区别

  • 在进行字符(数组)串的长度求取问题时,strlen()和sizeof()都可以作为选择,当时两者之间的区别还是值得考究的。
    (1)strlen()是函数,sizeof()是运算符;
    (2)对于定义字符数组大小的字符数组,sizeof 的值在声明时已经确定了,而 strlen()则根据数组中实际的字符计算大小;如:
char test2[100] = "hello world";

cout << "sizeof test2: " << sizeof(test2) << endl;
cout << "strlen test2: " << strlen(test2) << endl;

在这里插入图片描述
(3)对于为给定大小的字符数组,sizeof()计算时会将数组末尾的 ’\0 ‘ 也算上,而strlen()则不进行计算;如:

char test1[] = "hello world";

cout << "sizeof test1: " << sizeof(test1) << endl;
cout << "strlen test1: " << strlen(test1) << endl;

在这里插入图片描述

计算二进制中0的个数和1的个数

  • 求二进制中1的个数:(当然方法很多,就是写下来选择题容易出现这个)
int sumOfNum1(int n)
{
    
    
    int count = 0;
    while(n != 0)
    {
    
    
        n &= (n-1);
        count++;
    }

    return count;
}
  • 求二进制中0的个数:
int fun(int value)
{
    
    
    int count = 0;
    while(!value)
    {
    
    
        count++;
        value |= (value+1);
    }
}

大小端问题

  • 大小端区别以及各自优点,什么时候用
    大端优点:符号位在所表示的数据的内容的第一个字节中,便于快速判断数据的正负和大小;
    小端优点低地址放低字节,所以在强制转换时不需要调整字节的内容。而CPU做数值运算时从内存中依次从低到高取数据进行运算,直到最后刷新最高位的符号位,这样的方式会更高效。

用一个例子来说明:
在这里插入图片描述

  • 上述题目:
    (1)如果按照大端模式存储:从低地址到高地址:20 15 08 10
    存放时高字节放低地址,输出从低地址到高地址:20 15 08 10,因此结果就是:2015 810
    (2)如果按照小端模式存储:从低地址到高地址:10 08 15 20
    存放时高字节放高地址,输出从高地址到低地址:08 10 20 15,因此结果就是:810 2015

二叉树的最近公共祖先

在leetcode上看的一道题,觉得不错,记录一下

  • 题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
    在这里插入图片描述
  • 题解:主要思想是递归
    当我们用递归去做这个题时不要被题目误导,应该要明确一点,这个函数的功能有三个:给定两个节点 p 和 q
    (1)如果 p 和 q 都存在,则返回它们的公共祖先;
    (2)如果只存在一个,则返回存在的一个;
    (3)如果 pp 和 qq 都不存在,则返回NULL.
  • 具体思路
    (1)如果当前节点 root 等于 NULL,则直接返回 NULL;
    (2) 如果 root 等于 p 或者 q ,那这棵树一定返回 p 或者 q;
    (3)然后递归左右子树,因为是递归,使用函数后可认为左右子树已经算出结果,用 left 和 right 表示;
    (4)此时若 left 为空,那最终结果只要看 right;若 right 为空,那最终结果只要看 left;
    (5)如果 left 和 right 都非空,因为只给了 p 和 q 两个结点,都非空,说明一边一个,因此 root 是他们的最近公共祖先;
    (6)如果 left 和 right 都为空,则返回空(其实已经包含在前面的情况中了).
  • 时间复杂度是 O(n):每个结点最多遍历一次或用主定理,空间复杂度是 O(n):需要系统栈空间
  • C++实现
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
class Solution {
    
    
public:
   struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
    
    
        if(root == NULL)
            return NULL;
        if(root == p || root == q) 
            return root;
            
        struct TreeNode* left =  lowestCommonAncestor(root->left, p, q);
        struct TreeNode* right = lowestCommonAncestor(root->right, p, q);
       
        if(left == NULL)
            return right;
        if(right == NULL)
            return left;      
        if(left && right) // p和q在两侧
            return root;
        
        return NULL; // 必须有返回值
    }
};

strcpy函数返回值为什么要用char *

  • 已知 strcpy 的函数原型:char *strcpy(char *strDest,char *strSrc) 其中 strDest 是目的字符串,strSrc 是源字符串;strcpy 能把 strSrc 的内容复制到 strDest ,为什么还要 char * 类型作为返回值?
char *strcpy(char *strDest, const char *strSrc)
{
    
    
if ( strDest == NULL || strSrc == NULL)
return NULL ;
if ( strDest == strSrc)
return strDest ;
char *tempptr = strDest ;
while( (*strDest++ = *strSrc++) != ‘\0)
;
return tempptr ;
}
  • 这是因为返回 char * 原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。
    链式表达式如:
int iLength=strlen(strcpy(strA,strB));

C++中重写(覆盖),重载,隐藏之间的区别

  • 重写(覆盖):是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。
  • 重载:是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。
  • 重写和重载的区别:
    (1)范围的区别被重写和重写的函数位于两个类中,而重载和被重载的函数位于同一个类中;
    (2)参数的区别:被重写函数和重写函数的参数列表一定相同,而被重载函数和重载函数的参数列表一定不同
    (3)virtual 的区别重写的基类中被重写的函数必须要有 virtual 修饰而重载函数和被重载函数可以被 virtual 修饰,也可以没有。
  • 隐藏和重写、重载有以下几点不同:
    (1)隐藏与重载的范围不同:和重写一样,隐藏函数和被隐藏函数不在同一个类中;
    (2)参数区别:隐藏函数和被隐藏函数的参数列表可以相同,也可以不同,但是函数名肯定要相同。当参数不相同时,无论基类中的函数是否被 virtual 修饰,基类的函数都是被隐藏的,而不是被重写。
    在这里插入图片描述
  • 牛客上总结的一个解答,感觉不错:
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。

b.覆盖是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;
(4)基类函数必须有virtual 关键字。

c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

猜你喜欢

转载自blog.csdn.net/qq_41782149/article/details/107370623