( 파일 실행할때 주는 매개변수가 사용자명이 된다.) 1. who are you? 라고 물으면, 이름 입력받기(read 사용) 2. 사용자 명(apple)이 아니면, Permission Denied로 뜨게하기 3. 사용자 명(apple)이 맞다면, 1) ls –l 한 결과와 컴파일 할 파일을 묻고 입력받는다. 2) 이때, 컴파일 하여 생긴 실행파일(a.out)의 이름을 현재 날짜로 바꾸어주기 (ex. 20200529) ( ls 하여 현재 날짜로 된 실행파일이 있는지 확인하고, 실행해보기)
vi 편집기로 assign1.sh라는 파일에 위와 같은 내용을 입력해준다.
매직넘버를 이용하여 실행할 쉘스크립트를 만들어 준다.
who are you? 라는 질문에 답을 입력하기 위해 read라는 명령어를 이용하고 name이라는 변수에 입력한다.
입력과 출력을 한 행에서 처리하기 위해 -p 옵션을 사용한다.
if문을 이용하여 name 변수에 입력한 값과 매개변수($1=사용자명)에 입력한 값이 같다면
( if문을 쓸 때 괄호([ ])와 비교 연산자(=) 앞뒤에 반드시 공백이 있어야 함을 주의한다. )
파일 상세 정보를 출력하기 위해 ls -l을 해준다.
그리고 컴파일할 파일을 묻고(file to compile) 입력받기 위해 read명령어를 이용한다.
이 때도 위와 같이 -p 옵션을 사용하여 입력과 출력을 한 행에서 처리하고 file이라는 변수에 입력한다.
위에서 입력한 컴파일할 파일을 컴파일하기 위해 gcc를 이용한다.
컴파일하여 생성된 실행파일 a.out의 이름을 현재 날짜로 바꾸어주기 위해 파일명을 변경해주는 명령어인 mv를 이용한다.
이 때, date라는 명령어를 통해 현재 날짜와 시간으로 변경되도록 했고 (Y:연도, m:월, d:일)
date+포맷을 입력할 때 +와 포맷 사이에 공백이 없어야 함을 주의해야 한다.
또한, 컴파일이 성공되었다는 메세지(Success to compile)가 출력되도록 echo 명령어를 이용한다.
만약 name 변수에 입력한 값과 매개변수($1=사용자명)에 입력한 값이 다르다면 (=else)
Permission denied라는 메세지가 출력되게 하기 위해 echo를 이용한다.
위와 같은 내용을 모두 입력하고 vi 편집기를 저장 후 종료한다.
chmod를 이용하여 assign1.sh 파일에 실행 권한을 부여해준다.
apple을 매개변수로 하여 assign1.sh 파일을 실행시킨 후 who are you?라는 질문에 apple과 다른 swing을 입력해주면
permission denied라는 메세지가 잘 출력되는 것을 확인할 수 있다.
( 이와 같이 출력되는 이유는 매개변수와 입력받은 이름이 다르기 때문이다. )
assign1.sh 파일을 실행하기 전에 ls하여 현재 날짜로 된 실행파일이 없음을 확인할 수 있다.
apple을 매개변수로 하여 assign1.sh 파일을 실행시킨 후 who are you?라는 질문에 apple과 같은 apple을 입력해주면
ls -l한 결과가 잘 출력되고 컴파일 할 파일을 묻고 입력받을 수 있게 함을 확인할 수 있다.
hello.c를 입력하면 Success to compile이라는 메세지가 잘 뜨는 것을 확인할 수 있다.
위와 같이 ls를 입력하여 파일을 확인해보면 a.out 대신 현재 날짜(20200602)로 파일명이 잘 변경된 것을 확인할 수 있다.
또한, 20200602 파일을 실행시켜보면 hello.c파일에 입력되어있던 내용을 컴파일한 내용이 잘 출력됨을 확인할 수 있다.
1. 매개변수 총 4개를 가지고 실행한다. ( 매개변수명은 자유롭게 가능) 2. 첫번째 매개변수(ex. abcd)는 디렉토리명으로, 사용자 홈디렉토리에 생성한다. (~/) 3. 만든 디렉토리로 이동하여, 매개변수2,3,4 (ex. a, b, c)의 이름의 파일을 생성한다. (touch) 4. 만든 디렉토리에 파일이 잘 생성되었는지 확인하기 위해, ls –l을 해준다. 5. 삭제할 파일을 묻고(read), 입력 받아, 삭제한다. (rm) 6. 삭제 한 후, 다시 ls –l하여 삭제된 것을 확인할 수 있도록 한다.
vi 편집기로 assign0.sh 라는 파일에 위와 같은 내용을 입력해준다.
#! /bin/bash를 입력하여 실행할 쉘스크립트를 만들어 준다.
디렉토리를 생성하는 명령어인 mkdir을 이용하여 홈 디렉토리(~)에 $1 디렉토리를 생성해준다. ($1은 매개변수1을 의미)
만들어진 디렉토리로 이동하기 위해 cd ~/$1을 입력해준다.
만들어진 디렉토리에 매개변수 2, 3, 4 파일을 생성하기 위해 touch 명령어를 이용한다. ($2, $3, $4는 매개변수2,3,4를 의미)
만들어진 디렉토리에 파일이 잘 생성되었는지 확인하기 위해 ls -l을 해준다.
삭제할 파일을 묻기 위해 read 명령어를 이용한다. (-p는 입력과 출력을 한 행에서 처리해주는 옵션)
file이라는 변수로 삭제할 파일을 입력받은 후 rm 명령어를 이용하여 입력한 파일을 삭제한다.
입력한 파일이 잘 삭제되었는지 확인하기 이해 다시 ls -l을 해준다.
vi 편집기를 저장 후 종료하고, assign0.sh 파일을 실행시키면 다음과 같다.
assign0.sh 라는 파일에 실행 권한을 부여해준 후 abcd라는 디렉토리와 a, b, c라는 파일을 생성한다
위와 같이 처음에는 입력한 파일이 잘 생성된 것을 볼 수 있고 delete file에 삭제할 파일명을 입력하면
배열은 포인터를 사용하여 구현된다. 포인터는 배열을 반복할 때 사용할 수 있다. (배열 인덱스 대신 사용 가능)
C++에서 동적으로 메모리를 할당할 수 있는 유일한 방법이다. (가장 흔한 사용 사례)
데이터를 복사하지 않고도 많은 양의 데이터를 함수에 전달할 수 있다.
함수를 매개 변수로 다른 함수에 전달하는 데 사용할 수 있다.
상속을 다룰 때 다형성을 달성하기 위해 사용한다.
하나의 구조체/클래스 포인터를 다른 구조체/클래스에 두어 체인을 형성하는 데 사용할 수 있다.
▶ 간접 참조 연산자
- 지정된 주소에 있는 값을 액세스하는 연산자
- 역 참조 연산자, 간접 연산자, * 연산자라고도 한다.
- 피연산자의 메모리에 접근하여 주소 값을 저장해가는 변수
- ex) a의 주소를 100번지를 가리킨다고 가정하자.
int a = 200; int* p; p = &a;
cout << p; // 이 경우 a의 주소값인 100이 출력됨 cout << *p; // 이 경우 a의 값인 200이 출력됨
위의 표처럼 포인터 변수 앞에 '*' 을 붙여주어야 값이 출력된다.
▶ 포인터 연산
- 포인터 연산 규칙
① 포인터끼리 더할 수 없다. ( 주소 값을 더하는 것은 쓸모가 없음 )
② 포인터끼리 뺄 수 있다. ( 주소 값의 거리를 구함으로써 메모리상에서 얼마나 떨어져 있는지 구해낼 수 있음 )
③ 포인터에 정수를 더하거나 뺄 수 있다.
④ 자료형 포인터의 값을 1 증가시키면 자료형의 크기만큼 증가된다.
⑤ 포인터끼리 대입은 가능하다. ( 단, 대입받을 포인터와 대입할 포인터의 타입이 일치해야함 )
⑥ 포인터에 정수를 대입할 수 없다.
⑦ 포인터와 실수와의 연산은 할 수 없다.
⑧ 포인터끼리 비교는 가능하다.
- 포인터 증감 연산
※ 증감 연산자(++, --)와 참조 연산자(*)는 함께 쓰일 수 있다.
a = *ptr++
a에 ptr의 값을 대입한 후 ptr 증가 (주소가 증가)
a = (*ptr)++
a에 ptr의 값을 대입한 후 *ptr 증가 (값이 증가)
a = *++ptr
ptr 증가한 후 a에 대입 (주소가 증가, 증가한 주소가 가리키는 값 대입)
a = ++*ptr
*ptr 증가한 후 a에 대입 (값의 증가, 증가한 값 대입)
- ex) a의 주소는 100번지, b의 주소는 200번지라고 가정하자.
int b = 10; int* ptr = &b; int a;
cout << ptr; // b의 주소값인 200이 출력됨 cout << *ptr; // b의 값인 10이 출력됨
a = *ptr++;
cout << a; // 값을 대입 후 ptr이 증가하므로 10이 출력됨 cout << ptr; // b의 주소에서 4바이트 증가한 204가 출력됨 cout << *ptr; // a의 주소값인 100이 출력됨
int b = 10; int* ptr = &b; int a;
cout << ptr; // b의 주소값인 200이 출력됨 cout << *ptr; // b의 값인 10이 출력됨
a = (*ptr)++;
cout << a; // 값을 대입 후 ptr이 증가하므로 10이 출력됨 cout << ptr; // ptr은 증가하지 않으므로 200이 출력됨 cout << *ptr; // ptr의 값이 증가하므로 11이 출력됨
int b = 10; int* ptr = &b; int a;
cout << ptr; // b의 주소값인 200이 출력됨 cout << *ptr; // b의 값인 10이 출력됨
a = *++ptr;
cout << a; // ptr을 증가시킨 후 대입하므로 a의 주소값인 100이 출력됨 cout << ptr; // b의 주소에서 4바이트 증가한 204가 출력됨 cout << *ptr; // a의 주소값인 100이 출력됨
int b = 10; int* ptr = &b; int a;
cout << ptr; // b의 주소값인 200이 출력됨 cout << *ptr; // b의 값인 10이 출력됨
a = ++*ptr;
cout << a; // ptr의 값을 증가시킨 후 대입하므로 a의 값인 11이 출력됨 cout << ptr; // ptr은 증가하지 않으므로 200이 출력됨 cout << *ptr; // ptr의 값이 증가하므로 11이 출력됨
▶ 포인터와 배열
- 포인터와 배열은 매우 긴밀한 관게를 갖고 있고, 어떤 부분에서는 서로를 대체할 수도 있다.
- 배열의 이름은 포인터 상수이다.
※ 포인터 상수란 포인터 변수가 가리키고 있는 주소 값을 변경할 수 없는 포인터를 의미
- 가변 문자열 길이를 저장할 때는 배열보다 포인터가 유용하다.
- 정수형 배열의 메모리 할당
ex) int aa[3] = {10, 20, 30};
- 배열의 주소 표현 • aa[0]의 주소(&aa[0]) = 1031번지 • aa[1]의 주소(&aa[1]) = 1035번지 • aa[2]의 주소(&aa[2]) = 1039번지 • 배열 이름 aa = 전체 배열의 주소 = 1031번지 • 배열 aa의 주소를 구할 때는 ‘&’를 쓰지 않고, 단순히 ‘aa’로 표현
- 배열 이름 활용법
ex) aa 값을 1031로 가정하고, aa+1을 계산한 결과는? • 예상결과 : aa+1 ➔ 1031 + 1 = 1032 (X) • 실제 결과: aa+1 ➔ 1031 + 4 = 1035 (O)
계산 과정) ‘+1’의 의미 : 배열 aa의 위치에서 한칸 건너뛰어라 한칸 : aa가 정수형 배열이므로 4byte 즉, aa+1 = &aa[1] = 1035