C/C++ written test and some details that need attention in the interview

The difference between strlen() and sizeof()

  • When calculating the length of a character (array) string, both strlen() and sizeof() can be used as choices. The difference between the two is still worth studying.
    (1) strlen() is a function, sizeof() is an operator;
    (2) For a character array that defines the size of a character array, the value of sizeof has been determined at the time of declaration, and strlen() is calculated based on the actual characters in the array Size; such as:
char test2[100] = "hello world";

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

Insert picture description here
(3) For a character array of a given size, sizeof() will also count the'\0' at the end of the array when calculating, but strlen() will not calculate; for example:

char test1[] = "hello world";

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

Insert picture description here

Count the number of 0s and 1s in binary

  • Find the number of 1s in the binary: (Of course there are many ways, it is easy to write multiple-choice questions)
int sumOfNum1(int n)
{
    
    
    int count = 0;
    while(n != 0)
    {
    
    
        n &= (n-1);
        count++;
    }

    return count;
}
  • Find the number of 0s in binary:
int fun(int value)
{
    
    
    int count = 0;
    while(!value)
    {
    
    
        count++;
        value |= (value+1);
    }
}

Big and small problem

  • The difference between large and small endian and their respective advantages, when to use
    big endian advantage : The sign bit is in the first byte of the content of the data represented, which is convenient to quickly determine the sign of the data and the size;
    little endian advantage : low address lower word Section , so there is no need to adjust the byte content during the forced conversion. When the CPU performs numerical calculations, it takes data from low to high in the memory and performs operations until the sign bit of the highest bit is refreshed. This method will be more efficient.

Use an example to illustrate:
Insert picture description here

  • The above questions:
    (1) If stored in big-endian mode : from low address to high address: 20 15 08 10
    when storing, the high byte lowers the address , and the output is from low address to high address : 20 15 08 10, so the result is: 2015 810
    (2) If stored in little-endian mode : from low address to high address: 10 08 15 20
    , high byte puts high address when stored, output from high address to low address : 08 10 20 15, so the result is: 810 2015

Nearest common ancestor of binary tree

I read a question on LeetCode, and I think it’s good, so record it

  • Problem: Given a binary tree, find the nearest common ancestor of two specified nodes in the tree.
    Insert picture description here
  • Problem solution: The main idea is recursion.
    When we use recursion to do this problem, don’t be misled by the title. It should be clear. This function has three functions: given two nodes p and q
    (1) If both p and q exist, Then return their common ancestor;
    (2) If there is only one, return the existing one;
    (3) If pp and qq do not exist, return NULL.
  • Specific ideas
    (1) If the current node root is equal to NULL, it will return NULL directly;
    (2) If root is equal to p or q, then this tree must return p or q;
    (3) Then recursive left and right subtrees, because it is recursive, After using the function, it can be considered that the left and right subtrees have calculated results, which are represented by left and right;
    (4) If left is empty, then the final result only needs to look at right; if right is empty, then the final result only needs to look at left;
    (5) If both left and right are non-empty, because only two nodes p and q are given, both are not empty, indicating that one side is one, so root is their nearest common ancestor;
    (6) If both left and right are empty, return Empty (actually included in the previous case).
  • The time complexity is O(n): each node can be traversed at most once or the main theorem is used, and the space complexity is O(n): system stack space is required
  • C++ implementation :
/**
 * 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; // 必须有返回值
    }
};

Why use char * for the return value of strcpy function

  • The function prototype of known strcpy: char *strcpy(char *strDest, char *strSrc) where strDest is the destination string and strSrc is the source string; strcpy can copy the content of strSrc to strDest, why do you need char * as the return value?
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 ;
}
  • This is because returning the original value of char * enables the function to support chained expressions , increasing the "added value" of the function. If the function of the same function can reasonably improve the usability, it will naturally be more ideal.
    Chain expressions such as:
int iLength=strlen(strcpy(strA,strB));

The difference between rewriting (overwriting), overloading, and hiding in C++

  • Rewrite (overwrite) : It is a function that is redefined in the assigned class. Its function name, parameter list, return value type, all must be consistent with the function being overridden in the base class. Only the function body is different (in the curly braces), the overriding function of the derived class will be called when the derived class is called, and the overridden function will not be called. The overridden function in the overridden base class must be decorated with virtual.
  • Overload : It refers to several functions of the same name with different parameter lists (different parameter types, numbers, and orders) declared in the same accessible area. The parameter list determines which function to call. Overloading does not care about the return type of the function.
  • The difference between overriding and overloading:
    (1) The difference in scope : the overridden and overridden functions are in two classes , and the overloaded and overloaded functions are in the same class ;
    (2) the difference in parameters : The parameter list of the rewritten function and the rewritten function must be the same , and the parameter list of the overloaded function and the overloaded function must be different .
    (3) The difference between virtual : The rewritten function in the rewritten base class must be modified with virtual , and the overloaded function and the overloaded function can be decorated with virtual or not.
  • There are the following differences between hiding and rewriting and overloading:
    (1) The scope of hiding and overloading is different: like rewriting, the hidden function and the hidden function are not in the same class;
    (2) The parameter difference: hidden function and The parameter list of the hidden function can be the same or different, but the function name must be the same. When the parameters are not the same, regardless of whether the function in the base class is virtual modified or not, the function of the base class is hidden instead of being overridden.
    Insert picture description here
  • An answer summarized by Niuke, I feel good:
a.成员函数被重载的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual 关键字可有可无。

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

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

Guess you like

Origin blog.csdn.net/qq_41782149/article/details/107370623