반응형

str1은 문자열이 저장된 배열이다. 문자 배열이다 즉 변수성향의 문자열이다.

str2은 문자열의 주소 값을 저장한다. 자동 할당된 문자열의 주소 값을 저장한다.

따라서 상수성향의 문자열이다.

 

아래의 코드를 보면

 

str1은 다른 문자열 할당이 되지 않는다.

str2는 문자열주소를 할당받을 수 있어서

다른 문자열을 할당할 수 있다.

 

변수 성향의 str1에 저장된 문자열의 문자는 변경이 가능

상수 성향의 str2에 저장된 문자열의 문자는 변경이 불가능

char str1[] = "My String";
char* str2 = "Your String";
printf("%s %s \n", str1, str2);	// My String Your String 출력

//str1 = "Our String";	컴파일 오류발생
str2 = "Our String";
printf("%s %s \n", str1, str2);	// My String Our String 출력

str1[0] = 'X';
//str2[0] = 'X';	컴파일 오류 발생
printf("%s %s\n", str1, str2);	// Xy String Our String 출력

 

아래의 그림에서 WhoAreYou라는 함수는 char형 포인터를 매개변수로 받고

인자로 "Hong"을 넣어줍니다. "Hong"은 문자열인데 문자열은 char형 배열과 같습니다.

배열은 포인터와 호환이 되기에 인자로 전달받을 수 있습니다!

 

이미지 출처 : 윤성우의 열혈 C 프로그래밍

 

반응형

 

반응형

'C' 카테고리의 다른 글

회문  (0) 2023.05.17
내림차순 정렬  (0) 2023.05.17
포인터 대상의 const 선언  (0) 2023.05.15
Call by value와 Call by reference의 차이점  (0) 2023.05.15
포인터와 함수 이해  (0) 2023.05.15
반응형
포인터 변수 const선언

포인터 변수에 const선언을 하면 포인터 변수를 사용해서 변수에 저장된 값을

변경하는것을 허용하지 않게 됩니다.

 

그러나 변수 num에 저장된 값 자체의 변경이 불가능한 것은 아닙니다.

다만 포인터변수를 통해서 변경을 허용하지 않을 뿐입니다.

 

아래의 코드는 그에 대한 예시입니다.

int num = 10;
const int* ptr = #
*ptr = 30;	// 컴파일 에러
num = 40;	// 컴파일 성공

 

 

 

포인터 변수의 상수화

아래의 코드에서는 포인터 변수에 저장된 값을 상수화 한다는 의미입니다.

즉 포인터변수에 저장된 값이 변경이 불가능한 것입니다.

포인터 변수가 가리키는 대상의 변경을 허용하지 않는다는 뜻입니다.

 

int num1 = 10;
int num2 = 20;
int* const ptr = &num1;
ptr = &num2;	// 컴파일 에러
*ptr = 40;	// 컴파일 성공

const int* const ptr = #	// 가능

 

const int* const ptr = # 이렇게 사용이 가능합니다.

포인터 변수 자체를 const선언 포인터 변수에 저장된 값 또한 const를 붙여 상수화가 가능합니다.

 

이렇게 되면 포인터 변수를 통해서 저장된 값을 변경하지 못하고 포인터 변수가

가리키는 대상의 변경또한 허용하지 않습니다.

const int* const ptr = #	// 가능

 

 

const선언 의미

const선언은 추가적인 기능을 제공하기 위한 것이 아니라,

코드의 안전성을 높이기 위한 것입니다.

따라서 이러한 const의 선언을 소홀하기 쉬운데,

이러한 const의 선언과 같이 코드의 안전성을 높이는 선언은

매우 중요합니다!

 

값이 바뀌면 안되는 변수를 선언할때 const는 필요해집니다!

 

반응형

 

반응형

'C' 카테고리의 다른 글

내림차순 정렬  (0) 2023.05.17
상수 형태의 문자열을 가리키는 포인터  (0) 2023.05.16
Call by value와 Call by reference의 차이점  (0) 2023.05.15
포인터와 함수 이해  (0) 2023.05.15
포인터 배열  (0) 2023.05.15
반응형
Call by value와 Call by reference의 차이점

함수를 호출할 때 단순히 값을 전달하는 형태를 Call-by-value(값에 의한 호출)라 합니다.

메모리 접근에 사용되는 주소 값을 전달하는 형태는 Call-by-reference(참조에 의한 호출)라 합니다.

둘을 구분하는 기준은 함수의 인자로 전달되는 대상에 있습니다.

 

값을 전달하는 함수 호출 : Call-by-value

밑의 코드처럼 사용되는 것이 값에 의한 호출입니다.

Call-by-value 함수는 외부에 선언된 변수에 접근이 불가능합니다.

void CallByValue(int value);

 

밑의 코드는 Call-by-value의 전형적인 잘못된 예시입니다.

Swap 함수를 거치면 두개의 변수의 값이 바뀔것 같지만 실상은 그렇지 않습니다.

void Swap(int num1, int num2)
{
    int temp = num1;
    num1 = num2;
    num2 = temp;
    printf("num1 : %d num2 : %d \n", num1, num2);
}

int main()
{
    int num1 = 10;
    int num2 = 20;
    printf("Swap전 num1 : %d num2 : %d \n", num1, num2);

    Swap(num1, num2);	// 이 부분에서 Swap이 될 것으로 예상
    printf("Swap후 num1 : %d num2 : %d \n", num1, num2);
    return 0;
}

위의 코드를 실행하면 아래의 출력값이 나옵니다.

값에 의한 호출은 이렇게 함수에서 인자의 값을 main함수로 전달해주지 못합니다.

이것은 Swap함수안의 변수들은 main함수의 변수들과 아예 다른 변수입니다.

main함수의 변수들은 데이터가 계속 남아있지만, Swap함수의 변수들의 메모리는

Swap함수가 종료하면서 저절로 해제됩니다.

즉 main함수의 변수값에 영향을 주지 않습니다.

 

주소값을 전달하는 함수 호출 : Call-by-reference

C언어는 Call-by-reference를 지원하지 않는다!

C++에는 Call-by-reference를 지원한다

 

Call-by-refernce는 직접적으로 해당 메모리를 참조하여 그 값을 쓰지만

C에서는 함수에서 인자로 전달받은 메모리의 값을 매개변수 메모리에 복사하여 사용하는 것입니다.

즉 메모리 공간이 다릅니다. 이는 곳 값에 의한 호출을 의미합니다.

하지만 값은 바뀝니다! 편의상 구분을 위해 여기서는 참조에 의한 호출이라고 명하겠습니다

 

밑의 코드는 C언어의 참조에 의한 호출입니다.

참조에 의한 호출에 전형적인 예시인 Swap함수를 구현한 것입니다.

void Swap(int* ptr1, int* ptr2)
{
    int temp = *ptr1;
    *ptr1 = *ptr2;
    *ptr2 = temp;\
    printf("num1 : % d num2 : % d \n", *ptr1, *ptr2);

}


int main()
{
    int num1 = 10;
    int num2 = 20;
    printf("Swap전 num1 : %d num2 : %d \n", num1, num2);

    Swap(&num1, &num2);
    printf("Swap후 num1 : %d num2 : %d \n", num1, num2);
    return 0;
}

위의 코드를 실행하면 아래의 출력값이 나옵니다.

값에 의한 호출과 달리 swap함수후에는 값이 변하게 됩니다.

main함수의 변수의 메모리를 참조하기에 swap함수에서 값을 바꿔도

그 값이 main함수의 변수에도 영향을 미칩니다.

 

 

 

scanf함수 호출 시 &연산자를 붙이는 이유

변수 num앞에 &연산자를 붙이는 이유는

scanf함수 내에서 외부에 선언된 변수 num에 접근하기 위해서는 num의

주소 값을 알아야 한다. 그래서 scanf함수는 변수의 주소 값을 요구합니다.

 

배열 이름 str 앞에 &연산자를 붙이지 않는 이유는

str은 배열의 이름이고 그 자체가 주소 값이기 때문에 &연산자를 붙이지 않습니다.

str을 전달하는것은 scanf함수 내부로 배열 str의 주소 값을 전달하는 것과 같습니다.

int num;
scanf("%d", &num);

char str[50];
scanf("%s", str);

 

 

반응형

 

반응형

'C' 카테고리의 다른 글

상수 형태의 문자열을 가리키는 포인터  (0) 2023.05.16
포인터 대상의 const 선언  (0) 2023.05.15
포인터와 함수 이해  (0) 2023.05.15
포인터 배열  (0) 2023.05.15
포인터 연산  (0) 2023.05.15
반응형
배열을 함수의 인자로 전달하는 방식

배열의 주소 값을 전달하는 방식을 사용한다.

 

밑의 코드처럼 배열을 선언하고 함수의 인자로 배열을 넘깁니다.

그리고 매개변수에는 배열과 같은 자료형인 int형 포인터 변수를 담습니다.

이렇게 포인터 변수를 통해서도 배열의 형태로 접근이 가능합니다!

void Func(int*);

int main()
{
	int arr[3] = {10, 20, 30};
   	Func(arr);
}

void Func(int* pointer)
{
	printf("%d %d %d", pointer[0], pointer[1], pointer[2]);	// 10 20 30 출력
}

 

위의 코드처럼 포인터로 배열을 인자로 받을수 있지만, 아래의 코드처럼 배열을 인자로 전달받을 수 있습니다.

포인터의 선언 int* ptr을 int ptr[]으로 대체할 수는 없습니다.

void Func(int pointer[])
{
	printf("%d %d %d", pointer[0], pointer[1], pointer[2]);	// 10 20 30 출력
}

 

아래의 코드에서 Add함수는 인자로 배열을 받습니다.

배열은 포인터와 호환이 되기에 매개변수로 배열이 넘어갈 수 있습니다.

Add함수는 그 배열의 원소에 add변수를 더해주는 함수입니다.

참조값이기에 main함수의 배열의 원소에도 이 더해진 값이 적용이 됩니다.

 

그렇다면 인자로 길이를 넘겨받는 이유는 뭘까요?

인자로 받지 않는 대신에 Add함수에서 sizeof(param) / sizeof(int)로

길이를 구할 수 있지 않을까? 생각할 수도 있습니다.

 

하지만 그 결과는 다릅니다.

포인터변수를 sizeof연산자로 값을 구하면

해당 변수의 타입에 대한 크기가 나오는게 아니라

포인터 변수 의 크기가 나오게 됩니다.

포인터 변수의 크기는 시스템의 주소값 크기에 따라 다릅니다.

즉 int형 포인터 변수여도 시스템의 주소값 크기가 나온다는 뜻입니다.

 

그래서 인자로 넘겨받은 param에 sizeof연산자를 붙이면 우리가 기대한

int형 타입의 크기인 4byte가 아닌 시스템의 주소값 크기가 나오게 됩니다.

즉, main함수에서 길이를 구하고 인자로 넘겨주는 방법밖에 없습니다!

// 매개변수로 넘어온 배열의 값에 변수add를 더해주는 함수
void Add(int * param, int len, int add)
{
	for(int i = 0; i < len; i++);
    	param[i] += add;
}

int main()
{
    int arr[3] = {1,2,3};
    Add(arr, sizeof(arr) / sizeof(int), 1);
    for(int i = 0; i < len; i++)
    {
    	printf("%d ", arr[i]);	// 2 3 4 출력
    }
}

 

 

반응형

 

반응형

'C' 카테고리의 다른 글

포인터 대상의 const 선언  (0) 2023.05.15
Call by value와 Call by reference의 차이점  (0) 2023.05.15
포인터 배열  (0) 2023.05.15
포인터 연산  (0) 2023.05.15
포인터와 배열의 관계  (0) 2023.05.15
반응형
포인터 배열 선언과 초기화

포인터 배열의 선언과 초기화는

일반 배열의 선언과 별반 차이가 없다

int형이면 int * 변수명[배열의 길이] ;로 선언이 가능하다

int num1 = 10, num2 = 20, num3 = 30;
int* ptr[3] = {&num1, &num2, &num3};

for(int i = 0; i < 3; i++)
{
	printf("%d \n", *ptr[i]);	// 10 20 30 출력
}

 

 

 

문자열을 저장하는 포인터 배열

포인터는 배열처럼 사용할 수 있다.

배열은 문자의 배열 즉, 문자열을 사용할 수 있는데

이는 포인터 또한 이를 다룰수 있다는 뜻입니다.

포인터 배열에 문자열의 배열을 저장할 수 있습니다.

char * strArr[3] = {"Pointer", 'is", "God"};

for(int i = 0; i < 3; i++)
{
	printf("%s \n", strArr[i]);	// Pointer  is  God 출력
}

 

반응형
반응형

'C' 카테고리의 다른 글

Call by value와 Call by reference의 차이점  (0) 2023.05.15
포인터와 함수 이해  (0) 2023.05.15
포인터 연산  (0) 2023.05.15
포인터와 배열의 관계  (0) 2023.05.15
문자열에 대해서!  (0) 2023.05.15

+ Recent posts