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



晚上好,

我正在学习Kernighan和Ritchie的经典著作《C编程语言》中的练习。

在一些地方,练习让您创建自己版本的函数,该函数与标准库中函数的名称重复。与其为我的版本创建一个替代名称,我真的想告诉编译器,我宁愿使用我的函数版本,也不愿使用标准库函数。

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

我试过使用#unde,但似乎无法实现。

我搜索了之前的其他类似问题,找到了[这一个][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] = '';
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

答案

请参阅以下讨论:为什么我会得到一个";getline的冲突类型";在编译K&R2?

解决方案是在包含stdio.h:之前添加这两行

#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>

这设置了使用POSIX.1-2001标准编译代码,而POSIX.1-2008标准中添加了GNU getline()扩展名。

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

  • 为什么在编译K&R2
  • 符合ANSI C标准的实现是否可以在其标准库中包含其他功能

解决方案是在包括stdio.h:之前添加这两行

#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200112L
#include <stdio.h>

这设置了使用POSIX.1-2001标准编译代码,而POSIX.1-2008标准中添加了GNU getline()扩展名。

我认为您的练习希望让您学习动态注入共享库

gcc -shared -o libname.so filename.c一样编译文件(不带static)

在OSX上,您需要设置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,这取决于你的外壳。

则CCD_ 11的结果看起来像:

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

K&R现在是一本(非常)古老的书,你需要考虑到这一点。声明/定义getline与手册所说的完全一样(实际上)。当你在OSX上时,手册上写着:

概要

#include <stdio.h>
ssize_t
getline(char ** restrict linep, size_t * restrict linecapp, FILE * restrict stream);

使用原型。

如果您真的希望使用原始原型,您也可以稍微更改练习并将函数重命名为mygetline

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

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

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

最新更新