如何在 stdio.h (CLANG, OS X) 中实现自定义版本的 getline 函数(答案:更改用于编译的 POSIX 标准)

How to implement custom versions of the getline function in stdio.h (CLANG, OS X) (ANSWER: Change POSIX standard used to compile)

晚上好,

我正在完成 Kernighan 和 Ritchie 的经典作品 "The C Programming Language" 中的练习。

练习中有几个地方要求您创建自己的函数版本,该函数的名称与标准库中的函数名称相同。我不想为我的版本创建一个替代名称,我真的很想告诉编译器我宁愿使用我的函数版本而不是标准库函数。

具体来说,如果我尝试编译练习 1-18 的解决方案,该解决方案从每行输入中删除尾随空白和制表符,我将使用函数 'getline' 从标准输入中读取该行。不幸的是,这会产生编译器错误,因为 getline 是在 stdio.h.

中定义的

我试过使用#undef,但似乎无法正常工作。

我已经搜索过其他类似的问题并找到了[这个][1];然而,它似乎需要破解标准库头文件,而我不想这样做。

提前感谢您的帮助。

这是代码(为简短起见去掉了我的注释):

#include <stdio.h>
#include <stdlib.h>

#define MAXLINE 1000

static size_t getline(char s[], size_t lim) {

    char   c;
    size_t i = 0;

    while (--lim > 0 && (c = (char)getchar()) != (char)EOF && c != '\n')
        s[i++] = c;
    if (c == '\n')
        s[i++] = c;
    s[i] = '[=12=]';

    return i;
}

int main(void) {

    char   line[MAXLINE] = "";
    size_t len = 0;

    while ((len = getline(line, MAXLINE)) > 0)
        if (len > MAXLINE)
            printf("%s", line);

    exit(EXIT_SUCCESS);
}

而且,我得到的错误是:

cc -std=c99 -Wall -g -I. -c -o obj/cleantrailsnblanks.o cleantrailsnblanks.c
cleantrailsnblanks.c:14:15: error: static declaration of 'getline' follows non-static declaration
static size_t getline(char s[], size_t lim) {
              ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/stdio.h:442:9: note: previous declaration is here
ssize_t getline(char ** __restrict, size_t * __restrict, FILE * __restrict) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
        ^
cleantrailsnblanks.c:35:40: error: too few arguments to function call, expected 3, have 2
    while ((len = getline(line, MAXLINE)) > 0)
                  ~~~~~~~              ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/stdio.h:442:1: note: 'getline' declared here
ssize_t getline(char ** __restrict, size_t * __restrict, FILE * __restrict) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
^
2 errors generated.
make: *** [obj/cleantrailsnblanks.o] Error 1

更新 1

在我从我的定义中删除 'static' 之后,错误变为:

cc -std=c99 -Wall -g -I. -c -o obj/cleantrailsnblanks.o cleantrailsnblanks.c
cleantrailsnblanks.c:14:8: error: conflicting types for 'getline'
size_t getline(char s[], size_t lim) {
       ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/stdio.h:442:9: note: previous declaration is here
ssize_t getline(char ** __restrict, size_t * __restrict, FILE * __restrict) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
        ^
cleantrailsnblanks.c:35:40: error: too few arguments to function call, expected 3, have 2
while ((len = getline(line, MAXLINE)) > 0)
              ~~~~~~~              ^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/stdio.h:442:1: note: 'getline' declared here
ssize_t getline(char ** __restrict, size_t * __restrict, FILE * __restrict) __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
^
2 errors generated.
make: *** [obj/cleantrailsnblanks.o] Error 1

回答

参考这个讨论:Why do I get a "conflicting types for getline" error when compiling the longest line example in chapter 1 of K&R2?

解决方案是在包含 stdio.h:

之前添加这两行
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>

这设置使用 POSIX.1-2001 标准编译代码,而不是 POSIX.1-2008 标准,其中 GNU getline() 扩展被添加到标准.

这取决于您将如何使用它。

如果它只在一个 .c 文件中,我会将您的方法名称更改为其他名称,然后将所有调用更改为您的新名称或在文件顶部添加 #define realName overrideName

如果这更通用,或者为了在单元测试中进行模拟,我会将您重写的 getline 方法放入其自己的 .c 文件中。将其编译为 .so,然后在启动您的应用程序时,在任何其他库之前告诉您的系统您的覆盖 .so 文件。

我认为你的练习想让你学习动态注入共享库

gcc -shared -o libname.so filename.c

一样编译你的文件(没有static

在 OS X 上你需要设置 3 个环境变量 :

  • setenv DYLD_LIBRARY_PATH .

  • setenv DYLD_INSERT_LIBRARIES libname.so

  • setenv DYLD_FORCE_FLAT_NAMESPACE 1

其他一些信息:http://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html

编辑

让我通过更改 id

的结果向您展示一个示例
$> id
uid=501(yourname) gid(staff) groups ...

创建文件getuid.c

int getuid() {
     return (0);
}

int geteuid() {
     return (0);
}

gcc -shared -o myuid.so getuid.c

编译

可以写nm myuid.so,看看里面的函数

然后设置你的环境变量

setenv DYLD_LIBRARY_PATH .
setenv DYLD_INSERT_LIBRARIES libname.so
setenv DYLD_FORCE_FLAT_NAMESPACE 1

它可能与 export 一起使用,取决于您的 shell。

然后 id 结果如下:

$> id
uid=0(yourname) gid(staff) groups ...

K&R 现在是一本(非常)旧的书,您需要考虑到这一点。 Declare/define getline 完全按照手册所说(实际上)。正如你在 OSX,手册上说:

SYNOPSIS

 #include <stdio.h>

 ssize_t
 getline(char ** restrict linep, size_t * restrict linecapp, FILE * restrict stream);

使用那个原型。

如果你真的想使用原始原型,你也可以稍微改变一下练习并将函数重命名为mygetline

参考这两个讨论(第二个最相关):

  • Why do I get a "conflicting types for getline" error when compiling the longest line example in chapter 1 of K&R2?
  • Can an ANSI C-compliant implementation include additional functions in its standard library?

解决方案是在包含 stdio.h:

之前添加这两行
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>

这设置使用 POSIX.1-2001 标准编译代码,而不是 POSIX.1-2008 标准,其中 GNU getline() 扩展被添加到标准.