我正在使用S" ..."
字符串调用 C 函数printf
,并且我遇到了无效的内存地址。将指向在 Forth 端创建的以 null 结尾的字符串的指针传递给 C 的正确方法是什么?
以下是 hello world 的两个版本gforth
,一个使用专用语法来写出文字字符串,另一个使用type
将字符串存储为值(尽管微不足道(
这是helloworld.fs
#! /usr/bin/env gforth
.( Hello, world!)
CR
bye
和helloworld2.fs
#! /usr/bin/env gforth
S" Hello, world!" type
CR
bye
据我所知,语法S" Hello, world"
在 Forth 运行时内的某个全局区域中创建一个新字符串,并将指向它的指针推送到堆栈上。它也可能是一个比这更丰富的对象,我不知道 Forth 是否使用以 null 结尾的字符串。
无论如何,gforth
公开了一些用于调用 C 函数的词,这里hello_world_c.fs
#! /usr/bin/env gforth
c #include <stdio.h>
c-function printf- printf a -- n
S" hello" printf-
CR
bye
我希望这个脚本在运行时打印hello
然后换行符。函数printf
的原型是a -- n
...这意味着它需要一个地址并返回与int
大小相同的内容。单个格式字符串绝对是传递给printf
的可接受的参数集合。
但是,它会产生错误:
$ ./hello_world_c.fs
ar: `u' modifier ignored since `D' is the default (see `U')
in file included from *OS command line*:-1
hello_world_c.fs:5: Invalid memory address
S" hello" >>>printf-<<<
Backtrace:
$7F3A14D65018 call-c
$763A14D64F50 execute
我猜这里的问题来自这样一个事实,即S" hello"
并不是真正的指针,而是其他东西。有没有办法将其转换为指针,以便对printf
的调用指向正确的内容?
事实证明,S"
不会创建以 null 结尾的字符串,也不会将地址专门推送到堆栈上。
S"
创建一个临时位置(该位置似乎至少存在到下一次调用S"
(,并将长度和地址推送到堆栈上。
调用S"
后,长度位于堆栈的顶部,此顺序很重要。
下面是一个与gforth
互动会话的示例,为清楚起见,插入了注释和提示(>
(。
$ gforth
> S" a" ( define a new string, push length and addr )
> .s ( display size of stack and contents of stack )
<2> 22565888 1
> . ( print and drop top item of stack )
1
> .s ( display size and contents of stack again )
<1> 22565888
bye
单词s"
与S"
相似,只是它支持C风格的字符串转义。它以与S"
相同的方式劫持"阅读器",但执行一些翻译。
牢记所有这些,下面是调用正确printf-
的脚本实现。
#! /usr/bin/env gforth
c #include <stdio.h>
c-function printf- printf a -- n
( synonym for drop for documentation purposes.
remove the initial length of a length, bytes pair created by
S" or s" )
: drop-cstr-length drop ;
s" hello worldn " drop-cstr-length
printf-
bye
打印hello world
然后正常退出。
通常,在 Forth 中,您可以通过创建一个编译所需代码的新单词来简化此操作;如下所示:
: cstr" postpone s" postpone drop ; immediate
然后它变得更容易使用;
#! /usr/bin/env gforth
c #include <stdio.h>
c-function printf- printf a -- n
cstr" hello worldn " printf-