태그 보관물: embedded

embedded

임베디드 개발을 위해 C ++ 대신 C를 사용하는 이유가 있습니까? 인해 C ++가 약간 더 안전하다고 생각합니다. 오버로드

질문

하드웨어 C ++ 및 C89에 두 개의 컴파일러가 있습니다.

클래스와 함께 C ++를 사용하는 것에 대해 생각하고 있지만 (vtables를 피하기 위해) 다형성이 없습니다. C ++를 사용하고 싶은 주된 이유는 다음과 같습니다.

  • 매크로 정의 대신 “인라인”함수를 사용하는 것을 선호합니다.
  • 접두사가 코드를 복잡하게 만들 때 네임 스페이스를 사용하고 싶습니다.
  • 주로 템플릿과 장황한 캐스팅으로 인해 C ++가 약간 더 안전하다고 생각합니다.
  • 오버로드 된 함수와 생성자 (자동 캐스팅에 사용)를 정말 좋아합니다.

매우 제한된 하드웨어 (4kb RAM)를 개발할 때 C89를 고수 할 이유가 있습니까?

결론

답변 해 주셔서 감사합니다. 정말 도움이되었습니다!

나는 주제를 통해 생각했고 주로 다음과 같은 이유로 C를 고수 할 것입니다.

  1. C에서 실제 코드를 예측하는 것이 더 쉬우 며 4kb의 램만 있으면 정말 중요합니다.
  2. 우리 팀은 주로 C 개발자로 구성되어 있으므로 고급 C ++ 기능은 자주 사용되지 않습니다.
  3. 내 C 컴파일러 (C89)에서 함수를 인라인하는 방법을 찾았습니다.

좋은 답변을 너무 많이 제공했기 때문에 하나의 답변을 받아들이 기가 어렵습니다. 안타깝게도 위키를 만들고 수락 할 수 없으므로 가장 생각하게 만든 답변을 하나 선택하겠습니다.



답변

C ++보다 C를 사용하는 두 가지 이유 :

  1. 많은 임베디드 프로세서의 경우 C ++ 컴파일러가 없거나 추가 비용을 지불해야합니다.
  2. 내 경험으로는 임베디드 소프트웨어 엔지니어의 상당수가 C ++에 대한 경험이 거의 없거나 전혀 없다는 것입니다. (1) 때문이거나 전자 공학 학위에 대한 교육을받지 않는 경향이 있기 때문입니다. 따라서 계속 사용하는 것이 좋습니다. 그들이 아는 것.

또한 원래 질문과 많은 의견에는 4Kb의 RAM이 언급되어 있습니다. 일반적인 임베디드 프로세서의 경우 RAM의 양은 코드가 저장되고 플래시에서 실행되므로 코드 크기와 관련이 없습니다.

확실히 코드 저장 공간의 양은 염두에 두어야 할 사항이지만, 새롭고 용량이 더 큰 프로세서가 시장에 등장함에 따라 가장 비용에 민감한 프로젝트를 제외하고는 예전보다 문제가 적습니다.

임베디드 시스템과 함께 사용하기 위해 C ++의 하위 집합 사용 : 이제 MISRA C ++ 표준이 있습니다.

편집 : 임베디드 시스템의 C 대 C ++에 대한 논쟁으로 이어진 이 질문 도 참조하십시오 .


답변

4KB의 RAM과 같이 리소스 가 매우 제한된 대상의 경우 순수한 ANSI C 구현으로 쉽게 다시 이식 할 수없는 많은 노력을 기울이기 전에 몇 가지 샘플로 물을 테스트했습니다.

임베디드 C ++ 워킹 그룹은 언어의 표준 하위 집합과 표준 라이브러리의 표준 하위 집합을 제안했습니다. 불행히도 C 사용자의 저널이 죽었을 때 그 노력을 잃어 버렸습니다. 거기에서 기사 것 같습니다 위키 백과 하고 있다고 위원회는 여전히 존재합니다.

임베디드 환경에서는 메모리 할당에주의해야합니다. 이러한 관리를 시행하려면 사용 operator new()되지 않음을 알 수 있도록 연결될 수없는 무언가에 전역 및 그 친구 를 정의해야 할 수 있습니다. new반면에 안정적이고 스레드 안전하며 지연 시간이 보장되는 할당 체계와 함께 신중하게 사용하면 배치 는 친구가 될 수 있습니다.

인라인 함수는 애초에 진정한 함수 여야 할만큼 충분히 크지 않는 한 많은 문제를 일으키지 않습니다. 물론 매크로를 대체하는데도 동일한 문제가있었습니다.

인스턴스화가 제대로 실행되지 않는 한 템플릿도 문제를 일으키지 않을 수 있습니다. 사용하는 템플릿에 대해 생성 된 코드 (링크 맵에 충분한 단서가있을 수 있음)를 감사하여 사용하려는 인스턴스화 만 발생했는지 확인하십시오.

발생할 수있는 또 다른 문제는 디버거와의 호환성입니다. 다른 방법으로 사용 가능한 하드웨어 디버거가 원본 소스 코드와의 상호 작용을 매우 제한적으로 지원하는 것은 드문 일이 아닙니다. 어셈블리에서 효과적으로 디버그해야하는 경우 C ++의 흥미로운 이름 변경으로 인해 작업에 추가 혼란이 추가 될 수 있습니다.

RTTI, 동적 캐스트, 다중 상속, 무거운 다형성 및 예외는 모두 사용에 대해 어느 정도의 런타임 비용과 함께 제공됩니다. 이러한 기능 중 일부는 사용되는 경우 전체 프로그램에 비해 비용이 많이 드는 수준이고 다른 일부는 필요한 클래스의 가중치를 증가시킵니다. 차이점을 알고 최소한 간단한 비용 / 이익 분석에 대한 완전한 지식을 가지고 고급 기능을 현명하게 선택하십시오.

작은 임베디드 환경에서는 실시간 커널에 직접 연결하거나 하드웨어에서 직접 실행합니다. 어느 쪽이든 런타임 시작 코드가 C ++ 특정 시작 작업을 올바르게 처리하는지 확인해야합니다. 이는 올바른 링커 옵션을 사용하는지 확인하는 것만 큼 간단 할 수 있지만 전원 켜기 재설정 진입 점에 대한 소스를 직접 제어하는 ​​것이 일반적이므로 모든 작업을 수행하는지 확인하기 위해 감사해야 할 수도 있습니다. 예를 들어 제가 작업 한 ColdFire 플랫폼에서 개발 도구는 C ++ 이니셜 라이저가 있지만 주석 처리 된 CRT0.S 모듈과 함께 제공되었습니다. 상자에서 바로 사용했다면 생성자가 전혀 실행되지 않은 전역 개체에 의해 미스터리되었을 것입니다.

또한 임베디드 환경에서는 하드웨어 장치를 사용하기 전에 초기화해야하는 경우가 많으며, OS 및 부트 로더가없는 경우이를 수행하는 것이 코드입니다. 전역 개체에 대한 생성자 는를 호출 하기 전에 실행 main()되므로 전역 생성자가 호출 되기 전에 하드웨어 초기화를 완료하려면 로컬 CRT0.S (또는 이에 상응하는 항목)를 수정해야합니다 . 분명히, 정상은 main()너무 늦었습니다.


답변

아니요. 문제를 일으킬 수있는 C ++ 언어 기능 (런타임 다형성, RTTI 등)은 임베디드 개발을 수행하는 동안 피할 수 있습니다. 임베디드 C ++ 개발자 커뮤니티가 있습니다 (예전 C / C ++ 사용자 저널에서 C ++를 사용하는 임베디드 개발자의 칼럼을 읽은 기억이 있습니다). 선택이 그렇게 나쁘다면 그들이 매우 목소리를 낼 것이라고 상상할 수 없습니다.


답변

C ++ 성능에 대한 기술 보고서 는 이러한 종류의 훌륭한 가이드입니다. 임베디드 프로그래밍 문제에 대한 섹션이 있습니다!

또한 답변에서 Embedded C ++에 대한 언급에 대한 ++. 표준은 내 취향에 100 %는 아니지만 C ++의 어떤 부분을 떨어 뜨릴 지 결정할 때 좋은 참고 자료입니다.

소규모 플랫폼 용으로 프로그래밍하는 동안 예외 및 RTTI를 비활성화하고, 가상 상속을 피하고, 주변에있는 가상 기능의 수에 세심한주의를 기울였습니다.

그러나 당신의 친구는 링커 맵입니다. 자주 확인하면 코드 소스와 정적 메모리 팽창을 빠르게 발견 할 수 있습니다.

그 후에는 표준 동적 메모리 사용 고려 사항이 적용됩니다. 언급 한 것처럼 제한된 환경에서는 동적 할당을 전혀 사용하지 않는 것이 좋습니다. 때로는 작은 동적 할당을위한 메모리 풀이나 블록을 미리 할당하고 나중에 전체를 버리는 “프레임 기반”할당을 사용할 수 있습니다.


답변

C ++ 컴파일러를 사용하는 것이 좋지만 C ++ 특정 기능의 사용을 제한합니다. C ++에서 C처럼 프로그래밍 할 수 있습니다 (대부분의 임베디드 애플리케이션에서는 어쨌든 표준 라이브러리를 사용하지 않지만 C ++을 수행 할 때 C 런타임이 포함됩니다).

계속해서 C ++ 클래스 등을 사용할 수 있습니다.

  • 가상 기능의 사용을 제한하십시오 (말한대로)
  • 템플릿 사용 제한
  • 임베디드 플랫폼의 경우 new 연산자를 재정의하거나 메모리 할당을 위해 new 배치를 사용하고 싶을 것입니다.


답변

펌웨어 / 임베디드 시스템 엔지니어로서 C가 여전히 C ++보다 1 위를 선택하는 이유 중 일부를 말씀 드릴 수 있으며, 두 가지 모두에 능통합니다.

1) 우리가 개발하는 일부 타겟에는 코드와 데이터 모두에 대해 64kB의 RAM이 있으므로 모든 바이트 수를 확인해야합니다. 예, 2 시간이 소요되는 4 바이트를 절약하기 위해 코드 최적화를 처리했습니다. 2008.

2) 모든 C 라이브러리 함수는 크기 제한으로 인해 최종 코드에 포함되기 전에 검토되므로 분할 (하드웨어 분할기가 없으므로 큰 라이브러리가 필요함), malloc (힙이 없기 때문에)을 사용하지 않는 것이 좋습니다. , 모든 메모리는 512 바이트 청크의 데이터 버퍼에서 할당되며 코드 검토를 거쳐야 함) 또는 큰 페널티를 수반하는 기타 객체 지향 방식입니다. 사용하는 모든 라이브러리 함수가 카운트된다는 것을 기억하십시오.

3) 오버레이라는 용어를 들어 본 적이 있습니까? 코드 공간이 너무 작아서 때때로 다른 코드 세트로 교체해야합니다. 라이브러리 함수를 호출하는 경우 라이브러리 함수가 상주해야합니다. 오버레이 함수에서만 사용하면 너무 많은 객체 지향 메서드에 의존하여 많은 공간을 낭비하게됩니다. 따라서 C ++는 물론 C 라이브러리 함수도 허용되지 않는다고 가정하지 마십시오.

4) 제한된 하드웨어 설계 (예 : 특정 방식으로 연결된 ECC 엔진)로 인해 또는 하드웨어 버그에 대처하기 위해 캐스팅 및 패킹 (정렬되지 않은 데이터 구조가 단어 경계를 넘는 경우)이 필요합니다. 당신은 너무 많은 것을 암시 적으로 가정 할 수 없습니다. 그런데 왜 객체가 그것을 너무 많이 지향 하는가?

5) 최악의 시나리오 : 객체 지향 방법 중 일부를 제거하면 폭발 할 수있는 리소스 (즉, 데이터 버퍼가 아닌 스택에 512 바이트 할당)를 사용하기 전에 개발을 생각하고 잠재적 인 최악의 시나리오를 방지 할 수 있습니다. 전체 코드 경로를 함께 테스트하거나 제거하지 않습니다.

6) 우리는 소프트웨어로부터 하드웨어를 유지하고 코드를 가능한 한 이식 가능하고 시뮬레이션 친화적으로 만들기 위해 많은 추상화를 사용합니다. 하드웨어 액세스는 서로 다른 플랫폼간에 조건부로 컴파일되는 매크로 또는 인라인 함수로 래핑되어야합니다. 데이터 유형은 특정 대상이 아닌 바이트 크기로 캐스팅되어야합니다. 직접 포인터 사용은 허용되지 않습니다 (일부 플랫폼에서는 메모리 매핑 된 I / O가 데이터 메모리와 동일) 등

나는 더 많이 생각할 수 있지만 당신은 아이디어를 얻습니다. 우리 펌웨어 담당자는 객체 지향 교육을 받았지만 임베디드 시스템의 작업은 하드웨어 지향적이고 낮은 수준 일 수 있으므로 본질적으로 높은 수준이거나 추상화 할 수 없습니다.

BTW, 내가했던 모든 펌웨어 작업은 소스 제어를 사용합니다. 어디서 그 아이디어를 얻었는지 모르겠습니다.

-SanDisk의 펌웨어 전문가.


답변

개인적으로 선호하는 것은 C입니다.

  • 모든 코드 줄이 무엇을하고 있는지 (및 비용) 알고 있습니다.
  • 나는 모든 코드 라인이 무엇을하고 있는지 (그리고 비용) 알만큼 C ++를 잘 모른다

사람들은 왜 이렇게 말합니까? asm 출력을 확인하지 않으면 C의 모든 행이 무엇을하는지 알 수 없습니다 . C ++도 마찬가지입니다.

예를 들어,이 무고한 문이 생성하는 asm은 다음과 같습니다.

a[i] = b[j] * c[k];

상당히 순진 해 보이지만 gcc 기반 컴파일러는 8 비트 마이크로 용으로이 asm을 생성합니다.

CLRF 0x1f, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1f, F, ACCESS
MOVWF 0x1e, ACCESS
MOVLW 0xf9
MOVF 0xfdb, W, ACCESS
ADDWF 0x1e, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfa
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1f, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x1c
NOP
MOVFF 0xfef, 0x1d
NOP
MOVLW 0x1
CLRF 0x1b, ACCESS
RLCF 0xfdb, W, ACCESS
ANDLW 0xfe
RLCF 0x1b, F, ACCESS
MOVWF 0x1a, ACCESS
MOVLW 0xfb
MOVF 0xfdb, W, ACCESS
ADDWF 0x1a, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfc
MOVF 0xfdb, W, ACCESS
ADDWFC 0x1b, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0xfee, 0x18
NOP
MOVFF 0xfef, 0x19
NOP
MOVFF 0x18, 0x8
NOP
MOVFF 0x19, 0x9
NOP
MOVFF 0x1c, 0xd
NOP
MOVFF 0x1d, 0xe
NOP
CALL 0x2142, 0
NOP
MOVFF 0x6, 0x16
NOP
MOVFF 0x7, 0x17
NOP
CLRF 0x15, ACCESS
RLCF 0xfdf, W, ACCESS
ANDLW 0xfe
RLCF 0x15, F, ACCESS
MOVWF 0x14, ACCESS
MOVLW 0xfd
MOVF 0xfdb, W, ACCESS
ADDWF 0x14, W, ACCESS
MOVWF 0xfe9, ACCESS
MOVLW 0xfe
MOVF 0xfdb, W, ACCESS
ADDWFC 0x15, W, ACCESS
MOVWF 0xfea, ACCESS
MOVFF 0x16, 0xfee
NOP
MOVFF 0x17, 0xfed
NOP

생성되는 명령어의 수는 다음에 따라 크게 달라집니다.

  • a, b, c의 크기.
  • 해당 포인터가 스택에 저장되었는지 또는 전역인지 여부
  • i, j 및 k가 스택에 있는지 또는 글로벌인지 여부

이것은 프로세서가 C를 처리하도록 설정되지 않은 작은 임베디드 세계에서 특히 그렇습니다. 따라서 내 대답은 항상 asm 출력을 검사하지 않는 한 C와 C ++가 서로 나쁘다는 것입니다. 서로 똑같이 좋습니다.

휴고