C에서는 동일한 이름의 다른 내용의 함수를 정의할 수 없다. 그러나 C++에서는 매개변수의 선언만 다르게 해주면 같은 이름의 함수를 정의 할 수 있다. C에서는 호출할 함수를 찾을 때 함수의 이름만을 이용하여 찾지만, C++에서는 호출할 함수를 찾을 때 함수의 이름과 매개변수 선언의 두가지 정보를 동시에 이용하여 찾기 때문이다. 함수 오버로딩의 예시는 다음과 같다.

#include <iostream>

void MyFunc(void)
{
    std::cout << "MyFunc(void) called" << std::endl; 
}

void MyFunc(char c)
{
    std::cout << "MyFunc(char c) called" << std::endl;
}

void MyFunc(int a, int b)
{
    std::cout << "MyFunc(int a, int b) called" << std::endl;
}

int main(void)
{
    MyFunc();
    MyFunc('A');
    MyFunc(12, 13);

    return 0;
}

'Language > C, C++' 카테고리의 다른 글

[C++] new, delete  (0) 2021.08.24
[C++] Reference(참조자)  (0) 2021.08.21
[C++] 입출력  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15

C에서는 malloc과 free함수를 이용하여 힙 상에서 메모리의 할당과 해제를 지원하였다. C++에서 메모리의 할당과 해제를 지원하는 것은 new와 delete다. new로 힙 상에 메모리를 할당하고, delete로 할당한 메모리를 해제한다.

#include <iostream>

int main()
{
    int* p = new int;
    *p = 10;

    std::cout << *p << std::endl;

    delete p;
    return 0;
}

 

다음과 같이 new로 배열을 할당할 수 있다.

#include <iostream>

int main()
{
    int arr_size;
    
    std::cout << "array size : ";
    std::cin >> arr_size;
    
    int *list = new int[arr_size];

    for(int i = 0; i < arr_size; i++)
        std::cin >> list[i];
    
    for(int i = 0; i < arr_size; i++)
        std::cout << i << "th element of list : " << list[i] << std::endl;
    
    delete[] list;
    return 0;
}

배열을 할당할 때는 자료형과 [] 안에 배열의 크기를 넣어주면 된다. 배열로 할당된 메모리를 해제할 때는 delete[]처럼 delete 뒤에 []을 붙여주면 된다.

'Language > C, C++' 카테고리의 다른 글

[C++] 함수 오버로딩 (Function Overloading)  (0) 2021.09.07
[C++] Reference(참조자)  (0) 2021.08.21
[C++] 입출력  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15

C에서는 어떠한 변수를 가리키려면 반드시 포인터를 사용해야 했다.

C++에서는 포인터 말고도 레퍼런스(참조자)라는 방식으로 다른 변수나 상수를 가리킬 수 있다.

#include <iostream>

int main()
{
    int a = 3;
    int& another_a = a;     // 참조자 사용

    another_a = 5;
    std::cout << "a : " << a << std::endl;
    std::cout << "another_a : " << another_a << std::endl;

    return 0;
}

위 코드에서는 a 의 참조자 another_a를 정의하였는데, 참조자는 가리키고자 하는 타입 뒤에 &를 붙이면 된다. 

참조자를 선언한다는 것은 another_a는 a의 또 다른 이름이라고 컴파일러에게 알려주는 것이다. 따라서 위 코드에는 another_a에 5를 대입하였지만 a의 값이 5로 바뀐 것을 확인할 수 있다.

레퍼런스(참조자)는 포인터와 비슷하지만 몇가지 차이점이 있다. 레퍼런스는 반드시 정의할 때 어떤 변수나 상수의 별명이 될 것인지 지정해야 한다. int& another_a; 와 같은 형식의 코드는 불가능하다. 또한 레퍼런스가 한 번 별명이 되면 더 이상 다른 변수를 참조 할 수 없다. 

레퍼런스는 메모리 상에 존재하지 않을 수도 있다. 위 코드의 경우 another_a 변수는 메모리 상의 공간을 차지하지 않고 another_a가 쓰이는 자리를 모두 a로 바꾸면 되기 때문이다.

#include <iostream>

int change_var(int& p)
{
    p = 3;

    return 0;
}

int main()
{
    int num = 5;

    std::cout << num << std::endl;
    change_var(num);
    std::cout << num << std::endl;
    
    return 0;
}

위 코드에서는 함수의 인자로 레퍼런스를 받고 있다. 위 설명에서 int& p; 와 같은 코드는 사용할 수 없다고 했지만 함수의 인자로 사용하게 되면 int& p = num;의 의미가 되어 상관없다. 또한 인자로 변수를 넣을 때에도 포인터와 다르게 &num이 아닌 num을 넣으면 된다. 따라서 change_var()함수의 p=3; 이라는 코드는 num = 3; 이라는 코드와 동일하다.

 

#include <iostream>

int main()
{
    const int& ref = 4;

    std::cout << ref << std::endl;
    return 0;
}

위 코드와 같이 리터럴 값을 참조하려면 상수 참조자로 선언해야 한다.

 

레퍼런스의 배열은 C++ 규정상 표준안 8.3.2/4 에 선언할 수 없다고 명시되어 있다.

그러나 반대로 배열들은 레퍼런스로 참조가 가능하다.

#include <iostream>

int main()
{
    int arr[3] = {1, 2, 3};
    int(&ref)[3] = arr;

    ref[0] = 2;
    ref[1] = 3;
    ref[2] = 1;

    std::cout << arr[0] << arr[1] << arr[2] << std::endl;
    return 0;
}

위 코드와 같이 배열의 크기를 명시하여 배열을 참조할 수 있다.

 

#include <iostream>

int function()
{
    int a = 5;
    return a;
}

int main()
{
    const int& c = function();
    std::cout << c << std::endl;
    return 0;
}

위 코드처럼 참조자가 아닌 값을 리턴하는 함수 값을 참조자로 받을 수 있다. 함수의 리턴값을 참조자로 받으려면 const로 선언해줘야 한다.

'Language > C, C++' 카테고리의 다른 글

[C++] 함수 오버로딩 (Function Overloading)  (0) 2021.09.07
[C++] new, delete  (0) 2021.08.24
[C++] 입출력  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15
#include <iostream>

int main()
{
    char *inp;

    // std::cout : 출력
    std::cout << "[+] Input : ";
    // std::cin : 입력
    std::cin >> inp;
    
    std::cout << "[+] Output : ";
    // std::endl : 줄 바꿈
    std::cout << inp << std::endl;

    return 0;
}

iostream 은 Input/Output Stream의 약자로 c++에서 입출력을 위한 헤더 파일이다. 

출력은 std::cout로, 입력은 std::cin으로 받을 수 있다. 

std::endl은 출력 후 줄을 바꿔준다.

'Language > C, C++' 카테고리의 다른 글

[C++] new, delete  (0) 2021.08.24
[C++] Reference(참조자)  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15
[C] 스트림  (0) 2021.01.15

다음과 같이 2차원 리스트가 있을 때 행은 열로, 열은 행으로 변환할 수 있다.

li = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(li)
li = list(map(list, zip(*li)))
print(li)
'''
output : 
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
'''

 

'Language > Python' 카테고리의 다른 글

멜론차트 1~50위 크롤링  (0) 2019.09.02
반복문(for)  (0) 2019.03.07
반복문(while)  (0) 2019.02.09
조건문(if)  (0) 2019.02.08
입력 함수(input)  (0) 2019.02.08

Unit 34. 포인터 사용하기

값을 저장할 때 사용하는 변수는 컴퓨터의 메모리에 생성된다. 메모리에 일정한 공간을 확보해 두고 원하는 값을 저장하거나 가져오는 방식이다.

변수는 num과 같이 지정된 이름으로 사용되지만, 메모리의 특정 장소에 위치하므로 메모리 주로로도 표현할 수 있다.

메모리 주소는 다음과 같이 출력할 때변수 앞에 &을 붙이면 된다.

1
2
3
4
5
6
7
8
#include <stdio.h>
 
int main()
{
    int num1 = 10;
    printf("%p\n", &num1);
    return 0;
}
cs

메모리주소는 위와 같이 16진수 형태이며 서식지정자 %p를 사용하여 출력한다. 16진수를 의미하는 %x나 %X를 이용해도 된다. 메모리주소는 고정된 값이 아니기 때문에 컴퓨터마다, 실행할 때 마다 달라진다.

시스템이 32비트인지, 64비트인지에 따라 메모리 주소의 범위도 달라진다. 각각의 범위는 다음과 같다.

  • 32비트: 16진수 8자리

  • 0x00000000 ~ 0xFFFFFFFF

  • 예) 0x008AF7FC

  • 64비트: 16진수 16자리

  • 0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF

  • 예) 0x00000000008AF7FC

  • 64비트 메모리 주소는 0x00000000`00000000처럼 8자리씩 끊어서 `를 붙이기도 한다

리눅스나 맥의 osX에서는 서식지정자 %p를 사용하며 주소 앞에 0x를 붙이고, a~f는 소문자로 출력되며 높은 자리수의 0은 생략된다.

 

34.1 포인터 변수 선언하기

C에서 메모리 주소는 포인터 변수에 저장한다.

포인터 변수는 다음과 같이 *을 이용하여 선언한다.

자료형 *포인터이름;
포인터 = &변수;
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
 
int main()
{
    int *numPtr;      
    int num1 = 10;   
    numPtr = &num1;  
 
    printf("%p\n", numPtr);   
    printf("%p\n"&num1);     
    return 0;
}
cs

포인터 변수를 선언할 때는 자료형 뒤에 *(Asterisk, 애스터리스크)를 붙인다. *위치는 자료형과 변수 사이에 있다면 어디에 있던 상관 없다.

&로 변수의 주소를 구해서 포인터 변수에 저장할 수도 있다.

printf로 포인터 변수 numPtr을 출력해보면 num1의 메모리 주소와 포인터 변수는 같은 값이 나온다. 포인터와 메모리 주소는 같은 의미이다.

포인터 변수를 선언할 때는 자료형을 알려주고 *을 붙여야 한다. 만약 변수가 int형이면 이 변수를 저장하는 포인터는 int *로 지정해야 한다.

포인터는 메모리의 특정 위치를 가리킬 때 사용한다. 포인터가 메모리를 가리키는것을 그림으로 확인하면 다음과 같다.

 

34.2 역참조 연산자 사용하기

포인터 변수에는 메모리 주소가 저장되어 있는데, 이 포인터 주소를 이용하여 값을 가져오고 싶으면 역참조 연산자 *을 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main()
{
    int *numPtr;      
    int num1 = 10;  
    numPtr = &num1; 
    
    printf("%d\n"*numPtr);    // 역참조 연산자 사용
    return 0;
}
cs

역참조 연산자 *는 포인터 앞에 붙인다. 위와 같이 포인터 변수 앞에 *을 붙이면 그 변수에 저장된 메모리 주소로 가서 값을 가져온다.
다음은 포인터 변수에 역참조 연산자를 사용하여 값을 할당한 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int main()
{
    int *numPtr;      
    int num1 = 10;   
    numPtr = &num1;  
    *numPtr = 20;    
 
    printf("%d\n"*numPtr);    
    printf("%d\n", num1);       
    return 0;
}
cs

역참조 연산자로 메모리에 접근하여 값을 저장할 수도 있다. 위 num1 변수에는 10이 들어있었는데 num1을 가리키는 포인터변수 numPtr에 역참조 연산자로 20을 저장했기 때문에 num1 과 numPtr을 출력하면 둘 다 20이 나온다.
만약 포인터 변수 numPtr에 num1을 할당하면 간접 참조 수준이 다르다는 경고가 발생한다. numPtr과 num1이 각각 포인터와 int로 자료형이 다르기 때문이다. 컴파일 경고가 발생하지 않도록 하려면 numPtr앞에 * 을 붙여 값을 가져오게 하여 int와 동일한 자료형으로 만들어야 한다. 값을 가져오는것은 자료형을 동일하게 한다는 것이다. 주소연산자 &도 마찬가지로 자료형을 맞춰주는 역할을 한다.
변수, 주소 연산자, 역참조 연산자, 포인터의 차이는 다음과 같다.

 

34.3 디버거에서 포인터 확인하기

디버거를 사용하면 변수의 메모리 주소, 포인터, 역참조를 쉽게 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
int main()
{
    int *numPtr;     
    int num1 = 10;    
 
    numPtr = &num1;   
    *numPtr = 20;    
 
    printf("%d\n"*numPtr);    
    printf("%d\n", num1);      
    return 0;
}
cs

9번째 줄에 중단점을 설정하고 실행하면 다음과 같다.

위 코드에서 8번째 줄까지 실행한 상태이다. 포인터 변수 numPtr에 num1의 주소가 저장되있다. Locals 창에서 > 을 클릭하면 현재 메모리 주소에 저장된 값을 확인할 수 있다.

맥 OS에서 메모리 주소의 내용을 확인하려면 Xcode에서 디버깅 하면 된다. 

Xcode에서 9번째 줄과 10번째 줄에 중단점을 지정하고 한번 디버깅 버튼을 클릭하면 8번째 줄까지 실행되어 numPtr은 num1의 주소를 가리킨다.

이때 메모리 주소의 내용을 확인하려면 numPtr을 클릭하고 우클릭을 눌러 view memory of *numPtr을 클릭하면 된다.

num1은 int 자료형이기 때문에 0A 00 00 00 처럼 숫자 4개를 차지한다. 리틀 엔디언 방식으로 저장되기 때문에 값이 거꾸로 저장되어 원래는 00 00 00 0A 이며 0A는 10진수로 10이다. 

다음줄을 실행시키면 메모리 주소의 내용이 다음과 같이 바뀐다.

0A가 14로 바뀌었다. 16진수 14는 10진수로 20이다. 역참조후 20을 할당하여 메모리의 내용이 바뀐 것을 확인할 수 있다.

 

 

34.4 다양한 자료형의 포인터 선언하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
int main()
{
    long long *numPtr1;    
    float *numPtr2;        
    char *cPtr1;  
 
    long long num1 = 10;
    float num2 = 3.5f;
    char c1 = 'a';
 
    numPtr1 = &num1;    
    numPtr2 = &num2;    
    cPtr1 = &c1;       
 
    printf("%lld\n"*numPtr1);    
    printf("%f\n"*numPtr2);    
    printf("%c\n"*cPtr1);       
    return 0;
}
cs

C에서 사용할 수 있는 모든 자료형은 포인터로 만들 수 있다.

포인터에서 저장되는 메모리 주솟값은 정수형으로 동일하지만 선언하는 자료형에 따라 메모리에 접근하는 방법이 달라진다. 다음과 같이 포인터를 역참조 할 때 자료형의 크기에 맞게 값을 가져오거나 저장한다.

상수와 포인터

포인터에도 const 키워드를 붙일 수 있는데 const의 위치에 따라 특성이 달라진다.

1
2
3
4
5
const int num1 = 10;    
const int *numPtr;      
 
numPtr = &num1;
*numPtr = 20; //컴파일에러
cs

num1 이 const int 이므로 num1의 주소를 넣을 포인터도 const int로 선언해야 한다. 역참조 연산자로 값을 변경하려 해도 num1은 상수이기 때문에 컴파일 에러가 발생한다.

다음은 포인터 자체가 상수인 상황이다.

1
2
3
4
5
int num1 = 10;  
int num2 = 20;    
int * const numPtr = &num1;    
 
numPtr = &num2;    // 컴파일 에러
cs

numPtr은 포인터 자체가 상수이기 때문에 상수 포인터에는 다른 변수의 메모리 주소를 할당할 수 없다.

다음은 상수 포인터가 상수를 가리키는 상황이다.

1
2
3
4
5
6
const int num1 = 10;    
const int num2 = 20;   
const int * const numPtr = &num1;    
 
*numPtr = 30;      // 컴파일 에러
numPtr = &num2;    // 컴파일 에러
cs

포인터를 역참조한 값을 변경하려해도 해당 주소가 가리키는 것은 상수이기 때문에 컴파일 에러가 불가능하며 포인터도 상수 이므로 메모리 주소도 변경할 수 없다.

 

34.5 void포인터 선언하기

void 포인터는 자료형이 정해져있지 않은 포인터이다. 다음과 같이 사용한다.

void *포인터이름;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
int main()
{
    int num1 = 10;
    char c1 = 'a';
    int *numPtr1 = &num1;
    char *cPtr1 = &c1;
 
    void *ptr;        
 
    
    ptr = numPtr1;    
    ptr = cPtr1;     
 
   
    numPtr1 = ptr;   
    cPtr1 = ptr;      
 
    return 0;
}
cs

기본적으로 C는 자료형이 다른 포인터끼리 메모리 주소를 저장하면 컴파일 경고를 발생하지만 void 포인터는 어떤 자료형의 포인터든 모두 저장할 수 있다. 이런 특성 때문에 void 포인터를 범용 포인터라고 부르기도 한다.

void 포인터는 자료형이 정해지지 않았으므로 값을 가져오거나 저장할 크기도 정해지지 않았기 때문에 역참조를 하게 되면 컴파일 에러가 발생한다.

또한 void키워드로 변수를 선언할 수도 없다.

void포인터는 함수에서 다양한 자료형을 받아들일 때, 함수의 반환 포인터를 다양한 자료형으로 된 포인터를 저장할 때, 자료형을 숨기고 싶을 때 사용한다.

 

34.6 이중 포인터 사용하기

포인터의 메모리주소를 저장하는 포인터의 포인터도 선언할 수 있다. 이것을 이중 포인터 라고 하며 *을 두 번 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
 
int main()
{
    int *numPtr1;    
    int **numPtr2;    
    int num1 = 10;
 
    numPtr1 = &num1;  
    numPtr2 = &numPtr1; 
 
    printf("%d\n"**numPtr2);  
    return 0;
}
cs

포인터도 변수이기 때문에 포인터를 저장하고 있는 메모리 주소도 구할 수 있다. 포인터의 메모리 주소는 일반 포인터에는 저장할 수 없고 **을 사용한 이중 포인터에 저장해야 한다.

이중포인터를 끝까지 따라가서 실제 값을 가져오려면 변수 앞에 역참조 연산자를 두 번 사용하면 된다.

포인터를 선언할 때 *의 개수에 따라서 삼중포인터, 사중포인터 등도 만들 수 있다.

 

34.7 잘못된 포인터 사용

포인터는 메모리 주소를 저장하는 용도이므로 다음과 같이 값을 직접 저장하면 안된다.

 int *numPtr = 0x100; 

메모리에서 0x100은 잘못된 주솟값이기 때문이다.

위와 같이 할당할 때 까지는 에러는 발생하지 않지만 역참조로 메모리 주소를 접근할 때 에러가 발생한다. 운영체제는 프로그램이 잘못된 메모리 주소에 접근했을 때 에러를 발생시킨다.

만약 실제 존재하는 메모리 주소라면 포인터에 직접 저장할 수 있다.


34.8 퀴즈

정답은 c이다.

정답은 d이다.

정답은 a이다.

정답은 e이다.

정답은 d이다.

 

34.9 연습문제 : 포인터와 주소 연산자 사용하기

정답은 다음과 같다.

1. numPtr = &num1;

2. numPtr = &num2;

 

34.10 심사문제 : 포인터와 주소 연산자 사용하기

정답은 다음 코드와 같다.

1
2
numPtr1 = &num1;
numPtr2 = &numPtr1;
cs

 

'Language > C, C++' 카테고리의 다른 글

[C++] Reference(참조자)  (0) 2021.08.21
[C++] 입출력  (0) 2021.08.21
[C] 문자 단위 입출력 함수  (0) 2021.01.15
[C] 스트림  (0) 2021.01.15
[C] main 함수로의 인자 전달  (0) 2021.01.09

문자 출력 함수

모니터에 하나의 문자를 출력할 때 일반적으로 사용하는 함수는 putchar() 과 fputc()가 있다.

#include <stdio.h>
int putchar(int c);
int fputc(int c, FILE * stream);
// 함수 호출 성공시 쓰여진 문자 정보, 실패시 EOF반환

putchar() 함수는 인자로 전달된 문자정보를 stdout 표준 출력 스트림으로 전송하여 모니터로 출력하는 함수이다. 

fputc()함수는 문자를 전송한다는 측면은 putchar()함수와 동일하지만 문자를 전송할 스트림을 지정할 수 있어 파일을 대상으로도 데이터를 저장할 수 있다. fputc() 함수의 두 번째 매개변수 stream은 문자를 출력할 스트림을 지정하는데 사용된다.

 

문자 입력 함수

키보드로 부터 하나의 문자을 입력 받을 때 일반적으로 사용하는 함수는 getchar() 과 fgetc()가 있다.

#include <stdio.h>
int getchar(void)
int fgetc(FILE * stream);
// 파일의 끝에 도달하거나 함수 호출 실패 시 EOF반환

getchar() 함수는 stdin으로 표현되는 표준 입력 스트림으로부터 하나의 문자를 입력받아 반환하는 함수이다. 

fgetc() 함수는 문자를 입력받을 스트림을 지정할 수 있다.

 

#include <stdio.h>

int main(void)
{
    int ch1, ch2;

    ch1 = getchar();    // 문자 입력
    ch2 = fgetc(stdin); // 엔터키 입력

    putchar(ch1);       // 문자 출력
    fputc(ch2, stdout); // 엔터키 출력
    return 0;
}

위 코드에서 문자를 int 형 변수로 선언한 이유는 getchar() 함수와 fgetc()함수의 반환형이 int 이기 때문이다. 

 

문자 입출력에서의 EOF

EOF는 End Of File의 약자로 파일의 끝을 표현하기위해 정의된 상수이다. 따라서 EOF가 반환되면 파일의 끝에 도달하여 더 이상 읽을 내용이 없음을 의미한다. 키보드를 대상으로 하느 fgetc() 와 getchar() 함수는 다음의 경우중 하나가 만족되었을 때 EOF를 반환한다.

  • 함수 호출 실패
  • Windows에서 CTRL + Z, Linux에서 CTRL + D 키가 입력되었을 경우
#include <stdio.h>

int main(void)
{
    int ch;

    while (1)
    {
        ch = getchar();
        if(ch == EOF)
            break;
        putchar(ch);
    }
    return 0;
}

위 코드를 실행하면 문자 입출력이 반복되고 EOF를 반환하는 CTRL + D 키를 입력하면 프로그램이 종료된다.

 

위 함수들의 반환형이 int 이고, int형 변수에 문자를 담는 이유는 EOF 는 -1로 정의된 상수인데, 반환형이 char 이라면 처리하는 컴파일러에 따라 char을 unsigned char로 처리하는 경우가 있어 -1을 양의 정수로 형변환 하는 경우가 발생 할 수 있다. 그래서 어떤 컴파일러라도 -1 을 유지하기 위해 반환형을 int 로 정의해둔것이고, 반환형이 int 이기 때문에 int 형 변수에 값을 저장해야 한다.

'Language > C, C++' 카테고리의 다른 글

[C++] 입출력  (0) 2021.08.21
[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 스트림  (0) 2021.01.15
[C] main 함수로의 인자 전달  (0) 2021.01.09
[C] void 포인터  (0) 2021.01.08

프로그램 상에서 키보드로 입력받아 모니터로 출력할 때 이를 연결해 주는 매개체를 스트림 이라고 한다.

스트림은 입력 장치에서 실행중인 프로그램으로 연결시켜 주는 입력 스트림과, 프로그램에서 출력장치로 연결시켜주는 출력 스트림이 있다.

 

콘솔 입출력을 위한 입력 스트림과 출력 스트림은 프로그램이 생성되면 자동으로 생성되고, 프로그램이 종료되면 자동으로 종료되기 때문에 직접 요구할 필요는 없지만, 파일과의 연결을 위한 스트림의 생성은 직접 요구해야 한다.

 

콘솔 입출력을 위한 스트림은 기본적으로 제공되는 표준 스트림(standard stream)이다. 표준 스트림은 다음과 같다.

  • stdin : 표준 입력 스트림 : 키보드 대상으로 입력
  • stdout : 표준 출력 스트림 : 모니터 대상으로 출력
  • stderr : 표준 에러 스트림 : 모니터 대상으로 출력

'Language > C, C++' 카테고리의 다른 글

[Project H4C] C언어 코딩도장(6)  (0) 2021.03.02
[C] 문자 단위 입출력 함수  (0) 2021.01.15
[C] main 함수로의 인자 전달  (0) 2021.01.09
[C] void 포인터  (0) 2021.01.08
[C] 함수 포인터  (0) 2021.01.08

main 함수는 다음과 같이 정의할 수도 있다. 

int main(int argc, char * argv[]) { ... }

프로그램 실행시 main함수로 인자를 전달할 수 있고, main 함수도 인자를 전달받을 수 있도록 한 것이다. 

#include <stdio.h>

int main(int argc, char * argv[])
{
    int i = 0;
    printf("전달된 문자열의 수 : %d\n", argc);
    
    for(i = 0; i < argc; i++)
        printf("%d 번째 문자열 : %s\n", i+1, argv[i]);
    
    return 0;
}
/* 
input : 
# ./argcargv I love you

output:
전달된 문자열의 수 : 4
1 번째 문자열 : ./argcargv
2 번째 문자열 : I
3 번째 문자열 : love
4 번째 문자열 : you
*/

위 코드와 같이 main함수를 구성하면 프로그램을 실행할 때 인자를 전달 할 수 있다. argv는 char형 더블 포인터 변수이다. 

인자 전달 과정에서 공백은 문자열을 나누는 기준이 된다. 인자로 전달된 문자열들이 배열에 묶여 main 함수의 두 번째 인자로 전달이되고, 첫 번째 인자는 문자열의 수가 전달이 된다.

#include <stdio.h>

int main(int argc, char * argv[])
{
    int i = 0;
    printf("전달된 문자열의 수 : %d\n", argc);

    while(argv[i] != NULL)
    {
        printf("%d 번째 문자열 : %s\n", i+1, argv[i]);
        i++;
    }

    return 0;
}
/* 
input : 
# ./argvNull "I Love You"

output : 
전달된 문자열의 수 : 2
1 번째 문자열 : ./argvNull
2 번째 문자열 : I Love You
*/

위 코드를 통해 전달받은 배열의 마지막에 NULL이 삽입됨을 확인할 수 있다. 또한 큰 따옴표로 묶으면 공백을 포함하는 문자열을 인자로 전달할 수 있다.

'Language > C, C++' 카테고리의 다른 글

[C] 문자 단위 입출력 함수  (0) 2021.01.15
[C] 스트림  (0) 2021.01.15
[C] void 포인터  (0) 2021.01.08
[C] 함수 포인터  (0) 2021.01.08
[C] 2차원 배열의 포인터  (0) 2020.12.30

다음과 같이 선언되는 포인터를 void형 포인터라 한다.

void * ptr;

void형 포인터 변수는 함수의 주소를 포함한 어떠한 변수의 주소값이든 담을 수 있다.

#include <stdio.h>

void SimpleFunc(void)
{
    printf("Simple function\n");
}

int main(void)
{
    int num = 20;
    void *ptr;
    
    ptr = &num;
    printf("%p\n", ptr);

    ptr = SimpleFunc;
    printf("%p\n", ptr);
    return 0;
}
/* output : 
0x7ffee8607858
0x1075fbf00
*/

위 코드처럼 어떤 형태의 변수의 주소값이든 저장할 수 있다. 그러나 void형 포인터 변수는 형(type)에 대한 정보가 없기 때문에 포인터 연산, 값의 변경, 참조가 불가능하다.

'Language > C, C++' 카테고리의 다른 글

[C] 스트림  (0) 2021.01.15
[C] main 함수로의 인자 전달  (0) 2021.01.09
[C] 함수 포인터  (0) 2021.01.08
[C] 2차원 배열의 포인터  (0) 2020.12.30
[C] 삼중 포인터  (0) 2020.12.28

+ Recent posts