埋め込まれた面接筆記試験の質問 (13 日目)


序文

この記事では引き続き質問を取り上げます。

1. 純粋仮想関数と仮想関数の違い

1. 実装: 純粋仮想関数には特定の実装コードはなく、関数宣言の最後に = 0 を追加することで示される関数プロトタイプのみがあります。仮想関数にはデフォルトの実装コードがありますが、派生クラスでオーバーライドできます。

2. 抽象クラス: 純粋な仮想関数を含むクラスは抽象クラスであり、オブジェクトを直接インスタンス化することはできず、他の具象クラスの基本クラスとしてのみ使用できます。仮想関数を含むクラスはオブジェクトをインスタンス化できますが、少なくとも 1 つの純粋な仮想関数が含まれている場合、そのクラスは依然として抽象クラスです。

3. 継承と実装の要件: 派生クラスは基本クラスに純粋仮想関数を実装する必要があります。実装しない場合、派生クラスは抽象クラスのままになります。仮想関数の場合、派生クラスはオプションで基本クラスの実装をオーバーライドまたは継承できます。

4. 呼び出し方法: 仮想関数は動的バインディングを通じて呼び出され、オブジェクトの実際のタイプに基づいて関数のバージョンが呼び出されます。純粋な仮想関数には特定の実装がなく、直接呼び出すことはできず、派生クラスで書き換えた後にのみ呼び出すことができます。

2. 構造体とクラスの違い

1. デフォルトのアクセス レベル: クラスでは、メンバーのデフォルトのアクセス レベルはプライベートですが、構造体のデフォルトのアクセス レベルはパブリックです。これは、構造体で定義されたメンバー変数とメンバー関数はデフォルトで外部からアクセス可能ですが、クラス内ではデフォルトでプライベートであり、アクセス レベルを設定するためにアクセス修飾子を使用する必要があることを意味します。

2. 継承: クラスは継承を使用して新しいクラスを派生し、コードの再利用と階層設計を実現できます。構造体は他の構造体やクラスを直接継承することはできず、主に関連するデータ フィールドを整理するために使用されます。

3. メンバー関数: クラスは、操作データの動作をカプセル化するメンバー関数を定義できます。構造体は通常、データを保存するために使用され、動作は含まれません。ただし、C++ では、構造体にメンバー関数を含めることもできます。これは、クラスのメンバー関数と本質的には変わりません。

4. デフォルトのコンストラクターとデストラクター: クラスで、コンストラクターとデストラクターが明示的に定義されていない場合、コンパイラーはクラスのデフォルトのコンストラクターとデストラクターを生成します。構造体では、コンストラクターとデストラクターが明示的に定義されていない場合、コンパイラーはその構造体のデフォルトのコンストラクターとデストラクターも生成します。

3. スピンロックとセマフォの違い

スピン ロックがロックの取得を待機している間、スレッドは待機中で忙しく、積極的にスリープ状態にはなりません。ロックを取得するまで、ロックのステータスを継続的にチェックします。

セマフォがリソースを取得できない場合、スレッドまたはプロセスがスリープ状態になり、CPU リソースが他のスレッドまたはプロセスに放棄されます。他のスレッドまたはプロセスがリソースを解放し、セマフォのカウント値を増やすと、休止状態に入ったスレッドまたはプロセスが目覚めて実行を継続します。

4. 構造の整列を解除する方法

1. 前処理ディレクティブを使用する: 前処理ディレクティブ #pragma Pack または #pragma Pack() を使用して、構造体の配置を変更できます。たとえば、バイト アラインメントに設定します。

#pragma pack(1)

struct MyStruct {
    
    
    // 结构体成员...
};

#pragma pack()

上記のコードでは、#pragma Pack(1) は構造体が 1 バイトにアライメントされることを指定し、#pragma Pack() はデフォルトのアライメント (通常はコンパイラのデフォルトのアライメント規則に従って) を復元するために使用されます。

2. コンパイラ属性を使用する: 一部のコンパイラは、アライメントを制御する構造を定義するときに使用できる特定の属性または修飾子を提供します。たとえば、 GCC コンパイラの属性((packed)) 属性を使用すると、次のようになります。

struct __attribute__((packed)) MyStruct {
    
    
    // 结构体成员...
};

上記のコードでは、属性((packed)) 属性でバイト アライメント (アンアライメント) を指定しています。

5. リンクリストの交差

1.リンクリスト交差とは何ですか?

リンク リストの交差とは、2 つのリンク リストが特定のノードで重なり、共有部分を形成することを意味します。具体的には、2 つのリンク リストが交差するということは、それらのリスト内の少なくとも 1 つのノードが同じであり、このノードの後のすべてのノードが同じであることを意味します。

交差リンクリストの概略図は次のとおりです。

List A:    a1 → a2
                  ↘
                    c1 → c2 → c3
                  ↗            
List B: b1 → b2 → b3

上図では、リンクリストAとリンクリストBがノードc1で交差しており、その後のノードc2、c3も同じです。

なお、連結リストの共通部分は、2つの連結リストの長さが等しい場合に限定されない。交差するノードは短いリンク リストのどこにでも出現する可能性があり、交差する前の 2 つのリンク リストの部分の長さが異なる場合があります。

2 つのリンク リストが交差するかどうかを判断する目的は、それらの交差ノードを見つけることです。2 つのリンク リストが交差する場合、交差するノードから始まるすべてのノードは同じになります。リンク リストを走査し、ノードが等しいかどうかを比較し、交差するノードの位置を見つけることによって、リンク リストが交差するかどうかを判断できます。

2. リンクされたリストが交差するかどうかを判断するにはどうすればよいですか?

1. まず、2 つのリンクされたリストを走査して、それぞれの長さを取得します。2 つのポインタを使用して 2 つのリンク リストのヘッド ノードをそれぞれ指すことができ、2 つのリンク リストの長さは、ポインタを継続的に後方に移動してカウントすることによって取得できます。

2. 2 つのリンク リストの末尾ノードが等しくない場合、つまり最後のノードが異なる場合、2 つのリンク リストは交差してはならず、素の結果が直接返される可能性があります。

3. 2 つのリンクされたリストの終了ノードが等しい場合、それらは同じ終了部分を持ち、交差する可能性があることを意味します。このとき、2 つのリンク リストの長さの差(長いリンク リストから短いリンク リストを引いた値)を計算し、長い方のリンク リストのポインタを長さの差と同じステップ数だけ後方に移動します。 2 つのリンクされたリストの残りの長さは同じです。

4. このとき、2 つのリンクされたリストを同時に走査し、ノードが等しいかどうかを比較します。等しいノードが見つかった場合、2 つのリンクされたリストは交差します。2 つのリンク リストを走査した後に等しいノードが見つからない場合は、2 つのリンク リストが互いに素であることを意味します。

#include <stdbool.h>

// 定义链表节点结构
struct ListNode {
    
    
    int val;
    struct ListNode *next;
};

// 获取链表的长度
int getLinkedListLength(struct ListNode* head) {
    
    
    int length = 0;
    while (head != NULL) {
    
    
        length++;
        head = head->next;
    }
    return length;
}

// 判断两个链表是否相交
bool isLinkedListIntersect(struct ListNode* headA, struct ListNode* headB) {
    
    
    if (headA == NULL || headB == NULL) {
    
    
        return false;
    }

    // 获取链表的长度
    int lengthA = getLinkedListLength(headA);
    int lengthB = getLinkedListLength(headB);

    // 将指针移动到相同长度的起始位置
    int diff = lengthA - lengthB;
    if (diff > 0) {
    
    
        while (diff > 0) {
    
    
            headA = headA->next;
            diff--;
        }
    } else {
    
    
        while (diff < 0) {
    
    
            headB = headB->next;
            diff++;
        }
    }

    // 比较节点是否相等
    while (headA != NULL && headB != NULL) {
    
    
        if (headA == headB) {
    
    
            return true; // 相交
        }
        headA = headA->next;
        headB = headB->next;
    }

    return false; // 不相交
}

6. 握手が 3 回、手を振るのが 4 回あるのはなぜですか?

1.3回の握手

3 ウェイ ハンドシェイクの目的は、双方が相手方から確認を受け取り、双方が接続を確立することに同意することを保証することです。このハンドシェイク メカニズムにより、無効な接続要求セグメントがサーバーに再度送信され、リソースの無駄が発生することを防ぐことができます。同時に、ハンドシェイクごとにシーケンス番号を交換することで、期限切れの接続要求が新しい接続と誤って認識されるのを防ぐことができます。これにより、TCP 接続の信頼性と有効性が保証されます。

2. 4回手を振る

4 回手を振る目的は、双方がデータ送信を完了し、シャットダウン要求を確認できることを確認することです。4 回手を振ることで、送信されたすべてのデータが受信者に確実に受信され、その後に存在する可能性のあるデータ送信が接続を閉じる前に完了することができます。同時に、TIME-WAIT 状態で一定期間待機することにより、接続を閉じた後に残留する可能性のある遅延データ パケットが次の接続に干渉することを回避できます。

要約する

この記事ではここで説明します。

おすすめ

転載: blog.csdn.net/m0_49476241/article/details/132495549
おすすめ