Skip to content

Add return_local_address example #912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/singleapp/return_local_address/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
app_*
42 changes: 42 additions & 0 deletions examples/singleapp/return_local_address/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# これは何?

Goの書き方に慣れている人がC言語でプログラムを実装する際にやってしまいがちなことの一つ。

Goでは、「エスケープ分析」が存在するので関数ローカル変数のアドレスを戻り値として返却するコードを書いても

コンパイラがヒープに移動させてくれるため、問題にはならない。

が、C言語で同じことをすると当然うまく動かない。(コンパイル時に警告が出力されるので気づかないことは無いが)

C言語ではローカル変数のアドレスを返却した場合の挙動は「未定義」であるため、コンパイラによって挙動が変わる場合がある。

本サンプルをGitpod上で動作させたところ、gccの場合はコアダンプしたが、clangの場合はコアダンプはせず、値が上書きされて表示された。

Goの場合は呼び出しごとにヒープにエスケープされるので、狙った動作にはなる。

```sh
$ task
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Ubuntu clang version 14.0.0-1ubuntu1.1
task: [compile-c] gcc -o ../app_gcc main.c
main.c: In function ‘getvalue’:
main.c:5:12: warning: function returns address of local variable [-Wreturn-local-addr]
5 | return &x;
| ^~
task: [compile-c] clang -o ../app_clang main.c
main.c:5:13: warning: address of stack memory associated with parameter 'x' returned [-Wreturn-stack-address]
return &x;
^
1 warning generated.
task: [compile-go] go build -o app_go main.go
task: [run_gcc] sh -c './app_gcc | true'
Segmentation fault
task: [run_clang] ./app_clang
2,2
task: [run_go] ./app_go
1,2
```

## 参考情報

- https://www.dolthub.com/blog/2025-04-18-optimizing-heap-allocations/
38 changes: 38 additions & 0 deletions examples/singleapp/return_local_address/Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# https://taskfile.dev

version: '3'

tasks:
default:
cmds:
- task: show-ver
- task: compile-c
- task: compile-go
- task: run_gcc
- task: run_clang
- task: run_go
show-ver:
silent: true
cmds:
- gcc --version | head -n 1
- clang --version | head -n 1
compile-c:
dir: c
cmds:
- gcc -o ../app_gcc main.c
- clang -o ../app_clang main.c
compile-go:
cmds:
- go build -o app_go main.go
run_gcc:
internal: true
cmds:
- sh -c './app_gcc | true'
run_clang:
internal: true
cmds:
- ./app_clang
run_go:
internal: true
cmds:
- ./app_go
15 changes: 15 additions & 0 deletions examples/singleapp/return_local_address/c/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <stdio.h>

int *getvalue(int x)
{
return &x;
}

int main(void)
{
int *p1 = getvalue(1);
int *p2 = getvalue(2);
printf("%d,%d\n", *p1, *p2);

return 0;
}
13 changes: 13 additions & 0 deletions examples/singleapp/return_local_address/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

import "fmt"

func getvalue(x int) *int {
return &x
}

func main() {
p1 := getvalue(1)
p2 := getvalue(2)
fmt.Printf("%d,%d\n", *p1, *p2)
}
Loading