본문 바로가기

프로그래밍 언어[문제]/C

[C 문제] 연습 문제 1

문제1) 아래의 함수를 개선하시오.

void DisplayInfo_set(U8 TestNum)
{
	switch(TestNum) {
		case eTCh :
			Put_Tx_U8('C');
			Put_Tx_U8('H');
			Put_Tx_U8(' ');
			Put_Tx_U8(' ');
			break;
		case eTDisplaySWVer :
			Put_Tx_U8('D');
			Put_Tx_U8('I');
			Put_Tx_U8('S');
			Put_Tx_U8('W');
			break;
		case eTDisplayHWVer :
			Put_Tx_U8('D');
			Put_Tx_U8('I');
			Put_Tx_U8('H');
			Put_Tx_U8('W');
			break;
		case eTInputFWVer :
			Put_Tx_U8('I');
			Put_Tx_U8('N');
			Put_Tx_U8('F');
			Put_Tx_U8('W');
			break;
		default :
			break;
	}
}

 

문제2) 아래의 함수를 개선하시오.

void tsn_resp(void)
{
	U16 ch;
	U8  i, testNum, INtype, IsCali;

	testNum = *pCOMtbuf;
	pFTest.uData.pU16 = NULL;
	pFTest.TestNum = 100;
	INtype = 0;
	IsCali = 0;

	switch(testNum) {
		case eTCh :
			pFTest.uData.pU8 = &pFTest.Ch;
			break;

		case eTSpec :
			pFTest.uData.pS16 = &buf_SYSSPEC;
			MMI_no = MMI_CAL_SET1;
			break;

		case eTDisplaySWVer 	:
		case eTDisplayHWVer 	:
			pFTest.uData.pU8 = &para_DISPLAY_VER[testNum & 0x01][0];
			MMI_no = MMI_CAL_MENU;
			break;

		case eTInputFWVer 	:
		case eTInputHWVer 	:
			pFTest.uData.pU8 = &para_INPUT_VER[testNum & 0x01][0];
			MMI_no = MMI_CAL_MENU;
			break;

		case eTI_OFWVer 		:
		case eTI_OHWVer 		:
			pFTest.uData.pU8 = &para_RELAY_VER[testNum & 0x01][0];
			MMI_no = MMI_CAL_MENU;
			break;

		case eTCOMChk :
			pFTest.uData.pU8 = &flag_COMM_ERROR[0];
			MMI_no = MMI_CAL_SET2_2;
			break;

		case eTFRAMChk :
			flag_CAL_FRAM_TST = FM24CL64B_Test();
			pFTest.uData.pU8 = &flag_CAL_FRAM_TST;
			MMI_no = MMI_CAL_SET2_2;
			break;

		case eTSDChk :
			flag_CAL_SD_TST = SDCard_Test();
			pFTest.uData.pU8 = &flag_CAL_SD_TST;
			MMI_no = MMI_CAL_SET2_2;
			break;

		case eTUSBChk :
			flag_CAL_USB_TST = USB_Test();
			pFTest.uData.pU8 = &flag_CAL_USB_TST;
			MMI_no = MMI_CAL_SET2_2;
			break;

		case eTDIChk :
			pFTest.uData.pU8 = &DI_status;
			MMI_no = MMI_CAL_SET2_4;
			break;

		case eTDOChk :
			pFTest.uData.pS16 = &DO_STAT;
			DO_AutoMode = 0;
			flag_RLY = 0;
			MMI_no = MMI_CAL_SET2_5;
			break;

		case eTRJCInputChk :
			pFTest.uData.pS16 = &NRJC[0];
			for(i=0;i<12;i++) {
				para_INTYPE[i]=7;
				para_UNIT[i]=0;
				IN_Init(i);
			}
			MMI_no = MMI_CAL_SET4_1;
			break;

		case eTCali30mvZero :
		case eTCali30mvSpan :
			IsCali = 1;
			INtype = 11;
			CAL_no = ZERO_TC50;
			MMI_no = MMI_CAL_SET3_1;
			break;

		case eTCali150mvZero :
		case eTCali150mvSpan :
			IsCali = 1;
			INtype = 7;
			CAL_no = ZERO_TC10;
			MMI_no = MMI_CAL_SET3_2;
			break;

		case eTCali17_5vZero :
		case eTCali17_5vSpan :
			IsCali = 1;
			INtype = 21;
			CAL_no = ZERO_DCV10;
			MMI_no = MMI_CAL_SET3_3;
			break;

		case eTCali130vZero :
		case eTCali130vSpan :
			IsCali = 1;
			INtype = 22;
			CAL_no = ZERO_DCV1;
			MMI_no = MMI_CAL_SET3_4;
			break;

		case eTCali150ohmZero :
		case eTCali150ohmSpan :
			IsCali = 1;
			INtype = 2;
			CAL_no = ZERO_RTD50;
			MMI_no = MMI_CAL_SET3_5;
			break;

		case eTCali330ohmZero :
		case eTCali330ohmSpan :
			IsCali = 1;
			INtype = 1;
			CAL_no = ZERO_RTD10;
			MMI_no = MMI_CAL_SET3_6;
			break;

		case eTCalRJCBias :
			pFTest.uData.pS16 = &RJC_bias_input[0];
			INtype = 7;
			MMI_no = MMI_CAL_SET4_5;
			break;

		case eTPARAInit :
		case eTINTMEMInit :
		case eTRTCInit :
		case eTCaliSave :
			MMI_no = MMI_CAL_SET5;
			break;

		case eTRTDPT1Chk :
			pFTest.uData.pS16 = &NPV[0];
			flag_cal_ch_sel = 0;
			INtype = 1;
			MMI_no = MMI_CAL_SET4_2;
			break;

		case eTRTDPT2Chk :
			pFTest.uData.pS16 = &NPV[0];
			flag_cal_ch_sel = 1;
			INtype = 2;
			MMI_no = MMI_CAL_SET4_2;
			break;

		case eTVDC5VChk :
			pFTest.uData.pS16 = &NPV[0];
			flag_cal_ch_sel = 0;
			INtype = 21;
			MMI_no = MMI_CAL_SET4_3;
			break;

		case eTVDC30VChk :
			pFTest.uData.pS16 = &NPV[0];
			flag_cal_ch_sel = 1;
			INtype = 22;
			MMI_no = MMI_CAL_SET4_3;
			break;

		case eTTCK1Chk :
			pFTest.uData.pS16 = &NTC[0];
			flag_cal_ch_sel = 0;
			INtype = 7;
			MMI_no = MMI_CAL_SET4_4;
			break;

		case eTTCRChk :
			pFTest.uData.pS16 = &NTC[0];
			flag_cal_ch_sel = 1;
			INtype = 11;
			MMI_no = MMI_CAL_SET4_4;
			break;

		default :
			cmd_err1 = ECODE_OTHER;
			break;
	}

	if(!cmd_err1)	pFTest.TestNum = testNum;
	else {
		if(IsCali) {
			if(testNum & 0x01)	pFTest.uData.pU16 = &Cal_span[0];
			else					pFTest.uData.pU16 = &Cal_zero[0];
		}

		if(INtype) {
			for(i=0;i<12;i++) {
				para_INTYPE[i] = INtype;
				if(testNum == eTVDC5VChk || testNum == eTVDC30VChk)	IN_Init_special(i);
				else {
					para_UNIT[i] = 0;
					IN_Init(i);
				}
			}
		}
	}
}

정답1) 

typedef struct stDisplay_Map_tag {
	U8 TestNum;
	const U8* displayStr;
}stDisplay_Map;

static const stDisplay_Map aDisplay_table[] = {
	{eTCh, 				"CH"},
	{eTDisplaySWVer, 		"DISW"},
	{eTDisplayHWVer, 		"DIHW"},
	{eTInputFWVer, 		"INFW"}
};

void DisplayInfo_set(U8 TestNum)
{
	U8 i, tableSize;
	const U8* str;

	tableSize = sizeof(aDisplay_table) / sizeof(stDisplay_Map[0]);

	for(i=0; i<tableSize; i++) {
		if(aDisplay_table[i].TestNum == TestNum) {
			str = aDisplay_table[i].displayStr;
			while(*str)	Put_Tx_U8(*str++);
			break;
		}
	}
}

 

정답2)

static const TestCaseInfo_t testTable[] = {
    { eTCh, &pFTest.TestNum, DATA_U8, 0, 0, 0, 0 },
    { eTSpec, &buf_SYSSPEC, DATA_S16, 0, MMI_CAL_SET1, 0, 0 },
    { eTDisplaySWVer, &para_DISPLAY_VER[0][0], DATA_U8, 0, MMI_CAL_MENU, 0, 0 },
    { eTDisplayHWVer, &para_DISPLAY_VER[1][0], DATA_U8, 0, MMI_CAL_MENU, 0, 0 },
    { eTInputFWVer, &para_INPUT_VER[0][0], DATA_U8, 0, MMI_CAL_MENU, 0, 0 },
    { eTInputHWVer, &para_INPUT_VER[1][0], DATA_U8, 0, MMI_CAL_MENU, 0, 0 },
    { eTI_OFWVer, &para_RELAY_VER[0][0], DATA_U8, 0, MMI_CAL_MENU, 0, 0 },
    { eTI_OHWVer, &para_RELAY_VER[1][0], DATA_U8, 0, MMI_CAL_MENU, 0, 0 },
    { eTCOMChk, &flag_COMM_ERROR[0], DATA_U8, 0, MMI_CAL_SET2_2, 0, 0 },
    { eTFRAMChk, NULL, DATA_U8, 0, MMI_CAL_SET2_2, 0, 0 },
    { eTSDChk, NULL, DATA_U8, 0, MMI_CAL_SET2_2, 0, 0 },
    { eTUSBChk, NULL, DATA_U8, 0, MMI_CAL_SET2_2, 0, 0 },
    { eTDIChk, &DI_status, DATA_U8, 0, MMI_CAL_SET2_4, 0, 0 },
    { eTDOChk, &DO_STAT, DATA_S16, 0, MMI_CAL_SET2_5, 0, 0 },
    { eTRJCInputChk, &NRJC[0], DATA_S16, 7, MMI_CAL_SET4_1, 0, 0 },
    { eTCalRJCBias, &RJC_bias_input[0], DATA_S16, 7, MMI_CAL_SET4_5, 0, 0 },
    { eTRTDPT1Chk, &NPV[0], DATA_S16, 1, MMI_CAL_SET4_2, 0, 0 },
    { eTRTDPT2Chk, &NPV[0], DATA_S16, 2, MMI_CAL_SET4_2, 0, 1 },
    { eTVDC5VChk, &NPV[0], DATA_S16, 21, MMI_CAL_SET4_3, 0, 0 },
    { eTVDC30VChk, &NPV[0], DATA_S16, 22, MMI_CAL_SET4_3, 0, 1 },
    { eTTCK1Chk, &NTC[0], DATA_S16, 7, MMI_CAL_SET4_4, 0, 0 },
    { eTTCRChk, &NTC[0], DATA_S16, 11, MMI_CAL_SET4_4, 0, 1 },
    // Calibration cases can be handled separately
};

void tsn_resp(void) {
    uint8_t testNum = *pCOMtbuf;
    pFTest.uData.pU16 = NULL;
    pFTest.TestNum = 100;
    cmd_err1 = 0;

    for (int i = 0; i < sizeof(testTable) / sizeof(testTable[0]); i++) {
        if (testTable[i].testNum == testNum) {
            switch (testTable[i].dataType) {
                case DATA_U8:  pFTest.uData.pU8 = (uint8_t *)testTable[i].dataPtr; break;
                case DATA_S16: pFTest.uData.pS16 = (int16_t *)testTable[i].dataPtr; break;
                case DATA_U16: pFTest.uData.pU16 = (uint16_t *)testTable[i].dataPtr; break;
            }
            MMI_no = testTable[i].mmiNo;
            flag_cal_ch_sel = testTable[i].chSel;

            if (testNum == eTFRAMChk) flag_CAL_FRAM_TST = FM24CL64B_Test();
            else if (testNum == eTSDChk) flag_CAL_SD_TST = SDCard_Test();
            else if (testNum == eTUSBChk) flag_CAL_USB_TST = USB_Test();

            if (testTable[i].inType) {
                for (int ch = 0; ch < 12; ch++) {
                    para_INTYPE[ch] = testTable[i].inType;
                    if (testNum == eTVDC5VChk || testNum == eTVDC30VChk) IN_Init_special(ch);
                    else {
                        para_UNIT[ch] = 0;
                        IN_Init(ch);
                    }
                }
            }

            pFTest.TestNum = testNum;
            return;
        }
    }

    // Calibration special case 처리 생략 (원하면 추가 가능)
    cmd_err1 = ECODE_OTHER;
}

 


질문1) aDisplay_table을 DisplayInfo_set 함수 내부에  선언하면 메모리 공간을 더 아낄 수 있는데 그렇게 하지 않은 이유(2가지)는 무엇인가?

 

 

 


답변1) 

aDisplay_table을 DisplayInfo_set 함수 내부에 선언하면 함수가 호출될 때마다 테이블이 스택 메모리(RAM)에 생성되고, 함수가 끝나면 소멸된다. 이 방식은 메모리를 아끼는 것처럼 보일 수 있지만, 스택은 크기가 제한적이기 때문에 아래와 같은 2가지 문제가 발생할 수 있다.

1. 스택 오버플로우

aDisplay_table처럼 크기가 큰 상수 테이블을 함수 내부에 선언하면, 여러 함수가 동시에 호출될 때 스택 메모리 초과로 프로그램이 오작동할 수 있다.

2. 불필요한 반복 생성

테이블은 고정된 상수이기 때문에 함수가 호출될 때마다 반복해서 생성할 필요가 없다. 매번 생성하면 오히려 효율이 떨어진다.

따라서 aDisplay_table을 함수 바깥에 정적(static) 또는 상수(const) 전역 변수로 선언하면, ROM 영역에 한 번만 할당되고 스택 공간을 절약할 수 있다. 이런 방식이 임베디드 시스템에서 메모리 최적화에 더 적합하다.


<추가 자료>

https://powerdeng.tistory.com/263

 

[C 개념] 메모리 구조

 

powerdeng.tistory.com

https://powerdeng.tistory.com/264

 

[C 개념] ROM 종류와 특징

종류설명재작성가능 여부사용 예시Mask ROM제조 시 내용이 고정되어 변경 불가.불가능대량생산된 게임 카트리지PROM(Programmable ROM)한 번만 프로그래밍 가능1회 가능과거의 하드웨어 설정 값 저장EPR

powerdeng.tistory.com