| 증가하는 모바일 유입량.


  제 블로그처럼 뭔가 컴퓨터를 하면서 참고해야 할 만한 것(프로그래밍 관련이라던지 데스크탑 어플리케이션 유틸리티에 대한 팁이라던지..)을 포스트하는 블로그의 경우 살짝 예외가 되겠습니다만, 일반적으로 블로그는 짧은 시사적인 내용을 다룬다거나, 일상 생활에서 간단간단하게 스마트폰으로 확인하게 편한 글들을 적는 경우가 많죠.


이런 경우, 모바일에서의 유입량이 PC에서의 유입량보다 훨씬 많습니다. 따라서, 여러분이 가꾸려고 하는 블로그가 이런 형태의 타입이라고 한다면, PC에서의 블로그만큼이나 모바일에서 보이는 블로그도 같이 꾸며주어야 한다는 것입니다. 솔직히 말하자면, 어떠한 형태의 블로그를 가꾸든지 모바일에서 보이는 것도 신경을 많이 써야 하는 게 당연한 것이죠.


긴 말하는 것 좋아하지 않으니까, 바로 

"어떻게 모바일에서 애드센스로 광고를 할 수 있는지" 알아봅시다.






| 구글 애드 센스에서 '모바일 배너' 광고단위를 만들자. 


해결방법이 실은 정말 간단합니다. 여러분이 한번이라도 PC에서 애드 센스 광고 이미지들을 배치해본 적이 있다면, 쉽게 할 수 있는 것입니다. 여러분이 예전에 했듯이, 그대로 javascript 소스코드를 복사하여 원하는 곳에 붙여넣기하면 되는 것입니다. 


그러면, 애드센스 홈페이지에 들어가서 "내 광고"탭에 들어갑시다.

► https://www.google.com/adsense/app#myads-springboard


▲ 위 페이지에 들어가셨으면, "새 광고 단위"를 클릭합시다.


▲ 광고 크기 탭에서 '모바일 배너'를 클릭한 후 '저장 및 코드 생성'을 클릭합시다.


▲ 광고 코드를 복사합시다. 그리고 원하는 포스트 혹은 블로그의 html 소스 코드에다가 붙여넣기하면 끝입니다.





| 모바일 배너 소스 코드에 대한 팁은 없나?


1) 우선, 적용하시고 나면 바로 폰으로 확인하셔도 모바일에서 적용이 안되어 있을 가능성이 큽니다. 저같은 경우도, 소스 코드를 바로 집어넣은 후에는 모바일에 적용이 되어 있지 않아서 많이 당황했거든요 ^^


2) 아마 바로 적용시키시고 확인해보시면 모바일에서 광고가 왼쪽이나 오른쪽으로 치우쳐져 보이거나 정상적으로 한 가운데 있지 않은 경우가 많습니다. 이러한 경우를 막기 위해서 다음과 같이 소스 코드를 변경하신 후에 추가하는 것을 추천 드립니다.


<center>

<!-- ... 광고 코드를 바로 밑줄에 적으세요...  -->

</center>







| 우리 모두 모바일 애드 센스로 수익을 많이 올리기를 바라며...


 저는 포스트 분야 자체가 주류가 아니기 때문에.. (IT/전자 소식도 가끔 올리지만.. 주로 프로그래밍에 관련된 내용이므로 기껏해야 컴퓨터 전공으로 공부하는 대학생 및 고등학생 정도 쯤이 보시겠죠...) 아직 방문자수가 많지 않은 편이네요... 그래서 애드 센스 수익 광고도 거의 없는 편이구요... 하지만 꾸준히 하면 언젠가는 짭잘한 용돈 정도는 되지 않을까 생각을 하네요.


  이 포스트로 방문해주신 분들도 분명 초보(?) 블로거이실 테니까, 우리 모두 같이 애드 센스로 수익을 많이 올릴 수 있게 서로 자주 방문해주기로 해요(???) 혹시 그러시고 싶으시면 댓글로 블로그 주소를 남겨주시면 제가 여러번 방문해드리겠습니다..... 서로 윈윈합시다...ㅋㅋㅋ







| SkyDrive에서 OneDrive로...





마이크로 소프트 사의 공유 클라우드 서비스인 '스카이드라이브'(SkyDrive)가 '원드라이브'(OneDrive)로 명칭을 변경한다고 합니다. 명칭을 변경하는 일정에 대해서 언급된 것은 없으나, 곧(soon) 바뀐다고 합니다. 이번 변화에 있어서 사용자가 딱히 해야 할 것은 없는 것으로 보입니다.


마이크로 소프트 사가 스카이 드라이브 이름을 바꾸는 까닭은 영국의 위성방송사 'BSkyB'와의 상표분쟁 소송 때문이라고 합니다. 


아무튼, 이번에 바꾼 이름인 'One Drive'는 PC와 스마트폰은 물론, 태블릿 PC에 이르기까지 다양한 기기에서 하나의 저장장소를 쓴다는 의미를 담고 있다고 합니다.


아래는, OneDrive를 소개하는 마이크로소프트사의 유튜브 동영상입니다.




| Introducing OneDrive






스카이 드라이브 원드라이브 마이크로소프트 클라우드 공유 클라우드 서비스 'Sky Drive' 'One Drive' 'Microsoft' 'Cloud Services'OneDrive SkyDrive


일반적으로 C 포인터로 큰 규모의 메모리를 다룰 때 메모리가 조각화되어 관리된다. 즉, 인접한 메모리를 할당하는 것이 아니라, 적당하게 힙 메모리에서 떨어진 구역의 메모리들을 가져와서 마치 하나로 연결되어 있고 인접되어 있듯이 사용하는 것이다. 


그렇다면 한 번에 인접한 메모리를 할당하려고 한다면 어떻게 해야 할까?

이 포스트에서, C언어로 다차원 배열을 할당하는 경우 메모리를 인접하게 할당하는 방법에 대해서 다룰 것이다.




| 일반적인 코딩 방법


우리는 일반적으로 다차원 배열을 동적으로 할당할 때, 다음과 같이 코딩을 한다.


 int rows = 2;
 int columns = 5;
 int i=0, j=0;

 int ** matrix = (int**) malloc(rows*sizeof(int*));

 for(i=0; i<rows; i++){
         matrix[i] = (int*)malloc(columns * sizeof(int));
 }


그런데, 이렇게 해버리면 다음과 같이 결과가 나타난다.




▲ [0][4] 원소와 [1][0] 원소 사이에 16바이트 갭이 생겼다.


첫 번째 행의 마지막 원소와 두 번째 행의 첫번쨰 원소의 주소가 int 변수의 크기인 4 byte만큼 차이가 나는 것이 아니라 16 byte가 차이난다. (위 숫자들은 전부 16진수임을 알기를 바란다. 40에서 50으로 갔음은 10바이트가 아니라 16바이트가 차이났음을 의미한다.)


이제, 해결책에 대해서 알아보자.


2차원 배열에 인접한 메모리를 할당하는 두 가지 접근 방법이 있다. 첫 번째 기법은 '바깥쪽' 배열을 먼저 할당한 후 전체 열에 대한 메모리를 할당하는 방법이고, 두 번째 기법은 모든 메모리를 한 번에 할당하는 방법이다. 






| 첫 번째 방법


첫 번째 기법을 다음 예제 코드에서 설명하고 있다. 첫 번째 malloc 함수 호출로 정수에 대한 포인터의 배열을 할당한다. 각 요소는 열에 대한 포인터를 가지게 된다. 두 번째 malloc 함수 호출은 우리가 선언할 다차원 배열의 제일 첫 번째 원소의 주소에 모든 요소에 대한 메모리를 할당하게 된다. (말이 어렵다면, 그냥 배열의 이름에다가 이후에 저장할 모든 메모리의 크기를 한 번에 할당한다고 생각하자.) 그리고 for 루프는 첫 번째 배열의 각 요소에 두 번째 malloc에서 할당한 메모리의 일부분을 지정한다.


int rows = 2;
int columns = 5;
int i=0, j=0;

int ** matrix = (int**)malloc(rows*sizeof(int*));
matrix[0] = (int*)malloc(rows*columns*sizeof(int));
for(i=1; i<rows; i++)
        matrix[i] = matrix[0] + i*columns;



엄밀히 따지면, 첫 번째 배열의 메모리는 배열의 '본체(body)' 부분과 떨어져 있을 수도 있다. 하지만 배열의 본체 부분에서는 메모리의 인접한 영역이 할당된다. 



▲ 배열의 모든 요소들이 인접하게 나열됬다.





| 두 번째 방법


다음 예제 코드에서는 두 번째 기법을 설명하고 있다. 


int * matrix = (int*)malloc(rows*columns*sizeof(int));


결과는 다음과 같다.



▲ 배열의 모든 요소들이 인접하게 나열됬다.


나중에 코드 안에서 이 배열을 참조할 때는 배열 첨자를 사용할 수 없다. 대신, 다음 코드에서 설명하는 것처럼 배열에 대한 인덱스를 수동으로 계산해야 한다. 각 배열 요소는 그 인덱스의 곱으로 초기화한다. (컴파일러가 배열 첨자를 허용할 때 필요한 배열의 형태에 대한 정보가 없으므로 배열 첨자를 사용할 수 없기 때문에 배열 첨자를 사용할 수 없는 것이다.)


int i=0, j=0;

for(i=0; i<rows;i++){
        for(j=0; j<columns; j++){
                *(matrix+(i*columns)+j) = i*j;
        }       
} 


2차원 배열에 대한 인접한 메모리를 할당하는 두 가지 일반적인 접근 방법에 대해 설명했다. 어떤 방법을 이용할 것인지는 어플리케이션에 따라 다르다. 하지만 두 번째 접근 방법이 '전체'배열에 대한 단일 메모리 블록을 생성한다.



▲ 이번에 만들어볼 포스팅 이후 여러분의 사파리 오른쪽 클릭 창에 생길 녀석 '네이버 영어사전 찾기'



Automator를 이용하면, 여러분이 귀찮게 생각하던 여러 가지 단계를 오로지 클릭 두 단계만으로 해결할 수 있게 해줍니다. 진짜에요. 믿어봐요.



예를 들어봅시다. 우리가 '네이버 영어사전'에 '뜻이 궁금한 영어단어'를 검색하려고 한다면, 다음과 같은 복잡한 과정을 거쳐야 합니다.


1) 뜻이 궁금한 '영어단어'의 스펠링을 외우거나 복사한다.

2) 네이버 영어사전 웹사이트로 이동하기 위해 웹 브라우저 주소창에 주소를 입력하거나,

   네이버에 들어가서 영어사전 사이트를 찾아간다.

3) 영어 사전 사이트에 간 이후에도 입력을 위해서 입력창에 마우스로 클릭하여 포커스를 맞추어주어야 한다.

4) 그리고 나서, 궁금했던 영어단어를 입력하거나 붙여넣기한다.


간단해 보이지만.... 2번 과정과 3번 과정에서 꽤나 큰 스트레스를 받아야 합니다. 웹 브라우저 주소창에 마우스를 올리는 것은 물론, 주소를 직접 입력해야 하며 가령 키보드를 사용하지 않고 마우스로만 네이버 사이트를 뒤져서 사전 사이트로 들어간다고 쳐도 마우스를 피곤하게 움직여야 하죠...


그런데, Automator를 이용하면 단 2가지 과정... 그것도 쓸데없는 키보드 입력이나 마우스 움직임이 필요하지 않습니다.


1) 모르는 영어단어를 더블 클릭하거나 드래그하 후, 오른쪽 클릭한다. 

2) 서비스> '네이버 영어사전 찾기'를 누른다.


직관적이지 않습니까? 크게 복잡하지 않았던 일이지만, 꽤나 직관적으로 바뀌었습니다. 


바로 이것이 Automator입니다.




그러면 이제, 이 Automator를 어떻게 사용해야 하는가?를 알려드리겠습니다.




1. Automator를 실행하세요. 그리고 '새로운 도큐멘트'를 만듭니다.


2. 도큐멘트 유형을 '서비스'로 선택해 주세요.



3. 동작 > 보관함 > 유틸리티 > 'AppleScript 실행'을 더블 클릭하시거나 오른쪽 넓은 공간 드래그해주세요!



4. 그러면 이렇게, 애플스크립트를 적을 수 있는 공간이 생깁니다.




5. 애플 스크립트 실행 창에다가


on run {input, parameters}
end run


라고 입력해주세요!



6. 이번에는 동작 > 보관함 > 인터넷 > '웹 사이트 팝업'을 더블 클릭 혹은 드래그 해주세요.



6. 그리고, 사이즈 크기 '사용자화' 320 x 480 크기로,

사용자 에이전트는 'Safari'로, 위치는 '중앙', 출력은 '입력 URL'이 되도록 해주세요.


아, 그리고 맨 위쪽에 있는 선택 항목 위치도 'Safari.app'으로 만들어주세요.



7. 끝났습니다. 제가 초등학생도 따라할 수 있을 정도로 너무 자세하게 적어서 그렇지...

이걸 그냥 혼자 하면 1분도 안걸려서 오토메이터 작성이 가능하실 겁니다.



이제, Command + S 로 '저장'하시기만 하면 진짜 끝이구요.

사파리 가서 모르는 영어단어 드래그하시고 오른쪽 클릭해보세요.


그냥 서비스가 바로 생겼죠? 이렇게 오토메이터는 막강한 기능을 제공하는 간편한 도구로서 쓰일 수 있습니다.




이렇게 아주 간단한 예제를 따라 해보셨으니 오토메이터가 얼마나 작성하기 쉽고, 그와 더불어서 얼마나 막강한 기능을 제공하는지 느껴보셨을 거에요. 이제 여러분이 직접 이것 저것 동작 보관함에 있는 것들을 만져보고 커스터마이징 해서 새로운 기능의 오토메이터를 만들어보실 수 있으실 거라고 생각이 드네요. 


다음에 더 자세한 오토메이터 문법과 사용 실례를 가지고 포스팅하겠습니다.

그럼 이만... :)


C++ Pointers and Memory – Free Coding Tutorials



* 배열의 이름은 무엇을 의미하는가?

배열의 이름은 포인터이다. 단 그 값을 바꿀 수 없는 '상수 형태의 포인터'이다. 다음 예제에서는 이러한 사실을 증명하고 있다.

- 열혈 C 프로그래밍(윤성우 저) 에서 -



 우리나라 C프로그래밍 기초 기본서로 꽤 유명한 '열혈 C 프로그래밍'(윤성우 저)에서는 위와 같이 배열의 이름은 포인터라고 언급하고 있습니다. 물론, 저도 이 책을 처음으로 프로그래밍을 시작하고, 프로그래밍의 아주 기본적인 기법이라던지, C프로그래밍의 기본적인 문법을 익히고 기본 개념을 쌓는데 큰 도움을 받은 것이 사실입니다.


 하지만, C프로그래밍의 최고급 지식을 쌓기 위해 고군분투하는 요즘, 윤성우님의 책보다 조금 더 권위있는 책을 읽고 있습니다. 그리고 그 책에는 '배열의 이름은 포인터가 아니다'라고 언급하고 있습니다.


배열과 포인터에 대한 일반적인 오해는 서로 완벽하게 맞바꾸어 사용할 수 있다고 생각하는 것이다. 배열의 이름은 포인터가 아니다. 때로 배열의 이름을 포인터로 다루기도 하고, 배열의 표기가 포인터와 함께 사용되기도 하지만, 둘은 분명히 구분되어야 하고 언제나 서로 대치할 수 있는 것은 안다. 이 차이점을 이해하면 배열과 포인터의 표기법을 잘못 사용하지 않을 수 있다


- Understanding and Using C Pointers (리차드 리스 저) 에서 - 


  배열의 이름만 따로 사용한다면 배열의 주소를 반환하기는 하지만, 배열의 이름을 할당(assignment)의 대상으로 이용할 수 없으므로, 배열의 이름을 포인터와 동일한 개념, 혹은 포함되는 개념등으로 이해하면 안된다는 것입니다.




| 문제


주어진 그래프가 만약 DAG(Directed Acyclic Graph, 방향성 비사이클 그래프)라면, 그 그래프의 꼭짓점들을 위상 정렬 하는 프로그램을 구현하라. DFS(Depth-First Search, 깊이 우선 탐색) 과정을 진행하는 동안, 작은 숫자의 꼭짓점들을 먼저 방문하도록 설계해야 한다.


입력(표준 입력)

첫 번째 에는 꼭짓점의 개수를 입력한다.

다음 줄부터, 그래프 G의 인접 리스트가 서로 연결되어 있는 꼭짓점인 x와 y로 표현되어야 한다. 이 것은 꼭짓점 x에서 꼭짓점 y로 나아가는 간선이 존재함을 의미한다.


출력(표준 출력)

첫 번째 줄에는, 주어진 그래프 G가 DAG라면 1을 출력하고, 아니면 0을 출력하게 한다.

그리고 그래프가 DAG이면 그 다음 줄에, 위상 순서대로 꼭짓점들을 출력해야 한다.


example)

입력

출력 

9

1 4

1 5

2 5

4 5

4 6

6 9

7 6

7 8

8 9

EOF 입력(윈도우에서는 Ctrl+Z)

1

7 8 3 2 1 4 6 9 5 





| 소스 코드


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

#define WHITE			0
#define GRAY			1
#define BLACK			2

#define TREE_EDGE		1
#define BACK_EDGE		2
#define FORWARD_EDGE	3
#define CROSS_EDGE	4

#define TRUE			1
#define FALSE			0

typedef struct _vertex{
	int color;
	int f;
	int d;
} Vertex;

typedef struct _VertexInfo{
	int out_degree;
	int * list;
} VertexInfo;

Vertex * vertex;
VertexInfo * vertexInfo;
int numV, time;
int * topoList;
int topo_num=0;
int isDAG = TRUE;

void addEdge(int fromV, int toV){
	int i=0;
	int cur=0;

	for(i=0; i < numV; i++)
	{
		if(vertexInfo[fromV].list[i] == 0)
			break;
		cur ++;
	}
	vertexInfo[fromV].list[cur] = toV;
}

void DFS_Visit(int fromV){
	int i=0, toV;

	/* DISCOVERED */
	time++;
	vertex[fromV].color = GRAY;
	vertex[fromV].d = time;

	for(i=0; i < vertexInfo[fromV].out_degree; i++){
		toV = vertexInfo[fromV].list[i];
		switch(vertex[toV].color)
		{
		case WHITE:
			DFS_Visit(toV);
			break;

		case GRAY:
			isDAG = FALSE;
			break;

		case BLACK:
			break;
		default:
			break;
		}
	}

	/* FINISHED */
	vertex[fromV].color = BLACK;
	topoList[numV-(topo_num++)] = fromV;
	time ++;
	vertex[fromV].f = time;
}

void Sort(int * list, int num)
{
	int i=0, j=0, tmp;

	for(i=0; i < num; i++)
	{
		for(j=i+1; j < num; j++)
		{
			if(list[i] > list[j])
			{
				tmp = list[i];
				list[i] = list[j];
				list[j] = tmp;
			}
		}
	}
}


void DFS(void)
{
	int i;
	for(i=1; i <=numV; i++)
	{
		vertex[i].d = 0;
		vertex[i].f = 0;
		vertex[i].color = WHITE;
		Sort(vertexInfo[i].list, vertexInfo[i].out_degree);
	}

	time = 0;
	for(i=1; i <=numV; i++)
	{
		if(vertex[i].color == WHITE)
		{
			DFS_Visit(i);
		}
	}
}


int main(void)
{
	int i=0, j=0;
	int fromV, toV;

	scanf("%d", &numV);

	/* INITIALIZATION START! */
	vertexInfo = (VertexInfo*)malloc(sizeof(VertexInfo)*(numV+1));
	for(i=1; i <=numV; i++){
		vertexInfo[i].list = (int*)malloc(sizeof(int)*numV);
		vertexInfo[i].out_degree = 0;
	}
	for(i=1; i <=numV; i++){
		for(j=0; j < numV; j++){
			vertexInfo[i].list[j] = 0;
		}
	}
	vertex = (Vertex*)malloc(sizeof(Vertex)*(numV+1));
	topoList = (int*)malloc(sizeof(int)*(numV+1));
	/* INITIALIZATION END! */
	

// INPUT
	while(scanf("%d %d", &fromV, &toV)!=EOF){
		addEdge(fromV, toV);
		vertexInfo[fromV].out_degree ++;
	}
	DFS();

// OUTPUT
	printf("%d\n", isDAG);
	if(isDAG)
	{
		for(i=1; i <=numV; i++)
			printf("%d ", topoList[i]);
	}

	return 0;
}


+ Recent posts