배열
여기서 int a[5]라고 했는데 이는
'int a1, a2, a3, a4, a5;' 같은경우는 직접 쓸 수 있지만
만약 a100까지 정의해야된다면 사실상 힘든 일이므로 간단하게 표현하기위해
int a[100]라고 하면 int a[1]~int a[100]까지 표현하기 쉽기 때문에 이렇게 쓴다.
a[0] = 2;
a[1] = 3;
a[2] = 7;
a[3] = 6;
a[4] = 8;
라는 표현도 숫자가 커지면 표현하기 힘들기 때문에
int a[5] = { 2, 3, 7, 6, 8 }; 로 적으면 위와 똑같은 뜻을 가지므로 더 편하게 표현할 수 있다.
또한 이렇게 표현할 때 int a[5]에서 5를 생략하여
int a[] = { 2, 3, 7, 6, 8 }; 로 간단하게 표현할 수 있다
출력하는 것도
printf("%d\n", a[0]);
printf("%d\n", a[1]);
printf("%d\n", a[2]);
printf("%d\n", a[3]);
printf("%d\n", a[4]);
이렇게 일일이 직접 다 적기보단,
이렇게 간단하게 표현할 수 있다
결과적으로 이렇게 간단하게 표현할 수 있게된다~!
근데 a의 개수가 바뀌었을 때 'for (int i = 0; i <= 4; i++)'에서 'i <= 4'도 수정을 해줘야 되는데 이게 고치는게 번거로우니 sizeof()를 이용하여 보도록 하자!
앞서 sizeof()에 대해 공부한 적이 있는데 'sizeof()'란 소괄호 안에 들어있는게 몇바이트인지 출력해주는 함수라고 공부했었다.
그렇다면 int()는 4바이트였으므로 sizeof(int) = 4, int a[n]일 경우 sizeof(a) = (4 x n)바이트라고 볼 수 있다
따라서 'for (int i = 0; i < sizeof(a) / sizeof(int) ; i++)' 라고 적어주면
'for (int i = 0; i < n ; i++)'이 되므로 for()을 수정할 필요가 없어지고, 더 간단해진다.
완성본!
입력받은 숫자들을 거꾸로 출력
여기서 주의할점!
이렇게 int arr[n]을 쓸 경우 arr가 n을 만들어야 되는데 n은 사용자가 입력하는 값에 따라 변하는 변수이므로 만들지 못하여 저렇게 쓰면 안 되고 넉넉하게 int arr[1000]을 설정해두고 채우는 곳만 채우고 빈 곳은 비게 냅두는 방식으로 식을 써야된다.
이렇게 표현할 수 있다~
최대/ 최소
1. 최대
이번에도 arr[100]까지 넉넉하게 잡아두고 먼저 입력할 숫자의 개수를 scanf()를 통하여 받는다
그리고 for문을 통하여 arr[0] ~ arr[n-1]까지 스캔을 받고
최댓값을 arr[0]이라고 저장한다!
그리고 if문을 통해 arr[0]인 max가 arr[i]보다 작을경우 if문이 실행되어 max = arr[i]가 되고, 실행 하다가 max > arr[i]가 되는 경우엔 max를 출력하게 된다~!
그래서 최댓값이 나오게 된다!
2. 최소
최소인 경우는 최대인 경우에 부등호만 바꿔주면 된다!
'ctrl + h'를 이용하여 max를 min으로 바꿔주자
이렇게 최솟값이 나오게 된다!
3. 짝수의 개수
앞서 설명한 최대최소와 마찬가지로
여기까진 똑같이 써주고, 'cnt = 0'이라고 저장했을때
arrr[i] % 2 == 0이 될 때만 cnt++를 시행시켜 짝수의 개수를 늘려가고
printf("%d", cnt);를 해주어 cnt의 개수를 출력시킨다.
2차원 배열(배열의 배열)
a[3][4] | 0 | 1 | 2 | 3 |
---|---|---|---|---|
0 | 1 | 2 | 3 | 4 |
1 | 5 | 6 | 7 | 8 |
2 | 9 | 10 | 11 | 12 |
이처럼 2차원 배열은
arr[행][열] 로 표현할 수 있다.
파스칼의 삼각형
문자열 출력
문자열을 출력할 땐
'char'를 쓰고 arr[] = { 'h', 'e', 'l', 'l', 'o', '\0' } 처럼 이렇게 써야 되는데 이럴 필요 없이
'char arr[] = "hello, world!";'라고 큰따옴표를 이용하여 간단히 쓸 수 있다!
출력할때도 section 1에서 배운 '%s' 을 이용하여 문자열을 출력 할 수 있어
'printf("%s\n", arr);' 로 바로 써줄 수 있게 된다!
여기서 왜 "hello, world!"의 배열의 크기가 왜 13이 아니고 14일까?
이유는 'char arr[] = hello"'를 다시 표현 하면 'char arr[] = {'h', 'e', 'l', 'l', 'o', '\0'}'가 되어 NUL문자(\0)가 붙기 때문이다!
NUL문자는 문자열의 끝을 나타내는 역할을 한다.
scanf()는 문자열을 입력받을 때 "%s", 뒤에 &를 붙이지 않고 바로 scanf("%s", s)라고 적어줘야 된다!
자세한건 포인터를 할때 다루도록!
이렇게 실행을 해보면!
이렇게 입력한 그대로 출력이 된다.
<string.h>
strlen()
문자열의 함수를 선언하기 위해선
#include <stdio.h>
#include <string.h>
이 두개를 선언해줘야 된다!
이렇게
배열의 크기를 구하기 위해 'sizeof(str) / sizeof(char) - 1;'를 하면 5가 나오겠지만 만약에
이렇게
'char str[100];' 을 해버리면 배열의 크기는 어떤문자를 스캔하던 상관없이 99만 나오게 될 것이다.
그럼 어떻게 할까!
길이를 저장하는 변수 'len'을 만들고
'len = strlen(str);' 을 해주면 되는데
이 'strlen'이 <string.h>에 들어있는 함수이다!
strlen은 함수가 반환하는 값이 있는데 그 값이 바로 str의 길이이다!
그걸'len = strlen(str);'을 이용하여 len에 저장한다는 의미를 가지고 있다.
그래서 그걸 출력을 해주면
이렇게 된다.
strcpy()
char str1[] = "hello";
char str2[100];
이 있을때
'strcpy()'는 str1[]의 문자열 전체를 str2[] (다른 문자열)로 복사를 시키는 것이다.
순서는 strcpy(str2, str1), 즉 복사받아야 할 문자열을 먼저 쓰고 원본 문자열을 뒤에 쓰면 된다.
그리고 printf()를 이용하여 str2를 출력해주면
이렇게 str1의 문자열이 복사되어 str2의 문자열로 저장되었다!
strcat()
'strcat()'는 특정한 문자열 뒤에 또다른 문자열을 덧붙여주는 함수이다.ㅈ
이렇게 먼저
'char str[100] = "hello"; '를 입력해주고
'strcat(원래문자, "덧붙일 문자");' 로 입력해주고 printf()로 출력을 해주면
이렇게 hello 뒤에 world!가 추가된다!
이때 strcat은 뒤에 계속 덧붙일 문자를 쓸 수 있다.
설명 예시
톡을 하는데 갑자기 strcat가 생각나서 프로그램을 만들어봤다
한글도 되는 것을 알 수 있다!
strcmp()
'strcmp()'는 두 문자를 비교하는 함수인데 같은지 비교할 뿐만 아니라 누가 사전순으로 먼저 있는지 확인할 수도 있다
'int cmp = strcmp(str1, str2);'에서
첫번째 문자열이 두번째 문자열보다 사전순으로 앞쪽에 있으면 '-1'을 반환하고
첫번째 문자열이 두번째 문자열보다 사전순으로 뒷쪽에 있으면 '1'을 반환하고
첫번째 문자열과 두번째 문자열이 사전순으로 같이 있다면 '0'을 반환하게 된다.
이렇게 str1이 str2보다 사전순으로 앞쪽에 있으므로 '-1'을 반환하게 되어
'printf("%5\n", cmp);' 를 해주면 -1이 출력되어지는 것을 볼 수 있다.
'str1'과 'str2'의 순서를 바꿨을때 / 사전순이 같을 때
pointer
포인터 : 변수의 주소를 저장하는 변수
포인터를 선언할 때 다른 변수들과 구별해주기 위해
#include <stdio.h>
int main() {
int a = 20;
int* ptr_a;
}
처럼 'int a'를 가리키는 포인터를 쓰고 싶을 때 'int *ptr_a;'라고 선언해준다.
int뿐만아니라 'char c = 'h'; '일때도 마찬가지로 'cha *ptr_c'라고 선언해주면 된다.
따라서 포인터를 선언할 때
내가 가리킬 변수의 형을 먼저 쓰고 *을 쓰고 포인터의 이름을 적어주면 된다.
그리고 'ptr_a = &a;'를 해줌으로써 'ptr_a'에 'a'의 주솟값을
저장해주면 된다.
참고 : '&a'일 경우 'a'의 주솟값을 나타내 주는 것이고 '&main'도 main의 주솟값을 나타내는 것이다.
'&'가 나오면 주솟값을 말하는 것이다!
이렇게 'a'의 주솟값이 계속 바뀌는 이유는 실행시킬 때마다 Ram의 상황이 조금씩 달라지기 때문에 'a'의 위치가 바뀌어 실행할때마다 변수의 주솟값이 계속 변하게 된다.
여기서 마지막 'printf("ptr_a가 가리키는 변수의 값 : %d\n", *ptr_a);' 에 있는 '*'의 의미는 좀 다르다.
'int* ptr_a;'의 '*'은 'ptr_a'가 포인터라는 것을 컴퓨터에게 알려주기 위해서 썼고,
'*ptr_a'의 '*'은 선언이 아니고 'ptr_a'가 가리키는 변수 자체를 사용하고 싶을 때 쓴다.
a의 주솟값은 19920964인데
ptr_a = &a를 써서 ptr_a에 a의 주솟값을 저장했으므로 ptr_a에 저장된 값도 19920964가 된다.
그리고 ptr_a가 지금 a의 주솟값을 a에 저장함으로써 a라는 변수를 가리키고 있는 상황이 됐고, 그 상황에서 ptr_a가 가리키고 있는 변수의 값을 a의 변수의 값이 되어 20이 되는 것이다.
이번엔 a, b를 둘 다 선언해보고,
ptr이 포인터라는 것도 선언해주고,
포인터에 a의 주솟값을 저장하고 ptr이 가리키는 변수를 사용하고 싶기 때문에
'printf("ptr이 가리키는 변수에 저장된 값 : %d\n", *ptr);'라고 써주면 된다.
두번째로, ptr에 b의 주솟값을 저장하고싶기 때문에 ptr = &b;라고 쓰고 밑에는 똑같이
'printf("ptr이 가리키는 변수에 저장된 값 : %d\n", *ptr);'라고 해주면 된다.
그럼 이렇게 나오게 된다.
이번엔 ptr에 a를 저장하고 a의 값을 출력하면 10이 나오게 된다.
그리고 포인터가 가르키는 값에 20을 저장('*ptr = 20;')을 하고
'printf("a의 값 : %d\n", a);'를 해주면
이러한 결과가 나오게 된다.
포인터를 가리키는 포인터를 선언하는 방법은 **을 쓰면 된다!
int** ptr_ptr;을 해주면
ptr_ptr은 ptr의 주솟값을 저장하게 되어
int** ptr_ptr;
ptr_ptr = &ptr;
로 쓸 수 있다.
따라서 a는 10이 저장되어있고, &a(a의 주솟값)는 15727932가 되고,
ptr안에는 a의 주솟값을 저장했기때문에 ptr도 15727932가 되고,
ptr이라는 포인터 역시 변수이기 때문에 &ptr(ptr의 주솟값)은 15727920가 되고,
ptr_ptr이라는 포인터에 &ptr의 주솟값을 저장했기 때문에 ptr_ptr도 15727920이 된다.
만약에
printf("*ptr_ptr = %d\n", *ptr_ptr);
을 하게되면 *ptr_ptr이 가리키는게 ptr이기때문에 ptr의 주솟값인 '15727932'가 출력된다.
그리고
printf("**ptr_ptr = %d\n", **ptr_ptr);
을 하게되면 *ptr_ptr이 가리키는 것은 ptr, *ptr이 가리키는 것은 a이기 때문에 '10'이 출력된다.
int a = 10;
int *ptr_a = &a; : 포인터를 선언하고 그 포인터에 저장된 값 자체를 a의 주소로 바꿀거다.
int a = 10;
*ptr_a = 20; : 포인터 a가 가리키는 변수, 죽 a에 20을 저장하겠다.
왜 'ptr_a'에 1을 더했는데 4가 늘어났을까?
정답은 int때문이다.
전에 int 는 4바이트라고 하였는데, int a이므로 a는 □□□□ 처럼 4칸으로 이루어져있는데
ptr_a는 □□□□중에 어딘가가 될 것이다.
[□□□□][□□□□]라고 둘 때
첫번째 대괄호에 첫번째 칸이 'ptr_a'라고 두자, 그런데 'ptr_a + 1'은 그 첫번째 대괄호에 있어야 될 것 같지만,
'ptr_a + 1'은 두번째 대괄호 안에 존재하게 된다.
따라서 C언어 에서 포인터에 특정한 값을 더하게 되면 그 포인터가 가리키고 있는 형의 그 크기만큼을 더해야 된다.
그래서 'ptr_a' 의 값이 19921996이 나왔을 때
'ptr_a + 1'의 값이 19922000이 된 것이다.
이렇게 실제 메모리상에서도
arr[0] = 12516712일때,
arr[0] = 12516716이 되고
i가 1씩 커질때마다 arr[i]의 주솟값은 4씩 커진다는 것을 알 수 있다.
또한,
'printf("arr의 값 : %d\n", arr);'
를 사용하여 arr를 출력하면 뭐가 나올지 실행해보면
이렇게 'arr' 와 '&arr[0]'이 같음을 알 수 있다.
결국 'arr + 1'과 &arr[0] + 1'이 같으므로
arr[i] | i = 0 | i = 1 | i = 2 |
---|---|---|---|
100 | 104 | 108 |
'int arr;'을 해줬으니 'arr + 1'을 하면 'arr'에서 주솟값이 4만큼 늘어나고,
앞서 봤듯이 '&arr[0] + 1'을 하면 '&arr[0]'에서 주솟값이 4만큼 늘어나기 때문에
'arr + 1'과 &arr[0] + 1'이 같음을 알 수 있다.
따라서 'arr + i'는 '&arr[i]'와 같음을 알 수 있다.
'arr + i'는 '&arr[i]'와 같음
1.
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]);
}
=> 'arr[i]'의 값을 'arr[0]'부터 'arr[9]'까지 출력해라.
2.
for (int i = 0; i < 10; i++) {
printf("%d ", *(arr + i));
}
=> *(arr + i) == *(&arr[i])이므로 'printf("%d ", *(&arr[i]));'에서
'*(&arr[i])'는 arr[i]값의 주소값에 들어있는 값을 불러와라. => arr[i]
=> 'arr[i]'의 값을 'arr[0]'부터 'arr[9]'까지 출력해라.
3.
for (int* ptr = arr; ptr < arr + 10, ptr++) {
printf("%d", *ptr);
}
=> 포인터ptr을 선언, 초기값을 '&arr[0]'로 저장,
' printf("%d", *ptr);' => 'printf("%d ", *(&arr[i]));', 2와 같음
따라서 결과값은
이렇게 다 같다.
참고로
'int *ptr = arr;'
을 했을때 ptr이라는 포인터에 arr를 대입한 것이므로, arr대신 ptr을 넣고 출력해도 된다.
=> arr[i] == *(arr + i) == *(ptr + i) == i[ptr]
1. ptr == &ptr[0]
2. *ptr == ptr[0]
3. ptr + 1 == ptr에 sizeof(*ptr)을 더한 값
이와같이
printf("&arr = %d\n", &arr);
printf("&arr + 1 = %d\n", &arr + 1);
의 경우,
'printf("&arr = %d\n", &arr);'는 arr[0]의 주솟값인'15727356'을 출력하면 되지만,
'printf("&arr + 1 = %d\n", &arr + 1);'에서 '&arr + 1'은
'ptr + 1 == ptr에 sizeof(*ptr)을 더한 값'에서 ptr을 &arr로 바꿔주면
'&arr + 1 == &arr에 sizeof(arr)을 더한 값'이 되므로
여기서 sizeof(arr)는 12이기때문에 arr[0]의 주솟값에 12를 더한 값인 '15727368'이 되는 것이다.
배열 포인터
2차원 배열과 배열 포인터
sizeof(arr) : arr의 전체 사이즈 => 24
sizeof(arr[0]) : arr의 0행의 사이즈 => 12
sizeof(arr[0][0]) : arr의 0행, 0열의 사이즈 => 4
&arr : arr[0][0]의 주솟값
&arr[0] : arr[0][0]의 주솟값
&arr[0][0] : arr[0][0]의 주솟값
1. ptr == &ptr[0]
2. *ptr == ptr[0]
3. ptr + 1 == ptr에 sizeof(*ptr)을 더한 값
1. ptr[i] == arr[i]
2. ptr[i][j] == arr[i][j]
ptr == ar
이차원 배열을 포인터로 치환하고싶다면 이차원 배열의 한 행을 가리킬수있는 배열포인터를 만들어 그 배열포인터에 arr를 집어넣으면 된다.
위의 예시를 설명하자면,
int arr[2][3] = { {1, 2, 3}, {4, 5, 6} };는
arr[2][3] = { {1, 2, 3}, {4, 5, 6} } | 0 | 1 | 2 |
---|---|---|---|
0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 |
이러한 이차원을 가지는데
int (*ptr)[3] = arr; 선언함으로써, 이차원배열의 한 행을 가리킬 수 있는 ptr[3]을 선언하였고,
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", ptr[i][j]);
}
printf("\n");
}
이 for문은
이런 과정을 통해
라는 결과값이 나오게 된다.
이 이차원 배열 포인터는
이런 원리로 인해
이런 결과값이 나오게 된다.
포인터 배열
배열 포인터 : 배열을 가리키는 포인터
포인터 배열 : 포인터들이 배열
포인터를 쓸 땐
'int (*ptr)[4]'가 아니라' int *ptr[4]'로 써주면 된다.
이건 arr[4]가 □□□□
ptr[4]가 □□□□ 로 있을 때
ptr[0]엔 arr[0]의 주솟값이 들어가고, ... , ptr[3]엔 arr[3]의 주솟값이 들어가서 일대일 대응이 되는 것이다.
for문에서
'printf("%s\n", strings[i]);'를
'printf("%s\n", &strings[i][0]);'
로 바꿔주면
&strings[i][0]은 i가 0일때 strings[0][0]의 주솟값을 출력해야 되는데 이 주솟값은 &strings[0]을 출력하는 것과 같으므로 0행에 있는 문자들이 다 출력되는 것을 볼 수 있다.
여기선 strings[3][10]이라는 문자열에
p_str[3]라는 포인터배열을 대응시키는 건데
'p_str[i] = &strings[i][0];'을
'p_str[i] = strings[i];'로 바꿀 수 있으므로
for (int i = 0; i < 3; i++) {
p_str[i] = strings[i]; // &strings[i][0] == strings[i]
}
을
for (int i = 0; i < 3; i++) {
printf("%s\n", p_str[i]);
}
로 바꿔주고 실행을 시켜주면
이렇게 실행이 되는 것을 볼 수 있다.
1. 100개 이하의 정수를 입력받아 첫 줄에 짝수 번째 숫자들을 순서대로 출력하고, 다음 줄에 홀수 번째 숫자들을 순서대로 출력하는 프로그램을 만들어 보세요.
ex)
입력 예
7
3 1 4 1 5 9 2
출력 예
1 1 9
3 4 5 2
내 답 / 강의 답
2. 이 코드를 보고 어떤 것이 출력될 지 답하기.
ptr = &a로 놨으니 ptr은 a의 주솟값이겠고, ptr이 가리키는 변수의 값에 30을 저장했으므로 a에 30이 저장된다.
그리고 ptr이 b의 주솟값을 저장했다고 할때, ptr이 가리키는 변수의 값에 10을 저장했으므로 b에는 10이 저장된다.. 마지막으로 'ptr = &b;'이므로 '*ptr = b'가 된다
따라서
'printf("%d\n", a);' => 30
'printf("%d\n", b);' => 10
'printf("%d\n", *ptr);' => 'printf("%d\n", b);' => 10
3. 이 코드를 보고 어떤 것이 출력될 지 답하기.
일단 arr의 주솟값은 무슨값이 나올지 모르기 때문에 &arr[0]을 100이라고 두면
arr + 1은 int arr[]이므로 104가 될 것 이고, *(arr + 1)은 *(&arr[i])이므로 arr[i]가 나올 것이다.
따라서 먼저 printf("%d\n", arr);이 출력되어야 하므로 100이 출력될 것이고,
i = 3일땐 12가 더해져 112, 그리고 arr[3] = 1
i = 4일땐 4가 더해져 116, 그리고 arr[4] = 5
i = 5일땐 4가 더해져 120, 그리고 arr[5] = 9
i = 6일땐 4가 더해져 124, 그리고 arr[6] = 2
하고 i = 일땐 for문을 빠져나와 끝나게 된다.
따라서
100
112 1
116 5
120 9
124 2
로 나와야 된다.
4. 10*10 이하의 정수형 이차원 배열을 입력받아 그 배열의 각 행 요소의 합을 출력하는 프로그램을 만들어 보세요.
이렇게 10*10 이하지만 난 넉넉하게 arr[100][100]을 잡아줬고
그리고 n과m을 선언해준다.
그리고scanf()를 통하여 n행,m열을 받아주고
for문에서 'scanf("%d", &arr[i][j]);'를 해줘서 arr[i][j]에 값을 입력받아줌으로써
이차원 배열이 만들어진다!
그리고 다음 for문을 통하여 arr[i][j]의 합을 구해주고,
합을 출력해주면
이렇게 잘 나오게 된다!
헷갈려서 에타에 물어봤었는데 이해했다!!
5. 이 코드를 보고 어떤 것이 출력될 지 답하기.
int arr[3][3] = { 0 };
이므로 모든 칸에 다 0이 들어가 있다는 것을 알 수 있다.
printf("%d\n", &arr); => arr의 주솟값은 알수없으므로 100이라고 두자.
printf("%d\n", arr); => arr == &arr[0]이므로 100
printf("%d\n", *arr); => *arr는 arr에 들어있는 값을 의미하므로 100
printf("%d\n", &arr[0]); => arr[0]의 주솟값은 arr의 주솟값과 같으므로 100이 나올 것
printf("%d\n", arr[0]); => arr[0] == &arr[0][0] 이므로 100
printf("%d\n", *arr[0]); => *arr[0] == *(&arr[0])[0] == a[0][0] == 0
printf("%d\n", &arr[0][0]); => 100
printf("%d\n", arr[0][0]); => 0
printf("%d\n", &arr[0][0]); => 100이라고 둘 때
printf("%d\n", arr[0] + 1); => arr[0] == &arr[0][0]인데 + 1을 해주면 int이므로 4가 늘어나 104가 된다.
printf("%d\n", &arr[0] + 1); => &arr[0] == arr이므로 arr전체를 가리키게 되어 첫번째 행을 가리키는데 거기에 +1을 해주면 다음 행으로 넘어가 3열이므로 100 + 4*3 == 112가 된다.
printf("%d\n", arr + 1); => 위와 같다
printf("%d\n", &arr + 1); => &arr는 2차원 배열 자체를 가리키는데 +1을 해주면 아예 다른 배열로 넘어가 3행 3열 이므로 100 + 4*9 == 136이 된다.
6. 이 코드를 보고 어느부분이 잘못되었는지 맞추기.
출력값
*ptr[i][j]이 잘못되었다.
왜냐면 int (*ptr)[4] = arr; 로 선언함으로써
포인터 자체를 이차원 배열로 쓸 수 있게 되어 *을 붙이면 안된다.
'C | C++' 카테고리의 다른 글
C언어 - 연산자와 제어문 (1) | 2020.08.20 |
---|---|
C언어 - 입출력과 변수 (0) | 2020.08.20 |