C/C++은 함수 호출 시 call by reference로 할 수 없었을 까요 ?

parkon의 이미지

망상인것 같기는 하지만, 예전부터 궁금했던게,

포트란이 문법이 간단한 이유가 call by reference를 쓰기 때문이 아닐까 싶습니다.

C++ 좀 쓰다보면, 함수 인자로 넘길 수 있는게, value, pointer, reference, 이 세가지나 되고,

그래서 머리를 아프게 하죠.

특히 큰 사이즈를 넘기기 부담스럽고 그 값을 바꿀 수 없는 call by value가 기본이라

더 상황을 복잡케 만드는 것 같습니다.

여기서 우문, C나 C++의 문법과 컴파일러를 설계할 때, 포트란처럼 함수 호출시 call by reference로 할 수 없었을 까요 ?

그렇게 되면 어떤 문제가 생기는 지도 좀 궁금하고요.

klyx의 이미지

문법적으로 call by reference인 언어의 함수를, C에서 포인터로 구현하였을 때 컴파일되는 코드에 차이가 있는지가 궁금하네요.
만약에 문법적으로 call by reference인 경우라도, 실제 구현은 C의 포인터처럼 매번 메모리를 참조하는 형식으로 구현되는 것이라면, C는 포인터를 넣음으로서 사용자가 call by reference처럼 이용할 수 있도록 선택권을 준 셈이 되어서, 말씀하신대로 내용은 어려워져도 활용법도 다양해지고 더 효율적인 코드를 짤수 있을듯합니다.

sloth_의 이미지

by value로 대동단결이 문법은 더 간단하지 않나요
아시다시피 C는 포인터변수를 인자로넘기는것도 call by (address) value이구
개념은 단순한거 같은데요

C++은 뭐 그런거 제쳐놔두 너무 복잡해서 저처럼 머리나쁜사람은 다른사람과 협업하기가 참 힘들더라구요

생각해보니 저는 포트란을 모르네요
죄송합니다 꾸우벅

kkb110의 이미지

갠적으로 c++ 가르쳐줄때 call by reference, call by value로 나누어서 개념을 가르쳐주는걸 싫어합니다.
더 햇갈리고 뭔가 두개가 완전다른개념인것처럼 느껴 질 수도 있거든요.
sloth_님 말씀대로 c++에선 모두 call by value이고 address도 value 에 하나이다. 라고 생각하는게 훨씬 자연스러운 개념같습니다.

글쓴이께서는 언어의 call by ... 를 따지셨지만 사실 진짜 그 뒤에 숨어있는 질문은 [포인터value의 존재여부] 입니다. 포트란, 자바에는 포인터가 없고 c++에는 포인터가 있죠. 이 차이가 표면적으로 이러한 질문으로 나타나는것이구요.
근데 글쓴이님 바람대로, c++에 포인터가 없으면. 언어 존재의 이유가 위협받겠는데요? ㅋㅋㅋ

snowall의 이미지

call by reference를 기본으로 하면, 이번엔 "값을 바꾸면 안되는" 경우를 처리할 때 별도의 처리를 해야 하니까 더 골치아파질 수도 있습니다.

--------------------------
피할 수 있을때 즐겨라!
http://snowall.tistory.com

피할 수 있을때 즐겨라! http://melotopia.net/b

parkon의 이미지

말씀해 주신것처럼,

call by value로 하는게 더 다양하고 강력한 언어가 되긴 하겠습니다.
이 부분에 대해선 이견이 없고요,

단지 call by value는 필연적으로 포인터의 과다 사용을 초래하고,

프로그래밍시, 이것 저것 머리를 아프게 하죠..., 이게 문제단...

물론, 포인터 자체를 없애자는 말은 아니고,

포인터는 포인터가 필요한 경우만 쓰고, 함수 호출 방식때문에 생기는 불필요한 과다 사용은 피하자...는게 제 생각이고요.

그래서, call by reference만 허용하도록 하고,

값을 바꾸면 안되는 경우나 lvalue가 될 수 없는 인자들에 대해서 별도 처리를 한다고 했을때,

어떤 문제가 있을지가 제가 궁금했던 거예요...

klyx의 이미지

당장은 int나 double같이 작은 데이터를 포인터를 넘기면 효율이 떨어진다는 것 정도가 떠오르네요.
보통 int나 int*나 같은 4바이트이므로, 함수 인자로 int를 넘기나 const int*를 넘기나 복사하는데 필요한 비용은 같은 것입니다.
하지만 이것을 사용할려고 할때는, const int*의 경우는 매번 *연산자로 값을 참조해야합니다.
C가 처음 만들어질때에는 이런 것도 충분히 크리티컬한 문제가 아니었을까요?

kkb110의 이미지

그런것과 비슷한 고민의 결론으로 java같은게 나온것이구요.

컴파일된 low level의 투명성이 중요한 c++ 용도상 (운영체제,임베디드, 최적화 알고리즘 개발 등),현재의 c++이 가장 적합하다고 판단됩니다.

기계어 레벨에서 봤을때 call by reference는 레지스터에 값을 넣거나 스택에 바로 값을 쌓아버리고 function으로 점프하는 것보다는 한단계 더 포장이 필요한 단계이고, 이것만 허용한다면 프로그래밍 할수있는 범위를 제한해버리는 결과가 되니까요. call by value가 기본인 이유도, 기계어 레벨에서 봤을때 가장 단순하고 자연스러운 방식이기 때문입니다.

우열을 따질 문제는 아닙니다. 자바가 필요한곳에 자바 쓰면 되고 c++필요한곳에 c++ 쓰면 되는거고. 단, c++의 존재이유를 생각해볼때 call by reference로만 제한해버리는것은 아닌것같습니다.

혹시 지금 하시는 작업에 그런 의문이 드신다면 좀 더 고레벨 언어를 쓰셔야 할 곳에 c++을 쓰시는건 아닌지 조심스레 추측해봅니다.

powersys의 이미지

C가 강력할 뿐만아니라 덜골치 아픕니다.

이것은 예를들어 반대로 되어있었다면 call by value 로 하고 싶을때 어찌 해야할까요.. 더골치 아파지죠..

또한 C는 Call by reference가 안되는것이 아니냐..혹은 포인터를 인자로 넘기는것도 어차피 Call by value 아니냐는 질문에는 그렇지 않다는겁니다.

그건단지 표현법일뿐이고 그게 아니라면 세상에 Call by reference 라는것은 존재하지 않는 허상이나 마찬가지 입니다..
즉 자바나 C++ 에서도 그것은 표현상 그러할뿐이지 실제로는 여전히 주소(포인터)를 넘깁니다.
내부작동은 C의 그것과 차이가 없다는것입니다.

그러니 오히려 더명확히 보이는 C의 방식이 더 낫다고 생각되는데요..

그리고 제가 알고 있기론 Call by reference 라는것이 참조값(주소)를 넘겨서 참조값으로 실제값을 찾아 사용할수 있는 방식
이라고 알고 있습니다. 그러니..
C 에서 포인터를 넘겨 그포인터의 내용을 참조하게 하는방식은 Call by value 가아니라 Call by reference 가 정확히 맞지 않나요..

지리즈의 이미지

ex)
char *strcpy(char *dest, const char *src);

call by reference, call by value의 구분은
순전히 그 기능이나 성능에 촛점을 맞추어서 구현하면 됩니다.
데이터의 보호를 목적으로 한다면, const 키워드를 이용하면 되구요.

There is no spoon. Neo from the Matrix 1999.

There is no spoon. Neo from the Matrix 1999.

지리즈의 이미지

call by value가 call by reference 보다 overhead가 적은 것으로 알고 있습니다.

그외에 call by reference는 메모리 관리가 복잡해 질 수도 있는 위험도 있구요.

value,pointer, reference의 이러한 구분은 c(c++)을 난해하게 만드데 한몫하는 것은 분명하지만,
성능적 잇점을 생각한다면 없앴을 수도 없습니다.
특히 바이너리와 1대1 매칭을 고려해서 코딩해야 할 경우, 이러한 기능은 매우 유효하죠.

There is no spoon. Neo from the Matrix 1999.

There is no spoon. Neo from the Matrix 1999.

neogeo의 이미지

Quote:

call by value가 call by reference 보다 overhead가 적은 것으로 알고 있습니다.

c++ 이라면 덩치큰 class 가 오가게 되면 copy constructor 가 불리워야 합니다.

이런경우 call by reference 가 훨씬 overhead 가 적게 되지요.

C 라면 덩치큰 struct 같은걸 call by value 로 부르게 되면 memcpy 수준이 불리우게 되므로 꽤 느려집니다.

오가는 data 사이즈에 따라 overhead는 바뀝니다.

Neogeo - Future is Now.

Neogeo - Future is Now.

neogeo의 이미지

c++ 이라면

void function(int& a) 와 같은 방식으로

call by reference 를 어느정도 사용 할 수 있습니다.

Neogeo - Future is Now.

Neogeo - Future is Now.

parkon의 이미지

제가 관심이 있는 분야가 운영체제나 임베디드는 아닙니다,
주로 과학 계산용이고 (그래서 아직도 저의 주 종목이 구시대의 유물인 포트란... ^^), 그리고 때로 장난질..
그래서 이런 우문을 올린듯...

잘 알진 못하지만, 운영체제 같은 경우에는 현재와 같은 C같은 언어가 제격이라고 저도 생각하고요,

오버해드가 얼마만큼 차이 날지 그게 궁금하긴 한데, 포트란이 뭐 연산 속도면에서
더 뛰어나지는 않을지 몰라도 그렇게 떨어지는 언어는 아니라는 선입견(?) 때문에
call by reference도 대부분의 경우 문제가 없지 않을까 생각했습니다.

kkb110의 이미지

솔찍히 성능에 관해서는 parkon님의 말씀이 맞는 것 같습니다.

오버헤드 늘어난다고 해봐야 instruction 몇개 차이나는 정도일테고 이정도야 99.99%경우에 별로 지장이 없지 않을까 싶구요. parkon님 말씀대로 대부분의 경우 문제가 없을겁니다. 포인터를 직접 조작해야하는 경우만 제외하면요.

다만 성능에서 한발 물러나서 'c++로 이 컴퓨터를 내맘대로 동작시키겠어' 라는 목표에는 call by value는 필수이고, 변수 사이즈가 충분히 작다는 가정 하에, 기계어수준에서는 더 자연스럽다는 생각입니다.

ps. 구조체나 class가 큰 경우에는 어떻게 정확히 컴파일이 되는 지는 모르겠네요. 작은 경우에는 push하거나 레지스터에 넣던데, 혹시 아시는분? field 별로 push하나요?

sloth_의 이미지

http://en.wikipedia.org/wiki/Evaluation_strategy

이런 내용에 대한 토론이나 혹은 포트란 vs C 뭐 이런게 아니라
'왜 C는 이 방식을 택했을까' 라는 의문이셨다면
풀리셨으리라 생각됩니다.

아시다시피 모든 길에는 장단점이 있구, C는 어셈블리어로 작업하던 시스템 프로그래머들이 자기들이 쓰려구 만든언어였지요.

C가 아닌 C++은..
저도 그 양반들 머리속을 모르겠습니다.

뱀다리인데....
과학계산같은 응용에서는 포트란이 C보다 훨씬 성능이 좋은 결과물을 만들기 편하다고 알고있습니다.
컴파일러가 최적화하기가 훨씬 편하다고 그러네요.
말씀드렸다시피 포트란을 전혀 몰라서 주워들은 말이기는합니다.

complexz의 이미지

포트란의 call by ref.도 결국은 메모리 주소를 넘기는 방식으로 알고 있습니다.
c/c++ 에서 call by value를 기본으로 하는것은 모듈화된 프로그램을 지향하기 위해서 이구요
한 변수를 온갖 루틴들이 건드린다면 프로그램이 커질경우 굉장히 복잡하게 됩니다.
그래서 성능이 그렇게 중요하지 않을 경우 모듈화된 프로그래밍을 위해
변수는 지역적이어야 한다라는 개념이 나온걸로 알고 있구요
포트란이 더 최적화가 되었다는 이야기는 어떤 근거에서 말씀하시는지 모르겠네요.
c/c++를 포트란같이 사용할 경우 즉 (전역변수, 참조자, 포인터, inline함수 난무 추상화 제한) 포트란과의 차이가 있을까 싶네요
과학기술분야에서 포트란을 사용하는 이유는 두 세가지 밖에 없습니다.
기존에 검증되고 성능좋은 코드가 너무 많다. 대표적으로 LAPACK 같은...
프로그래밍 접근이 상대적으로 쉽다. 복잡한 추상화가 전혀 없습니다.
뭐 최근에 나온 포트란은 객체지향도 들어간다고 하지만 대부분 포트란 77, 90들을 사용하시니...
대부분의 머신에서 컴파일러를 지원한다. 계산용 코드는 슈퍼컴에서 돌리는데 대부분 포트란을 지원합니다.
언어자체가 간단하니깐 컴파일러 만드는것도 상대적으로 수월하죠
c++ 같은경우는 대중적으로 많이 사용하고 있는 pc에서 조차도 다 구현되지 못한걸로 알고 있습니다.
그럼에도 불구하고 과학기술분야에서 c/c++의 역할은 점점 강화되어 왔습니다.
c++의 inline 기능과 template 기능을 이용해서 효율성과 추상화의 두마리 토끼를 모두 잡을 수 있기 때문이죠
계산용 컴퓨터로서도 고성능 pc의 등장과 클러스터링으로 어느정도 커버가 되구요
유명한 open source CFD 코드도 c/c++로 나오고 있고 유명한 수학 라이브러리도 c/c++로 포팅되고 있습니다.(이부분의 완성도는
좀 의심스럽긴 하지만...) matlab도 초기에 포트란이었다가 c/c++ 베이스로 바꼈다고 하구요
FEM 코드는 글쎄요...그렇게 성공적인 코드는 본적이 없네요 대부분 실험적인 코드들인거 같구요
문제는 이겁니다.
더 많은 능력을 줄수록 사용법은 복잡해 진다. 필요는 필요를 부른다.

parkon의 이미지

말씀하신 것처럼,
요즘 과학/공학에도 점차 포트란에서 C++로 옮겨가는 추세이긴 합니다.
저도 점차 저의 주 언어를 c++로 옮겨가고 있고요.
설령 성능 차이가 좀 난다 해도, 요즘 cpu 가격이 계속 내려가니
좀 더 유지보수가 쉽고 확장성이 좋은 프로그래밍으로 가는게 당연하겠죠.

잘 짜여진 C++ 코드는 포트란과 속도면에서 대등소이 할 것으로 저도 생각합니다만,
가끔, C++ 사용해서 만들어진 라이브러리가 포트란 라이브러리에 비해
연산이 몇배 정도 느려지는 경우가 있더군요
(제가 본 예는 몬테 카를로 시뮬레이션 코드이구요).
근데 이게 언어 자체의 차이에 기인하는건지, 그 라이브러리를 개발한 개발진의 역량 차이때문인지는 잘 모르겠고요.

complexz의 이미지

언어적으로야..c++ 같은 경우는 포인터, 레퍼런스, inline 등 지원되니깐
포트란에 비해서 떨어질껀 없다고 생각합니다. 짜는 사람 능력에 달린 문제겠지요(또는 사용한 라이브러리)
다만,,,복소수 연산은 답이 없습니다.
포트란의 경우 언어자체에서 지원하는 내장형 complex 타입이 있습니다.
c++의 경우는 없죠...그냥 클래스로 만든것일뿐이고
포트란의 complex 연산 속도를 따라 갈 수 없다고 하더군요

snowall의 이미지

그런데 그건 call by value냐 call by reference냐 하고는 상관 없는 문제 아닌가요?

--------------------------
피할 수 있을때 즐겨라!
http://snowall.tistory.com

피할 수 있을때 즐겨라! http://melotopia.net/b

parkon의 이미지

복소수는 정말 그렇죠... ^^

얼핏 들은 말로는 곧 복소수 라이브러리가 C++ 컴파일러 기본으로 들어 갈 가능성이 많다고 하는데
일단 그 전에는 문제고요,

사실, 이것 외에도 c/c++은 float형에 대한 math 라이브러리 지원이 너무 약해요.
경우에 따라서, double precision은 필요치 않고 대신 연산 속도에 목맬때가 있는데
이 때도 답이 없습니다.
대표적인 아주 바보같은 짓이
float x=1.,y;
y= (float) sin((double) x);

그리고, 아주 사소하긴 하지만, 코딩때 짜증 지대로인게
지수에 대한 c/c++ 식이 너무 번잡해요.
포트란에서는 x**5 하면 되는데 c에선 pow(x,5),
이게 별것 아니긴 하지만 수식을 쳐 넣을때 계속 괴롭힌단...,

이렇게 주절 주절 쓰니 제가 c/c++ 안티로 보일까 겁납니다...,
사실 그건 아닌데...^^

snowall의 이미지

하긴...생각해보니 저도 C언어로 복소수 다루는 루틴을 전부 만들어서 썼었군요...
...왜그랬더라...;;;

--------------------------
피할 수 있을때 즐겨라!
http://snowall.tistory.com

피할 수 있을때 즐겨라! http://melotopia.net/b

ifree의 이미지

float 연산이 double 연산보다 빠르지 않은 것으로 알고 있습니다.
FPU 상에서는 모두 double 로 연산되기 때문에 캐스팅 오버로드를 감안하면 오히려 double 이 더 빠를 수 있다고 합니다.

parkon의 이미지

아, 그런가요 ?

말씀하신게 포트란의 float 연산도 (캐스팅 쓰는게 아니라 별도로 float 연산을 위해
마련된 함수들도) 마찬가지란 말씀이시죠 ?

그렇담 다행입니다, 지금 하고 있는 제 일에서 근심하나 들었네요.

complexz의 이미지

직접 확인한 내용입니다.
단순한 속도 체크 루틴일 경우 컴파일러에서 미리 최적화를 해버리기 때문에
제대로된 테스트가 안돼구요
float 형이 30% 정도 빨랐구요 나눗셈 연산에서는 비슷한 결과가 나왔습니다.

float 지원 라이브러리가 없는건 좀 문제군요
포트란에서는 데이터형에 대해서 따로 라이브러리가 있는 경우가 많죠
c++의 경우 이 짓(!)이 비효율적이라서 template을 지원 해주지만 아직은 관련 라이브러리가 없는가 보죠?
intel math 라이브러리도 그런가요?????

그리고 아래 문제는 내장함수에 대한 문제네요
float x=1.,y;
y= (float) sin((double) x);
일반적으로 형변환에 대한 overhead도 크니깐 저런식의 형변환은 별로 안 좋은거 같구요
c++의 경우에는 각 타입에 대한 내장 수학 함수의 구현이 다 되어 있습니다.
포트란이나 c 와 달리 함수중첩이 가능하기 때문에 타입이 다르다고 함수 이름이 바뀌지 않습니다.
즉 아래와 같은 함수가 구현되어 있습니다.
float cos(float x ); // C++ only

결론은 수치해석에는 c 보다는 c++이 좋다는...

sangwoo의 이미지

포트란 함수들을 c에서 argument를 포인터로 바꿔서 call 할 수 있는 걸 생각해 보면 결국 포트란도 내부적으로는 call by value를 사용한다고 볼 수 있지 않을까요? 다만 사용자가 보기에는 call by reference 이지만요.
하긴 implementation detail 일수도 있겠네요. 하지만 전 딴 C/C++ 루틴과의 링킹을 허용하지 않는 포트란 컴파일러는 사용하고 싶지 않네요 :-)
----
Let's shut up and code.

----
Let's shut up and code.

parkon의 이미지

제가 이렇게 c/c++과 포트란을 크로스 링킹 해 본 적은 아직 없는데

지금 저희 그룹에서 하는 일도

나중에 포트란에서 불러 쓸 수 있는 c++ 라이브러리 제작이거든요.

구글에서 검색해 보니

http://arnholm.org/software/cppf77/cppf77.htm

대충 이런 자료가 보이고,

또 실제로 포트란에서 c 불러 쓰는 경우도 본 적이 있습니다.

sangwoo의 이미지

C++와 Fortran 은 calling convention만 맞춰주면 서로 링킹 가능합니다. (최소한 intel compiler랑 gcc는..) 물론 C++에서 심볼은 export "C" 로 name mangling을 없애줘야 하긴 해요. 제가 저희 랩 프로젝트 중에 다른 사람 코드랑 연결시킬 때 이렇게 한 게 있거든요.

----
Let's shut up and code.

----
Let's shut up and code.