재사용 가능한 구성요소 라이브러리: 대상 간 마이그레이션 단순화
마이크로 컨트롤러 외부 구성 요소 또는 대상 자체를 사용하여 작동하는 것은 펌웨어 개발의 표준입니다. 그러므로 이들을 위한 라이브러리를 개발하는 방법을 아는 것이 필수적입니다. 이러한 라이브러리를 통해 우리는 라이브러리와 상호 작용하고 정보나 명령을 교환할 수 있습니다. 그러나 레거시 코드 또는 학생(또는 학생이 아닌 사람)의 코드에서 구성 요소와의 이러한 상호 작용이 애플리케이션 코드에서 직접 수행되거나 별도의 파일에 배치된 경우에도 이러한 상호 작용이 본질적으로 발생하는 경우가 많습니다. 목표에 묶여 있습니다.
STMicroelectronics STM32F401RE용 애플리케이션에서 Bosch BME280 온도, 습도 및 압력 센서용 라이브러리 개발의 좋지 않은 예를 살펴보겠습니다. 이 예에서는 구성 요소를 초기화하고 1초마다 온도를 읽으려고 합니다. (예제 코드에서는 다양한 시계 및 주변 장치의 초기화 등 STM32CubeMX/IDE에서 발생하는 "노이즈"나 USER CODE BEGIN 또는 USER CODE END와 같은 주석을 모두 생략합니다.)
#include "i2c.h" #include <stdint.h> int main(void) { uint8_t idx = 0U; uint8_t tx_buffer[64] = {0}; uint8_t rx_buffer[64] = {0}; uint16_t dig_temp1 = 0U; int16_t dig_temp2 = 0; int16_t dig_temp3 = 0; MX_I2C1_Init(); tx_buffer[idx++] = 0b10100011; HAL_I2C_Mem_Write(&hi2c1, 0x77U << 1U, 0xF4U, 1U, tx_buffer, 1U, 200U); HAL_I2C_Mem_Read(&hi2c1, 0x77U << 1U, 0x88U, 1U, rx_buffer, 6U, 200U); dig_temp1 = ((uint16_t)rx_buffer[0]) | (((uint16_t)rx_buffer[1]) << 8U); dig_temp2 = (int16_t)(((uint16_t)rx_buffer[2]) | (((uint16_t)rx_buffer[3]) << 8U)); dig_temp3 = (int16_t)(((uint16_t)rx_buffer[4]) | (((uint16_t)rx_buffer[5]) << 8U)); while (1) { float temperature = 0.0f; int32_t adc_temp = 0; int32_t t_fine = 0; float var1 = 0.0f; float var2 = 0.0f; HAL_I2C_Mem_Read(&hi2c1, 0x77U << 1U, 0xFAU, 1U, rx_buffer, 3U, 200U); adc_temp = (int32_t)((((uint32_t)rx_buffer[0]) << 12U) | (((uint32_t)rx_buffer[1]) << 4U) | (((uint32_t)rx_buffer[2]) >> 4U)); var1 = (((float)adc_temp) / 16384.0f - ((float)dig_temp1) / 1024.0f) * ((float)dig_temp2); var2 = ((((float)adc_temp) / 131072.0f - ((float)dig_temp1) / 8192.0f) * (((float)adc_temp) / 131072.0f - ((float)dig_temp1) / 8192.0f)) * ((float)dig_temp3); t_fine = (int32_t)(var1 + var2); temperature = ((float)t_fine) / 5129.0f; // Temperature available for the application. } }
이 예를 바탕으로 일련의 질문을 제기할 수 있습니다. 목표를 변경해야 하는 경우(재고 부족, 비용 절감 또는 단순히 동일한 구성 요소를 사용하는 다른 제품 작업 등) 어떻게 되나요? 시스템에 동일한 유형의 구성 요소가 두 개 이상 있으면 어떻게 되나요? 다른 제품이 동일한 구성 요소를 사용하면 어떻게 되나요? 아직 하드웨어가 없는 경우 어떻게 개발을 테스트할 수 있나요?(프로세스의 특정 지점에서 펌웨어 및 하드웨어 개발 단계가 종종 겹치는 전문 세계의 매우 일반적인 상황)?
처음 세 가지 질문에 대한 대답은 코드를 편집하는 것인지, 대상을 전환할 때 완전히 변경할 것인지, 기존 코드를 복제하여 동일한 유형의 추가 구성 요소와 작동할 것인지, 또는 동일한 코드를 구현하는 것인지입니다. 다른 프로젝트/제품. 마지막 질문에서는 코드를 실행할 하드웨어 없이 코드를 테스트할 수 있는 방법이 없습니다. 즉, 하드웨어가 완성된 후에야 코드 테스트를 시작하고 펌웨어 개발 자체에 내재된 오류를 수정하기 시작할 수 있으므로 제품 개발 시간이 연장됩니다. 이로 인해 이 게시물이 탄생하게 된 질문이 제기됩니다. 대상과 독립적이고 재사용이 가능한 구성 요소용 라이브러리를 개발하는 것이 가능합니까? 대답은 '예'입니다. 이번 게시물에서는 이에 대해 살펴보겠습니다.
대상에서 라이브러리 분리
타겟에서 라이브러리를 분리하기 위해 우리는 두 가지 규칙을 따릅니다. 1) 자체 컴파일 단위, 즉 자체 파일에 라이브러리를 구현하고 2) 타겟별 헤더나 함수에 대한 참조가 없습니다. . 우리는 BME280을 위한 간단한 라이브러리를 구현하여 이를 입증할 것입니다. 시작하려면 프로젝트 내에 bme280이라는 폴더를 만듭니다. bme280 폴더 안에 bme280.c, bme280.h, bme280_interface.h 파일을 생성합니다. 명확히 하자면, 아니요, 파일 이름을 bme280_interface.c로 지정하는 것을 잊지 않았습니다. 이 파일은 라이브러리의 일부가 아닙니다.
저는 보통 Application/lib/ 안에 라이브러리 폴더를 넣습니다.
bme280.h 파일은 애플리케이션에서 호출할 라이브러리에서 사용할 수 있는 모든 함수를 선언합니다. 반면, bme280.c 파일은 라이브러리에 포함될 수 있는 보조 및 개인 기능과 함께 해당 기능의 정의를 구현합니다. 그렇다면 bme280_interface.h 파일에는 무엇이 포함되어 있습니까? 글쎄, 우리의 목표는 그것이 무엇이든 어떤 방식으로든 BME280 구성 요소와 통신해야 합니다. 이 경우 BME280은 SPI 또는 I2C 통신을 지원합니다. 두 경우 모두 대상은 구성 요소에 바이트를 읽고 쓸 수 있어야 합니다. bme280_interface.h 파일은 해당 함수를 라이브러리에서 호출할 수 있도록 선언합니다. 이러한 기능의 정의는 특정 대상과 연결된 유일한 부분이 되며 라이브러리를 다른 대상으로 마이그레이션하는 경우 편집해야 하는 유일한 부분입니다.
라이브러리 API 선언
bme280.h 파일 내 라이브러리에서 사용 가능한 함수를 선언하는 것으로 시작합니다.
#include "i2c.h" #include <stdint.h> int main(void) { uint8_t idx = 0U; uint8_t tx_buffer[64] = {0}; uint8_t rx_buffer[64] = {0}; uint16_t dig_temp1 = 0U; int16_t dig_temp2 = 0; int16_t dig_temp3 = 0; MX_I2C1_Init(); tx_buffer[idx++] = 0b10100011; HAL_I2C_Mem_Write(&hi2c1, 0x77U << 1U, 0xF4U, 1U, tx_buffer, 1U, 200U); HAL_I2C_Mem_Read(&hi2c1, 0x77U << 1U, 0x88U, 1U, rx_buffer, 6U, 200U); dig_temp1 = ((uint16_t)rx_buffer[0]) | (((uint16_t)rx_buffer[1]) << 8U); dig_temp2 = (int16_t)(((uint16_t)rx_buffer[2]) | (((uint16_t)rx_buffer[3]) << 8U)); dig_temp3 = (int16_t)(((uint16_t)rx_buffer[4]) | (((uint16_t)rx_buffer[5]) << 8U)); while (1) { float temperature = 0.0f; int32_t adc_temp = 0; int32_t t_fine = 0; float var1 = 0.0f; float var2 = 0.0f; HAL_I2C_Mem_Read(&hi2c1, 0x77U << 1U, 0xFAU, 1U, rx_buffer, 3U, 200U); adc_temp = (int32_t)((((uint32_t)rx_buffer[0]) << 12U) | (((uint32_t)rx_buffer[1]) << 4U) | (((uint32_t)rx_buffer[2]) >> 4U)); var1 = (((float)adc_temp) / 16384.0f - ((float)dig_temp1) / 1024.0f) * ((float)dig_temp2); var2 = ((((float)adc_temp) / 131072.0f - ((float)dig_temp1) / 8192.0f) * (((float)adc_temp) / 131072.0f - ((float)dig_temp1) / 8192.0f)) * ((float)dig_temp3); t_fine = (int32_t)(var1 + var2); temperature = ((float)t_fine) / 5129.0f; // Temperature available for the application. } }
우리가 만드는 라이브러리는 매우 간단하며 기본적인 초기화 기능과 온도 측정값을 얻기 위한 또 다른 기능만 구현하겠습니다. 이제 bme280.c 파일에 함수를 구현해 보겠습니다.
게시물을 너무 장황하게 작성하지 않기 위해 기능을 설명하는 댓글은 건너뛰었습니다. 해당 댓글이 저장되는 파일입니다. 오늘날 사용할 수 있는 AI 도구가 너무 많기 때문에 코드를 문서화하지 않을 이유가 없습니다.
드라이버 API 구현
bme280.c 파일의 골격은 다음과 같습니다.
#ifndef BME280_H_ #define BME280_H_ void BME280_init(void); float BME280_get_temperature(void); #endif // BME280_H_
초기화에 집중해보자. 앞서 언급했듯이 BME280은 I2C 및 SPI 통신을 모두 지원합니다. 두 경우 모두 대상의 적절한 주변 장치(I2C 또는 SPI)를 초기화한 다음 이를 통해 바이트를 보내고 받을 수 있어야 합니다. I2C 통신을 사용한다고 가정하면 STM32F401RE에서는 다음과 같습니다.
#include "i2c.h" #include <stdint.h> int main(void) { uint8_t idx = 0U; uint8_t tx_buffer[64] = {0}; uint8_t rx_buffer[64] = {0}; uint16_t dig_temp1 = 0U; int16_t dig_temp2 = 0; int16_t dig_temp3 = 0; MX_I2C1_Init(); tx_buffer[idx++] = 0b10100011; HAL_I2C_Mem_Write(&hi2c1, 0x77U << 1U, 0xF4U, 1U, tx_buffer, 1U, 200U); HAL_I2C_Mem_Read(&hi2c1, 0x77U << 1U, 0x88U, 1U, rx_buffer, 6U, 200U); dig_temp1 = ((uint16_t)rx_buffer[0]) | (((uint16_t)rx_buffer[1]) << 8U); dig_temp2 = (int16_t)(((uint16_t)rx_buffer[2]) | (((uint16_t)rx_buffer[3]) << 8U)); dig_temp3 = (int16_t)(((uint16_t)rx_buffer[4]) | (((uint16_t)rx_buffer[5]) << 8U)); while (1) { float temperature = 0.0f; int32_t adc_temp = 0; int32_t t_fine = 0; float var1 = 0.0f; float var2 = 0.0f; HAL_I2C_Mem_Read(&hi2c1, 0x77U << 1U, 0xFAU, 1U, rx_buffer, 3U, 200U); adc_temp = (int32_t)((((uint32_t)rx_buffer[0]) << 12U) | (((uint32_t)rx_buffer[1]) << 4U) | (((uint32_t)rx_buffer[2]) >> 4U)); var1 = (((float)adc_temp) / 16384.0f - ((float)dig_temp1) / 1024.0f) * ((float)dig_temp2); var2 = ((((float)adc_temp) / 131072.0f - ((float)dig_temp1) / 8192.0f) * (((float)adc_temp) / 131072.0f - ((float)dig_temp1) / 8192.0f)) * ((float)dig_temp3); t_fine = (int32_t)(var1 + var2); temperature = ((float)t_fine) / 5129.0f; // Temperature available for the application. } }
주변기기가 초기화되면 구성요소를 초기화해야 합니다. 여기서는 제조업체가 데이터시트에 제공한 정보를 사용해야 합니다. 다음은 간략한 요약입니다. 온도 샘플링 채널(기본적으로 절전 모드에 있음)을 시작하고 구성 요소의 ROM에 저장된 일부 교정 상수를 읽어야 합니다. 이는 나중에 온도를 계산하는 데 필요합니다.
이 게시물의 목표는 BME280 사용법을 배우는 것이 아니므로 데이터시트에서 확인할 수 있는 자세한 사용법은 건너뛰겠습니다.
초기화는 다음과 같습니다.
#ifndef BME280_H_ #define BME280_H_ void BME280_init(void); float BME280_get_temperature(void); #endif // BME280_H_
코멘트할 세부정보입니다. 우리가 읽은 교정 값은 dig_temp1, dig_temp2 및 dig_temp3이라는 변수에 저장됩니다. 이러한 변수는 전역으로 선언되므로 라이브러리의 나머지 함수에 사용할 수 있습니다. 그러나 라이브러리 내에서만 액세스할 수 있도록 정적으로 선언됩니다. 도서관 외부의 누구도 이러한 값에 액세스하거나 수정할 필요가 없습니다.
또한 I2C 명령어의 반환 값을 확인하고, 실패할 경우 함수 실행이 중단되는 것을 볼 수 있습니다. 이것은 괜찮지만 개선될 수 있습니다. 그렇다면 BME280_init 함수 호출자에게 문제가 발생했음을 알리는 것이 더 낫지 않을까요? 이를 위해 bme280.h 파일에 다음 열거형을 정의합니다.
저는 typedef를 사용합니다. 세부 사항을 숨기는 대신 코드 가독성을 향상시키기 때문에 typedef 사용에 대한 논쟁이 있습니다. 이는 개인 취향의 문제이며 개발팀의 모든 구성원이 같은 생각을 갖고 있는지 확인하는 것입니다.
void BME280_init(void) { } float BME280_get_temperature(void) { }
두 가지 참고 사항: 저는 일반적으로 typedef에 _t 접미사를 추가하여 typedef임을 나타냅니다. 그리고 typedef의 값이나 멤버에 typedef 접두사를 추가합니다(이 경우 BME280_Status_). 후자는 다른 라이브러리의 열거형 간의 충돌을 방지하는 것입니다. 모두가 OK를 열거형으로 사용한다면 문제가 생길 것입니다.
이제 BME280_init 함수의 선언(bme280.h)과 정의(bme280.c)를 모두 수정하여 상태를 반환할 수 있습니다. 우리 함수의 최종 버전은 다음과 같습니다.
void BME280_init(void) { MX_I2C1_Init(); }
#include "i2c.h" #include <stdint.h> #define BME280_TX_BUFFER_SIZE 32U #define BME280_RX_BUFFER_SIZE 32U #define BME280_TIMEOUT 200U #define BME280_ADDRESS 0x77U #define BME280_REG_CTRL_MEAS 0xF4U #define BME280_REG_DIG_T 0x88U static uint16_t dig_temp1 = 0U; static int16_t dig_temp2 = 0; static int16_t dig_temp3 = 0; void BME280_init(void) { uint8_t idx = 0U; uint8_t tx_buffer[BME280_TX_BUFFER_SIZE] = {0}; uint8_t rx_buffer[BME280_RX_BUFFER_SIZE] = {0}; HAL_StatusTypeDef status = HAL_ERROR; MX_I2C1_Init(); tx_buffer[idx++] = 0b10100011; status = HAL_I2C_Mem_Write( &hi2c1, BME280_ADDRESS << 1U, BME280_REG_CTRL_MEAS, 1U, tx_buffer, (uint16_t)idx, BME280_TIMEOUT); if (status != HAL_OK) return; status = HAL_I2C_Mem_Read( &hi2c1, BME280_ADDRESS << 1U, BME280_REG_DIG_T, 1U, rx_buffer, 6U, BME280_TIMEOUT); if (status != HAL_OK) return; dig_temp1 = ((uint16_t)rx_buffer[0]); dig_temp1 = dig_temp1 | (((uint16_t)rx_buffer[1]) << 8U); dig_temp2 = ((int16_t)rx_buffer[2]); dig_temp2 = dig_temp2 | (((int16_t)rx_buffer[3]) << 8U); dig_temp3 = ((int16_t)rx_buffer[4]); dig_temp3 = dig_temp3 | (((int16_t)rx_buffer[5]) << 8U); return; }
상태 열거형을 사용하고 있으므로 bme280.c 파일에 bme280.h 파일을 포함해야 합니다. 우리는 이미 라이브러리를 초기화했습니다. 이제 온도를 검색하는 함수를 만들어 보겠습니다. 다음과 같습니다:
typedef enum { BME280_Status_Ok, BME280_Status_Status_Err, } BME280_Status_t;
눈치채셨죠? 컴포넌트에 통신 문제가 있었는지 여부를 나타내는 상태를 반환하고, 그 결과는 함수에 매개변수로 전달된 포인터를 통해 반환되도록 함수 시그니처를 수정했습니다. 예제를 따르는 경우 bme280.h 파일의 함수 선언을 일치하도록 수정해야 합니다.
BME280_Status_t BME280_init(void);
좋아요! 이 시점에서 애플리케이션에서는 다음을 수행할 수 있습니다.
#include "i2c.h" #include <stdint.h> int main(void) { uint8_t idx = 0U; uint8_t tx_buffer[64] = {0}; uint8_t rx_buffer[64] = {0}; uint16_t dig_temp1 = 0U; int16_t dig_temp2 = 0; int16_t dig_temp3 = 0; MX_I2C1_Init(); tx_buffer[idx++] = 0b10100011; HAL_I2C_Mem_Write(&hi2c1, 0x77U << 1U, 0xF4U, 1U, tx_buffer, 1U, 200U); HAL_I2C_Mem_Read(&hi2c1, 0x77U << 1U, 0x88U, 1U, rx_buffer, 6U, 200U); dig_temp1 = ((uint16_t)rx_buffer[0]) | (((uint16_t)rx_buffer[1]) << 8U); dig_temp2 = (int16_t)(((uint16_t)rx_buffer[2]) | (((uint16_t)rx_buffer[3]) << 8U)); dig_temp3 = (int16_t)(((uint16_t)rx_buffer[4]) | (((uint16_t)rx_buffer[5]) << 8U)); while (1) { float temperature = 0.0f; int32_t adc_temp = 0; int32_t t_fine = 0; float var1 = 0.0f; float var2 = 0.0f; HAL_I2C_Mem_Read(&hi2c1, 0x77U << 1U, 0xFAU, 1U, rx_buffer, 3U, 200U); adc_temp = (int32_t)((((uint32_t)rx_buffer[0]) << 12U) | (((uint32_t)rx_buffer[1]) << 4U) | (((uint32_t)rx_buffer[2]) >> 4U)); var1 = (((float)adc_temp) / 16384.0f - ((float)dig_temp1) / 1024.0f) * ((float)dig_temp2); var2 = ((((float)adc_temp) / 131072.0f - ((float)dig_temp1) / 8192.0f) * (((float)adc_temp) / 131072.0f - ((float)dig_temp1) / 8192.0f)) * ((float)dig_temp3); t_fine = (int32_t)(var1 + var2); temperature = ((float)t_fine) / 5129.0f; // Temperature available for the application. } }
정말 깨끗해요! 이것은 읽을 수 있습니다. STM32CubeMX/IDE의 Error_Handler 함수 사용을 무시하십시오. 일반적으로 사용하는 것은 권장되지 않지만 예를 들어 우리에게는 효과적입니다. 그럼 끝났나요?
아니요! 우리는 구성 요소와의 상호 작용을 자체 파일에 캡슐화했습니다. 하지만 해당 코드는 여전히 대상 함수(HAL 함수)를 호출하고 있습니다. 타겟을 변경하면 라이브러리를 다시 작성해야 합니다! 힌트: 아직 bme280_interface.h 파일에는 아무것도 작성하지 않았습니다. 지금부터 해결해 보겠습니다.
인터페이스 선언
bme280.c 파일을 보면 대상과의 상호 작용은 주변 장치 초기화, 바이트 쓰기/보내기, 바이트 읽기/받기 등 세 가지로 이루어집니다. 따라서 우리가 할 일은 bme280_interface.h 파일에서 이 세 가지 상호 작용을 선언하는 것입니다.
#ifndef BME280_H_ #define BME280_H_ void BME280_init(void); float BME280_get_temperature(void); #endif // BME280_H_
알다시피, 인터페이스 상태에 대한 새로운 유형도 정의했습니다. 이제 대상 함수를 직접 호출하는 대신 bme280.c 파일에서 이러한 함수를 호출하겠습니다.
void BME280_init(void) { } float BME280_get_temperature(void) { }
Et voilà! 대상 종속성이 라이브러리에서 사라졌습니다. 이제 STM32, MSP430, PIC32 등에서 작동하는 라이브러리가 있습니다. 세 가지 라이브러리 파일에는 대상과 관련된 어떤 것도 나타나지 않아야 합니다. 남은 것은 무엇입니까? 음, 인터페이스 기능을 정의합니다. 각 타겟에 맞게 마이그레이션/적응해야 하는 유일한 부분입니다.
저는 주로 Application/bsp/comComponents/폴더 안에서 합니다.
다음 내용으로 bme280_implementation.c라는 파일을 생성합니다.
void BME280_init(void) { MX_I2C1_Init(); }
이 방법으로 다른 프로젝트나 다른 대상에서 라이브러리를 사용하려면 bme280_implementation.c 파일만 조정하면 됩니다. 나머지는 그대로 유지됩니다.
고려해야 할 다른 측면
이제 우리는 라이브러리의 기본 예를 살펴보았습니다. 이 구현은 가장 간단하고 안전하며 가장 일반적입니다. 그러나 우리 프로젝트의 특성에 따라 다양한 변형이 있습니다. 이 예에서는 링크 타임에 구현 선택을 수행하는 방법을 살펴보았습니다. 즉, 컴파일/링크 프로세스 중에 인터페이스 기능의 정의를 제공하는 bme280_implementation.c 파일이 있습니다. 두 가지 구현을 원한다면 어떻게 될까요? 하나는 I2C 통신용이고 다른 하나는 SPI 통신용입니다. 이 경우 함수 포인터를 사용하여 런타임에 구현을 지정해야 합니다.
또 다른 측면은 이 예에서는 시스템에 BME280이 하나만 있다고 가정한다는 것입니다. 둘 이상이면 어떻게 될까요? 코드를 복사/붙여넣고 BME280_1 및 BME280_2와 같은 함수에 접두사를 추가해야 합니까? 아니요. 그건 이상적이지 않습니다. 우리가 할 일은 핸들러를 사용하여 구성 요소의 다른 인스턴스에서 동일한 라이브러리로 작업할 수 있도록 하는 것입니다.
하드웨어를 사용하기 전에 이러한 측면과 라이브러리를 테스트하는 방법은 다른 게시물의 주제이며 향후 기사에서 다룰 것입니다. 현재로서는 라이브러리를 제대로 구현하지 않을 변명의 여지가 없습니다. 그러나 나의 첫 번째 권장 사항(그리고 역설적이게도 끝까지 남겨둔 권장 사항)은 무엇보다도 제조업체가 해당 구성 요소에 대한 공식 라이브러리를 이미 제공하지 않는지 확인하라는 것입니다. 이것이 라이브러리를 시작하고 실행하는 가장 빠른 방법입니다. 제조업체가 제공하는 라이브러리는 오늘날 우리가 본 것과 유사한 구현을 따를 가능성이 높으며, 우리의 임무는 인터페이스 구현 부분을 목표나 제품에 맞게 조정하는 것입니다.
이 주제에 관심이 있으시면 제 블로그에서 이 게시물과 임베디드 시스템 개발과 관련된 다른 게시물을 찾아보실 수 있습니다! ?
위 내용은 재사용 가능한 구성요소 라이브러리: 대상 간 마이그레이션 단순화의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

C#과 C의 역사와 진화는 독특하며 미래의 전망도 다릅니다. 1.C는 1983 년 Bjarnestroustrup에 의해 발명되어 객체 지향 프로그래밍을 C 언어에 소개했습니다. Evolution 프로세스에는 자동 키워드 소개 및 Lambda Expressions 소개 C 11, C 20 도입 개념 및 코 루틴과 같은 여러 표준화가 포함되며 향후 성능 및 시스템 수준 프로그래밍에 중점을 둘 것입니다. 2.C#은 2000 년 Microsoft에 의해 출시되었으며 C와 Java의 장점을 결합하여 진화는 단순성과 생산성에 중점을 둡니다. 예를 들어, C#2.0은 제네릭과 C#5.0 도입 된 비동기 프로그래밍을 소개했으며, 이는 향후 개발자의 생산성 및 클라우드 컴퓨팅에 중점을 둘 것입니다.

C는 시스템 프로그래밍 및 하드웨어 상호 작용에 적합합니다. 하드웨어에 가까운 제어 기능 및 객체 지향 프로그래밍의 강력한 기능을 제공하기 때문입니다. 1) C는 포인터, 메모리 관리 및 비트 운영과 같은 저수준 기능을 통해 효율적인 시스템 수준 작동을 달성 할 수 있습니다. 2) 하드웨어 상호 작용은 장치 드라이버를 통해 구현되며 C는 이러한 드라이버를 작성하여 하드웨어 장치와의 통신을 처리 할 수 있습니다.

C 및 XML의 미래 개발 동향은 다음과 같습니다. 1) C는 프로그래밍 효율성 및 보안을 개선하기 위해 C 20 및 C 23 표준을 통해 모듈, 개념 및 코 루틴과 같은 새로운 기능을 소개합니다. 2) XML은 데이터 교환 및 구성 파일에서 중요한 위치를 계속 차지하지만 JSON 및 YAML의 문제에 직면하게 될 것이며 XMLSCHEMA1.1 및 XPATH 3.1의 개선과 같이보다 간결하고 쉽게 구문 분석하는 방향으로 발전 할 것입니다.

C 지속적인 사용 이유에는 고성능, 광범위한 응용 및 진화 특성이 포함됩니다. 1) 고효율 성능 : C는 메모리 및 하드웨어를 직접 조작하여 시스템 프로그래밍 및 고성능 컴퓨팅에서 훌륭하게 수행합니다. 2) 널리 사용 : 게임 개발, 임베디드 시스템 등의 분야에서의 빛나기.

C 멀티 스레딩 및 동시 프로그래밍의 핵심 개념에는 스레드 생성 및 관리, 동기화 및 상호 제외, 조건부 변수, 스레드 풀링, 비동기 프로그래밍, 일반적인 오류 및 디버깅 기술, 성능 최적화 및 모범 사례가 포함됩니다. 1) std :: 스레드 클래스를 사용하여 스레드를 만듭니다. 예제는 스레드가 완성 될 때까지 생성하고 기다리는 방법을 보여줍니다. 2) std :: mutex 및 std :: lock_guard를 사용하여 공유 리소스를 보호하고 데이터 경쟁을 피하기 위해 동기화 및 상호 배제. 3) 조건 변수는 std :: 조건 _variable을 통한 스레드 간의 통신과 동기화를 실현합니다. 4) 스레드 풀 예제는 ThreadPool 클래스를 사용하여 효율성을 향상시키기 위해 작업을 병렬로 처리하는 방법을 보여줍니다. 5) 비동기 프로그래밍은 std :: as를 사용합니다

C는 XML과 타사 라이브러리 (예 : TinyXML, Pugixml, Xerces-C)와 상호 작용합니다. 1) 라이브러리를 사용하여 XML 파일을 구문 분석하고 C- 처리 가능한 데이터 구조로 변환하십시오. 2) XML을 생성 할 때 C 데이터 구조를 XML 형식으로 변환하십시오. 3) 실제 애플리케이션에서 XML은 종종 구성 파일 및 데이터 교환에 사용되어 개발 효율성을 향상시킵니다.

C의 메모리 관리, 포인터 및 템플릿은 핵심 기능입니다. 1. 메모리 관리는 새롭고 삭제를 통해 메모리를 수동으로 할당하고 릴리스하며 힙과 스택의 차이에주의를 기울입니다. 2. 포인터는 메모리 주소를 직접 작동시키고주의해서 사용할 수 있습니다. 스마트 포인터는 관리를 단순화 할 수 있습니다. 3. 템플릿은 일반적인 프로그래밍을 구현하고 코드 재사용 성과 유연성을 향상 시키며 유형 파생 및 전문화를 이해해야합니다.

C 학습자와 개발자는 StackoverFlow, Reddit의 R/CPP 커뮤니티, Coursera 및 EDX 코스, GitHub의 오픈 소스 프로젝트, 전문 컨설팅 서비스 및 CPPCon에서 리소스와 지원을받을 수 있습니다. 1. StackoverFlow는 기술적 인 질문에 대한 답변을 제공합니다. 2. Reddit의 R/CPP 커뮤니티는 최신 뉴스를 공유합니다. 3. Coursera와 Edx는 공식적인 C 과정을 제공합니다. 4. LLVM 및 부스트 기술 향상과 같은 GitHub의 오픈 소스 프로젝트; 5. JetBrains 및 Perforce와 같은 전문 컨설팅 서비스는 기술 지원을 제공합니다. 6. CPPCON 및 기타 회의는 경력을 돕습니다
