반응형

 

 

안녕하세요. 오늘은 UNION과 STRUCT에 대해 이야기 하겠습니다. 

 

coding-yoon.tistory.com/119

 

[C] 구조체 포인터 접근과 최적화

요즘 기본기가 중요함을 느끼고 C를 공부하고 있습니다. 1. 구조체 단순히접근 #include //Struct packet 선언 typedef struct packet { int data0; int data1; int data2; }Packet; int main() { Packet p; //Str..

coding-yoon.tistory.com

구조체에 대한 이야기는 위에 작성했지만, 저보다 훨씬 자세히 적은 블로그 분들이 많기 때문에 따로 찾아보시길 바랍니다.

 

UNION의 구조를 먼저 알아보겠습니다. 

 

UNION은 가장 사이즈가 큰 변수와 메모리를 공유합니다.

 

이게 무슨 말인가 하면

#include <stdio.h>

// 공용체 선언
typedef union data
{
	char a; 
	char b; 
	char c[2]; 
}Data;

int main(){

	Data data; // 공용체 data 정의

	data.c[0] = 'a';
	data.c[1] = 'b';

	printf("data.a = %c\n", data.a);
	printf("data.b = %c\n", data.b);
	printf("data.c[0] = %c\n", data.c[0]);
	printf("data.c[1] = %c", data.c[1]);
	

	return 0;
}


 

이런 간단한 코드에서  결과는 어떻게 나올까요?

 

왜 이런 결과가 나오느냐. 그림을 보시면 바로 이해할 수 있습니다.

 

이런 구조로 메모리를 서로 공유하고 있습니다. 주소를 찍어보면 더 확실하게 알 수 있습니다. 

 

#include <stdio.h>


typedef union data
{
	char a; 
	char b; 
	char c[2]; 
}Data;

int main(){

	Data data; 

	data.c[0] = 'a';
	data.c[1] = 'b';

	printf("data.a = %p\n", &data.a);  // a 주소 출력
	printf("data.b = %p\n", &data.b);  // b 주소 출력
	printf("data.c[0] = %p\n", &data.c[0]);  // c[0] 주소 출력
	printf("data.c[1] = %p", &data.c[1]);  // c[1] 주소 출력
	

	return 0;
}


출력

Union의 사이즈는 가장 사이즈가 큰 변수이며, 메모리를 공유합니다. 

 

Union은 어디에 많이 쓰일까요? 대표적으로 통신 분야로 가장 많이 사용됩니다. 

 

통신에서는 데이터를 전송을 잘하는 것도 중요하지만, 데이터를 수신해서 제대로 분해하는 것도 굉장히 중요합니다. 

 

꼭 이렇게 사용한다는 아니지만, 구조체와 적절히 사용하면 송신부분 수신부분을 만들 수 있습니다.  

 

#include <stdio.h>
#include <stdint.h>


// Struct Packet_s 선언
typedef struct packet_s
{
	uint8_t data0;
	uint8_t data1;
	uint8_t data2;
	uint8_t data3;

}Packet_s;

typedef union packet_u
{
	Packet_s ps;  // Struct Packet_s ps 정의 // 송신
	uint8_t pu[4];  // 수신
}Packet_u;



int main()
{
	Packet_u packet_data;

	packet_data.ps.data0 = 0;  // 예를 들어 uuid
	packet_data.ps.data1 = 1;  // minor
	packet_data.ps.data2 = 2;  // tx power
	packet_data.ps.data3 = 3;  // rssi

	printf("pacekt_data size = %d bytes\n\n", sizeof(packet_data));

	printf("송신 사이즈 : %d bytes\n\n", sizeof(packet_data.ps));

	
	printf("------송신 준비 완료------\n");
	printf("--------------------------\n");
	printf("--------------------------\n");
	printf("-------송수신 완료--------\n\n");

	printf("수신 사이즈 : %d bytes\n", sizeof(packet_data.pu));
	printf("uuid = %d\n", packet_data.pu[0]);
	printf("minor = %d\n", packet_data.pu[1]);
	printf("tx power = %d\n", packet_data.pu[2]);
	printf("rssi = %d\n", packet_data.pu[3]);


	

	return 0;
}

 

 

이런식으로 재밌게 구조체와 공용체를 관계를 적절히 사용하여 통신 패킷을 만들어 보았습니다.

 

그 외에도 Union은 다양한 방법으로도 유용하게 사용되고 있습니다. 

728x90
반응형
반응형

요즘 기본기가 중요함을 느끼고 C를 공부하고 있습니다. 

 

 

1. 구조체 단순히접근

#include <stdio.h>

//Struct packet 선언
typedef struct packet
{
	int data0;
	int data1;
	int data2;

}Packet;

int main()
{
	Packet p;  //Struct Packet 정의

	p.data0 = 0;
	p.data1 = 1;
	p.data2 = 2;

	printf("%d %d %d ", p.data0, p.data1, p.data2);
	return 0;
}

 

 

 

 

 

2. 구조체 포인터로 접근

#include <stdio.h>


// Struct packet 선언
typedef struct packet
{
	int data0;
	int data1;
	int data2;

}Packet;



int main()
{
	Packet p;  // Struct Packet 정의
	Packet* p_pointer;  // Packet에 대한 Pointer 선언 
	p_pointer = &p;  // Pointer 정의 

	p_pointer->data0 = 0;
	p_pointer->data1 = 1;
	p_pointer->data2 = 2;

	printf("%d %d %d ", p.data0, p.data1, p.data2);
	return 0;
}

 

 

 

 

 

3. Struct 최적화 (Padding bit)

#include <stdio.h>


// Struct packet 선언
typedef struct packet
{
	char data0;
	char data1;
	int data2;

}Packet;



int main()
{
	Packet p;  // Struct Packet 정의

	printf("%d", sizeof(p));

	return 0;
}

 

과연 위 구제체는 몇 비트인가?

 

6바이트라고 생각할 수 있지만, 8바이트

 

이유 : 구조체는 가장 큰 변수만큼 증가하기 때문이다. 

 

그렇다면 둘의 순서가 바뀌면 어떻게 되는가?

 

#include <stdio.h>


// Struct packet 선언
typedef struct packet
{
	int data0;
	char data1;
	char data2;
	

}Packet;



int main()
{
	Packet p;  // Struct Packet 정의

	printf("%d", sizeof(p));

	return 0;
}

 

똑같이 8바이트이다.

 

그렇다면 이런 구조체일 때는 몇 바이트인가?

 

#include <stdio.h>


// Struct packet 선언
typedef struct packet
{
	char data0;
	int data1;
	char data2;
	

}Packet;



int main()
{
	Packet p;  // Struct Packet 정의

	printf("%d", sizeof(p));

	return 0;
}

위 구조체는 12bytes이다.

 

극한의 메모리 낭비를 일으킨다. 

 

BLE 통신을 하게 되었는데, 통신에서는 이런 구조체와 공용체가 굉장히 중요하다. 

728x90
반응형
반응형

안녕하세요. 오랜만에 C언어에 대해 글을 써봅니다.

 

여러분들 BMP를 불러올 때 어떻게 하시나요? 

 

https://dojang.io/mod/page/view.php?id=703

 

C 언어 코딩 도장: 81.2 비트맵 구조체 작성하기

비트맵 파일의 구조를 알아보았으니 이제 비트맵 구조체를 작성합니다. 이때 주의할 점은 반드시 구조체를 1바이트 크기로 정렬해야 한다는 점입니다. 즉, 비트맵 파일에서 각 정보는 위치와 크기가 정확하게 정해져 있으므로 반드시 구조체의 크기와 형태 그대로 읽어야 합니다. #pragma pack(push, 1) // 구조체를 1바이트 크기로 정렬 typedef struct _BITMAPFILEHEADER // BMP 비트맵 파일 헤더 구조체 { unsigned

dojang.io

아마 위 본 예제가 가장 많이들 사용하실겁니다. 

 



#pragma pack(push, 1)                // 구조체를 1바이트 크기로 정렬

typedef struct _BITMAPFILEHEADER     // BMP 비트맵 파일 헤더 구조체
{
    unsigned short bfType;           // BMP 파일 매직 넘버
    unsigned int   bfSize;           // 파일 크기
    unsigned short bfReserved1;      // 예약
    unsigned short bfReserved2;      // 예약
    unsigned int   bfOffBits;        // 비트맵 데이터의 시작 위치
} BITMAPFILEHEADER;

typedef struct _BITMAPINFOHEADER     // BMP 비트맵 정보 헤더 구조체(DIB 헤더)
{
    unsigned int   biSize;           // 현재 구조체의 크기
    int            biWidth;          // 비트맵 이미지의 가로 크기
    int            biHeight;         // 비트맵 이미지의 세로 크기
    unsigned short biPlanes;         // 사용하는 색상판의 수
    unsigned short biBitCount;       // 픽셀 하나를 표현하는 비트 수
    unsigned int   biCompression;    // 압축 방식
    unsigned int   biSizeImage;      // 비트맵 이미지의 픽셀 데이터 크기
    int            biXPelsPerMeter;  // 그림의 가로 해상도(미터당 픽셀)
    int            biYPelsPerMeter;  // 그림의 세로 해상도(미터당 픽셀)
    unsigned int   biClrUsed;        // 색상 테이블에서 실제 사용되는 색상 수
    unsigned int   biClrImportant;   // 비트맵을 표현하기 위해 필요한 색상 인덱스 수
} BITMAPINFOHEADER;

typedef struct _RGBTRIPLE            // 24비트 비트맵 이미지의 픽셀 구조체
{
    unsigned char rgbtBlue;          // 파랑
    unsigned char rgbtGreen;         // 초록
    unsigned char rgbtRed;           // 빨강
} RGBTRIPLE;

#pragma pack(pop)

#pragma pack(push, 1)

 

구조체 데이터를 강제로 1바이트 단위로 쪼개서 정렬한다는 뜻입니다. 

가장 최소로 쪼개면 빈공간이 없이 정렬이 되기 때문에 메모리가 100퍼센터 사용할 수 있게 됩니다.

 

하지만 1바이트 씩 정렬하면 너무 낭비가 되지 않을까요? 

cpu가 한 번에 32bit, 64bit를 한 번에 수행하는 반면에 1바이트씩이면은... 분명 낭비가 됩니다. 그래서 많은 블로그에 

글쓰신 분들께서도  추천하지는 않습니다. 

 

오늘은 pack#pragma pack(push, 1)를 쓰지 않고 정렬하여 사용하는 방법을 알아보도록 하겠습니다.

 

 

1. BMP 바이너리 모드

BMP에 대해 알아보겠습니다. bmp를 binary mode로 열어봅시다.

 

1. visual studio

visual studio를 열어줍니다.

2. 파일 열기
3. binary mode
BMP Binary mode

파일을 열고나서 BMP인지 아닌지를 판별하는 데이터가 시작주소에서 2바이트가 존재합니다.

0x424D(16진수) => BM을 의미합니다. (bfType)

BM으로 BMP파일인지 아닌지 확인하게 됩니다. (BM이면 BMP파일) 

 

 

 

 

 

그럼 BMP파일인지 아닌지 확인해보았고.

BMP파일 구조체에 대해 알아보도록 하겠습니다.

 

1. pragma pack(push, 1)

 

2. 정렬 없음.

첫번 째 사진인 pragma pack(push,1)가 있고,

 

두번 째 사진은 pragma pack(push,1)이 없습니다.

 

첫번 째 경우는 당연히 가장 최소 단위로 쪼개기 때문에 바로 사용가능합니다.

 

하지만 두번 째 경우는 어떻게 될까요?

 

두번 째 사진을 디버깅한 결과

포인터 오류가 뜨네요. 널포인터... 아주 무서운 녀석입니다.

 

왜 이런 결과가 나올까요? 그 이유는 포인터정렬이 제대로 되지 않아 NULL 포인터가 생겼기 때문입니다.

 

구조체는 #pragma가 정의가 되어 있지 않으면 가장 큰 변수의 사이즈를 기준으로 정렬하게 됩니다.

 

2. 정렬 없음.

다시 한 번 볼까요? 가장 큰 바이트가 4바이트입니다.  그렇다면 4바이트 기준으로 정렬을 하겠네요. 

그림보시면 단박에 이해가 가실겁니다.

그림 죄송합니다 ㅜㅜ

4바이트 씩 맞춰 정렬하게 되면 이와 같이 NULL 포인터가 2바이트 발생하게 되어 저 위와 같은 오류가 뜨게 됩니다.

 

어떻게 하면 NULL 포인터를 없애면서 4바이트씩 정렬할 수 있을까요?

 

bfType을 지워버리면 됩니다.

 

요렇게요. 그렇다면 4바이트씩 정렬이 되겠죠?

이렇게요.

어? 그렇다면 bfType을 없앴는데 어떻게 bmp파일인지 아닌지 어떻게 구별하나?

 

그래서 위에 BMP파일을 바이너리 모드로 연 것입니다.

 

기억하시나요? 

 

맨 첫 주소에서 2바이트가 BM인지 아닌지 판별하는 bfType입니다. 

 

bfType이 맨 첫주소에 있기 때문에 파일 입출력 문제로 바뀌게 되는것입니다. 

 

fread로 읽어도 되지만... 파일입출력 공부겸 연습을 위해 굳이... 이렇게 써봤습니다. 

여러분들은 저 방법 말고 fread를 사용해서 읽으시길 바랍니다.~

 

아까와 똑같은 bmp파일으 열고 

 

bmp파일을 fseek함수를 이용해서 맨 첫주소로 이동하여, 0x42, 0x4D를 확인합니다. 

 

if문은 0x424D(==BM) 이면 첫 두 바이트를 출력하게 됩니다

잘 나오네요?

이 방법을 사용하면 1바이트씩이 아닌 4바이트씩 사용하여 BMP를 입출력하는 것이 가능하게 됩니다.

 

다음 C언어 글은 저 위 방법을 사용하면서, BMP를 사용해서 size를 키우는 실습(upscaling)을 해보도록 하겠습니다. 

728x90
반응형
반응형

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>


#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS

typedef uint8_t  BYTE;
typedef uint32_t DWORD;
typedef int32_t  LONG;
typedef uint16_t WORD;


/*********************************************************STRUCTS***********************************************************/
#pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER
{
WORD bfType;  //specifies the file type
DWORD bfSize;  //specifies the size in bytes of the bitmap file
WORD bfReserved1;  //reserved; must be 0
WORD bfReserved2;  //reserved; must be 0
DWORD bfOffBits;  //species the offset in bytes from the bitmapfileheader to the bitmap bits
}BITMAPFILEHEADER;


typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;  //specifies the number of bytes required by the struct
LONG biWidth;  //specifies width in pixels
LONG biHeight;  //species height in pixels
WORD biPlanes; //specifies the number of color planes, must be 1
WORD biBitCount; //specifies the number of bit per pixel
DWORD biCompression;//specifies the type of compression
DWORD biSizeImage;  //size of image in bytes
LONG biXPelsPerMeter;  //number of pixels per meter in x axis
LONG biYPelsPerMeter;  //number of pixels per meter in y axis
DWORD biClrUsed;  //number of colors used by the bitmap
DWORD biClrImportant;  //number of colors that are important
}BITMAPINFOHEADER;

typedef struct
{
BYTE  rgbtBlue;
BYTE  rgbtGreen;
BYTE  rgbtRed;
}RGBTRIPLE;

typedef struct
{
BYTE hslHue;
BYTE hslSaturation;
BYTE hslLightness;
}HSL;
#pragma pack(pop)
/*********************************************************STRUCTS***********************************************************/

#define filterWidth 3
#define filterHeight 3

void Filters(int biHeight, int biWidth, RGBTRIPLE** triple);
int CopyBitmapFile(char* filename, BITMAPINFOHEADER* bitmapInfoHeader, char* copy_filename);

int main()
{
char image[80], image_copy[80];
printf("원본 이미지 파일 : ");
scanf("%s", image);

printf("편집 이미지 파일 : ");
scanf("%s", image_copy);


BITMAPINFOHEADER bitmapInfoHeader;
memset(&bitmapInfoHeader, 0, sizeof(BITMAPINFOHEADER));
CopyBitmapFile(image, &bitmapInfoHeader, image_copy);

}


int CopyBitmapFile(char* filename, BITMAPINFOHEADER* bitmapInfoHeader, char* copy_filename)
{
int i, j, k;
FILE* filePtr; 
BITMAPFILEHEADER bitmapFileHeader; 
memset(&bitmapFileHeader, 0, sizeof(BITMAPFILEHEADER));

filePtr = fopen(filename, "rb");
if (filePtr == NULL)
return 1;

FILE* outptr = fopen(copy_filename, "wb");
if (outptr == NULL)
{
fclose(filePtr);
fprintf(stderr, "Could not create %s.\n", copy_filename);
return 2;
}


fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);

if (bitmapFileHeader.bfType != 0x4D42)
{
printf("이미지 파일 찾을 수 없엉");
fclose(filePtr);
fclose(outptr);
return 3;
}


fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);


fwrite(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, outptr);

fwrite(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, outptr);

int padding = (4 - (bitmapInfoHeader->biWidth * sizeof(RGBTRIPLE)) % 4) % 4;

int biHeight = abs(bitmapInfoHeader->biHeight);
int biWidth = bitmapInfoHeader->biWidth;

RGBTRIPLE** triple;
triple = (RGBTRIPLE**)malloc(sizeof(RGBTRIPLE*) * biHeight);
int ii;
for (ii = 0; ii < biHeight; ii++)
{
triple[ii] = (RGBTRIPLE*)malloc(sizeof(RGBTRIPLE) * biWidth);
}


for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{
fread(&triple[i][j], sizeof(RGBTRIPLE), 1, filePtr);
}
}



for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{
// brighter image
int x, y, z;
x = triple[i][j].rgbtBlue  + 75;
y = triple[i][j].rgbtGreen + 75;
z = triple[i][j].rgbtRed + 75;
triple[i][j].rgbtBlue = ((x > 255) ? 255 : x);
triple[i][j].rgbtGreen = ((y > 255) ? 255 : y);
triple[i][j].rgbtRed = ((z > 255) ? 255 : z);
}
}

for (i = 0; i < biHeight; i++)
{
for (j = 0; j < biWidth; j++)
{

fwrite(&triple[i][j], sizeof(RGBTRIPLE), 1, outptr);
}

fseek(filePtr, padding, SEEK_CUR);

for (k = 0; k < padding; k++)
{
fputc(0x00, outptr);
}
}

fclose(filePtr);
fclose(outptr);

for(int p = 0; p < biHeight; p++ )
{
free(triple[p]);
}
free(triple);

return 0;
}

728x90
반응형

+ Recent posts