Việc làm

18/09 · Hồ Chí Minh · Tại văn phòng

Embedded Firmware Engineer – BLE & IoT

Safetrust · Embedded Engineer

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

10 câu hỏi có thể gặp khi phỏng vấn

Chọn theo kỹ năng và cấp của tin (C/C++ & Embedded). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.

  1. 01Con trỏ và tham chiếu (reference) trong C++ khác nhau thế nào?C/C++ & Embedded · Junior

    Gợi ý trả lời

    Con trỏ là biến chứa địa chỉ, có thể nullptr và trỏ sang đối tượng khác. Tham chiếu là tên gọi khác của một đối tượng đã tồn tại, phải khởi tạo ngay và không gán lại được.

    int x = 1, y = 2;
    int* p = &x;  p = &y;   // đổi đối tượng được trỏ
    int& r = x;   r = y;    // gán giá trị y cho x
    
    • Truyền tham số: dùng const T& cho đối tượng lớn chỉ đọc, T& khi cần sửa, con trỏ khi tham số có thể vắng mặt.
    • Có con trỏ tới con trỏ và phép toán số học trên con trỏ; tham chiếu thì không.

    Hay sai: trả về tham chiếu hoặc con trỏ tới biến cục bộ. Biến bị huỷ khi hàm kết thúc và dùng nó là undefined behavior.

    Ôn thêm câu hỏi C/C++ & Embedded →
  2. 02Stack và heap khác nhau thế nào? Memory leak và dangling pointer là gì?C/C++ & Embedded · Junior

    Gợi ý trả lời

    Stack chứa biến cục bộ, cấp phát và giải phóng tự động theo phạm vi hàm, rất nhanh nhưng kích thước nhỏ. Heap cấp phát động (new/malloc), sống đến khi giải phóng thủ công, linh hoạt nhưng chậm hơn và dễ phân mảnh.

    • Memory leak: cấp phát trên heap mà không giải phóng, bộ nhớ tăng dần. Trên thiết bị nhúng chạy lâu ngày đây là lỗi chết người.
    • Dangling pointer: con trỏ vẫn giữ địa chỉ của vùng nhớ đã giải phóng hoặc biến đã hết phạm vi.
    • Double free: giải phóng một vùng hai lần.

    Phòng tránh: dùng RAII và smart pointer trong C++, đặt con trỏ về NULL sau free trong C, kiểm tra bằng AddressSanitizer hoặc Valgrind.

    Người phỏng vấn muốn nghe (nhúng): nhiều hệ thống hạn chế hoặc cấm cấp phát động sau khi khởi động, dùng memory pool hoặc mảng tĩnh.

    Ôn thêm câu hỏi C/C++ & Embedded →
  3. 03Từ khoá volatile dùng để làm gì trong lập trình nhúng?C/C++ & Embedded · Junior

    Gợi ý trả lời

    volatile báo cho compiler rằng giá trị biến có thể thay đổi ngoài luồng code hiện tại, nên mỗi lần truy cập phải đọc hoặc ghi thật vào bộ nhớ, không được tối ưu bỏ hay cache vào thanh ghi.

    Dùng cho:

    • Thanh ghi phần cứng ánh xạ bộ nhớ (memory-mapped register).
    • Biến chia sẻ giữa ISR (hàm phục vụ ngắt) và vòng lặp chính.
    volatile bool data_ready = false;
    void UART_IRQHandler(void) { data_ready = true; }
    while (!data_ready) { }  // không có volatile có thể thành lặp vô hạn
    

    Hay sai: nghĩ volatile đảm bảo thread-safe hoặc atomic. Nó không chống race condition và không tạo memory barrier giữa các core; cần atomic, tắt ngắt hoặc mutex.

    Ôn thêm câu hỏi C/C++ & Embedded →
  4. 04Giải thích 4 tính chất OOP và virtual function / vtable trong C++.C/C++ & Embedded · Junior

    Gợi ý trả lời

    Bốn tính chất: đóng gói (ẩn dữ liệu sau interface), kế thừa, đa hình (gọi cùng interface, hành vi theo kiểu thật) và trừu tượng (chỉ lộ cái cần thiết).

    Đa hình lúc chạy trong C++ dùng virtual: mỗi class có hàm ảo có một bảng vtable chứa địa chỉ hàm, mỗi object giữ một con trỏ vptr tới bảng đó. Gọi qua con trỏ hoặc tham chiếu lớp cha sẽ tra vtable để gọi hàm của lớp con.

    struct Sensor { virtual ~Sensor() = default; virtual int read() = 0; };
    struct TempSensor : Sensor { int read() override { return 25; } };
    

    Hay sai: quên destructor ảo ở lớp cha, khi delete qua con trỏ lớp cha sẽ không gọi destructor lớp con. Trên nhúng cũng nên nhắc chi phí: thêm vptr cho mỗi object và một lần gọi gián tiếp.

    Ôn thêm câu hỏi C/C++ & Embedded →
  5. 05RAII là gì? So sánh unique_ptr, shared_ptr và weak_ptr.C/C++ & Embedded

    Gợi ý trả lời

    RAII (Resource Acquisition Is Initialization): gắn tài nguyên vào vòng đời một object; constructor lấy tài nguyên, destructor tự giải phóng. Nhờ vậy không rò rỉ kể cả khi có exception hoặc return sớm.

    • unique_ptr: sở hữu duy nhất, chỉ move được, không tốn thêm chi phí so với con trỏ thường. Nên là lựa chọn mặc định.
    • shared_ptr: sở hữu chung bằng bộ đếm tham chiếu (cập nhật atomic), giải phóng khi bộ đếm về 0.
    • weak_ptr: quan sát một shared_ptr mà không tăng bộ đếm, dùng phá vòng tham chiếu (vd. con trỏ ngược từ con về cha).
    auto dev = std::make_unique<Device>("/dev/i2c-1");
    

    Hay sai: dùng shared_ptr khắp nơi "cho an toàn", làm mờ quyền sở hữu và tốn chi phí atomic.

    Ôn thêm câu hỏi C/C++ & Embedded →
  6. 06Move semantics và rvalue reference giải quyết vấn đề gì? Rule of 3/5/0 là gì?C/C++ & Embedded

    Gợi ý trả lời

    Move semantics cho phép chuyển tài nguyên (buffer, handle) từ một object tạm hoặc sắp bị bỏ đi sang object khác thay vì sao chép sâu. Rvalue reference T&& nhận các object như vậy; std::move chỉ là phép ép kiểu để cho phép move.

    std::vector<int> a(1'000'000);
    std::vector<int> b = std::move(a);  // chuyển con trỏ, không chép 1 triệu phần tử
    
    • Rule of 3: nếu tự viết destructor, copy constructor hoặc copy assignment thì gần như phải viết đủ cả ba.
    • Rule of 5: thêm move constructor và move assignment.
    • Rule of 0: tốt nhất là không tự viết hàm nào, để các thành viên RAII (vector, unique_ptr) lo.

    Hay sai: dùng lại object sau khi đã move. Object vẫn hợp lệ nhưng ở trạng thái không xác định.

    Ôn thêm câu hỏi C/C++ & Embedded →
  7. 07Viết hàm phục vụ ngắt (ISR) cần lưu ý những gì?C/C++ & Embedded

    Gợi ý trả lời

    ISR phải ngắn và nhanh: ghi nhận sự kiện, lấy dữ liệu tối thiểu, rồi để phần xử lý nặng cho task hoặc vòng lặp chính.

    • Không gọi hàm có thể block (delay, chờ mutex), không printf, tránh cấp phát động.
    • Biến dùng chung với code chính khai báo volatile và truy cập atomic, hoặc tắt ngắt ngắn khi đọc dữ liệu nhiều byte.
    • Xoá cờ ngắt đúng cách để không bị gọi lại liên tục.
    • Với RTOS, chỉ dùng API dành cho ngắt (vd. hàm có hậu tố FromISR trong FreeRTOS) để báo task qua semaphore, queue hoặc task notification.
    • Dùng ring buffer cho dữ liệu đến liên tục như UART.

    Người phỏng vấn muốn nghe: bạn biết về độ ưu tiên ngắt, ngắt lồng nhau, và cách đo thời gian chạy ISR bằng cách bật tắt một chân GPIO rồi xem trên oscilloscope.

    Ôn thêm câu hỏi C/C++ & Embedded →
  8. 08RTOS khác vòng lặp super-loop thế nào? Mutex khác semaphore ra sao?C/C++ & Embedded

    Gợi ý trả lời

    Super-loop chạy tuần tự các việc trong một vòng while(1), đơn giản nhưng một việc chậm sẽ làm trễ mọi việc khác. RTOS có scheduler preemptive theo độ ưu tiên, giúp các việc cần thời gian thực được đáp ứng đúng hạn.

    • Mutex: khoá loại trừ cho tài nguyên dùng chung, có khái niệm sở hữu (ai khoá thì người đó mở) và thường có priority inheritance để giảm đảo ưu tiên.
    • Binary semaphore: dùng để báo hiệu giữa ISR và task hoặc giữa các task, không có sở hữu.
    • Counting semaphore: quản lý N tài nguyên giống nhau.

    Hay sai: dùng semaphore thay mutex để bảo vệ tài nguyên, gặp priority inversion: task ưu tiên cao chờ task thấp đang giữ khoá, trong khi task trung bình chiếm CPU.

    Người phỏng vấn muốn nghe: bạn biết đặt độ ưu tiên task và kích thước stack hợp lý, và từng phát hiện stack overflow.

    Ôn thêm câu hỏi C/C++ & Embedded →
  9. 09Undefined behavior trong C/C++ là gì? Cho ví dụ và cách phát hiện.C/C++ & Embedded · Senior

    Gợi ý trả lời

    Undefined behavior (UB) là những thao tác mà chuẩn ngôn ngữ không quy định kết quả. Compiler được phép giả định UB không bao giờ xảy ra và tối ưu theo giả định đó, nên lỗi có thể chỉ lộ ra ở bản build -O2 hoặc trên một compiler khác.

    Ví dụ hay gặp:

    • Tràn số nguyên có dấu (tràn số không dấu thì được định nghĩa là quay vòng).
    • Truy cập mảng ngoài phạm vi, dereference con trỏ null hoặc dangling.
    • Đọc biến chưa khởi tạo, data race giữa các thread.
    • Vi phạm strict aliasing, vd. ép float* sang int* rồi đọc (nên dùng memcpy hoặc std::bit_cast từ C++20).

    Phát hiện: bật cảnh báo -Wall -Wextra, chạy test với -fsanitize=address,undefined, dùng static analysis (clang-tidy, cppcheck, MISRA checker).

    Người phỏng vấn muốn nghe: "chạy đúng trên máy tôi" không chứng minh code không có UB.

    Ôn thêm câu hỏi C/C++ & Embedded →
  10. 10Bạn phản ứng thế nào khi nhận góp ý tiêu cực về công việc của mình?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Cho thấy bạn tách góp ý khỏi cảm xúc cá nhân và biến nó thành hành động.

    • Lắng nghe hết, hỏi lại để hiểu ví dụ cụ thể và kỳ vọng.
    • Cảm ơn, kể cả khi chưa đồng ý hoàn toàn; nếu có góc nhìn khác, trao đổi bằng thông tin, không phòng thủ.
    • Lên kế hoạch cải thiện và chủ động xin phản hồi lại sau một thời gian.

    Câu mẫu: "Có lần lead góp ý code review của em quá dài, khó review. Lúc đầu em hơi chột dạ, nhưng em hỏi anh muốn chia thế nào cho dễ. Từ đó em tách pull request nhỏ theo từng thay đổi, thời gian review của team giảm hẳn và em cũng ít bị yêu cầu sửa lại hơn."

    Hay sai: nói "em luôn vui vẻ đón nhận mọi góp ý" mà không có ví dụ.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở Safetrust?

Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.

Chia sẻ kinh nghiệm phỏng vấn