【问题标题】:Copying strings from extern char environ in C从C中的extern char environ复制字符串
【发布时间】:2018-04-28 01:42:40
【问题描述】:

我有一个关于extern char **environ 的问题。我正在尝试制作一个 C 程序来计算环境列表的大小,将其复制到字符串数组(字符数组数组),然后使用冒泡排序按字母顺序对其进行排序。它将根据格式值以name=valuevalue=name 顺序打印。

我尝试使用strncpy 将字符串从 environ 获取到我的新数组,但字符串值显示为空。我怀疑我正在尝试以我无法使用的方式使用 environ,所以我正在寻求帮助。我试图在网上寻求帮助,但这个特定的程序非常有限。我不能使用system(),但我在网上找到的唯一帮助告诉我编写一个程序来进行这个系统调用。 (这没有帮助)。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char *argv[])
{
    char **env = environ;
    int i = 0;
    int j = 0;
    printf("Hello world!\n");
    int listSZ = 0;
    char temp[1024];
    while(env[listSZ])
    {
        listSZ++;
    }
    printf("DEBUG: LIST SIZE = %d\n", listSZ);
    char **list = malloc(listSZ * sizeof(char**));
    char **sorted = malloc(listSZ * sizeof(char**));
    for(i = 0; i < listSZ; i++)
    {
        list[i] = malloc(sizeof(env[i]) * sizeof(char));        // set the 2D Array strings to size 80, for good measure
        sorted[i] = malloc(sizeof(env[i]) * sizeof(char));
    }
    while(env[i])
    {
        strncpy(list[i], env[i], sizeof(env[i]));
        i++;
    }           // copy is empty???

    for(i = 0; i < listSZ - 1; i++)
    {
        for(j = 0; j < sizeof(list[i]); j++)
        {
            if(list[i][j] > list[i+1][j])
            {
                strcpy(temp, list[i]);
                strcpy(list[i], list[i+1]);
                strcpy(list[i+1], temp);
                j = sizeof(list[i]);                    // end loop, we resolved this specific entry
            }
            // else continue
        }
    }

这是我的代码,非常感谢帮助。为什么这个话题这么难找?是不是没有必要?

编辑:粘贴了错误的代码,这是同一主题的单独 .c 文件,但我从另一个文件开始。

【问题讨论】:

标签: c strncpy environ


【解决方案1】:

在 unix 环境中,环境是 main 的第三个参数。

试试这个:

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

int main(int argc, char *argv[], char **envp)
{

   while (*envp) {
   printf("%s\n", *envp);
   *envp++;
   }
 }

【讨论】:

  • 这是获取环境的另一种方式,但全局变量也是有效的,甚至可以在main()以外的函数中使用,而main()不必传递它的第三个参数。
  • 非常有趣,谢谢。为什么传递的参数数量有一个值,而可用环境变量的数量却没有类似的值?
【解决方案2】:

您的代码存在多个问题,包括:

  • listsorted 分配“错误”大小(您乘以sizeof(char **),但应该乘以sizeof(char *),因为您分配了char * 的数组。这个错误不会这次真的伤害了你。使用sizeof(*list)可以避免问题。
  • listsorted 中的元素分配了错误的大小。您需要使用strlen(env[i]) + 1 作为大小,记住允许终止字符串的空值。
  • 您没有检查内存分配。
  • 您的字符串复制循环正在使用 strncpy() 并且不应该(实际上,您应该很少使用 strncpy()),尤其是因为它只复制每个环境变量的 4 或 8 个字节(取决于您是否在 32 位或 64 位系统上),并且不能确保它们是空终止字符串(只是 使用 strncpy() 的众多原因之一。
  • “排序”代码的外循环正常;您的内部循环是 100% 虚假的,因为您应该使用一个或另一个字符串的长度,而不是指针的大小,并且您的比较是在单个字符上进行的,但您随后使用 strcpy(),您只需要移动指针。
  • 您分配但不使用sorted
  • 您不会打印 sorted 环境来证明它已排序。
  • 您的代码缺少最后的}

这是一些使用标准 C 库 qsort() 函数进行排序的简单代码,并模拟 POSIX strdup() 在名称 dup_str() 下 — 如果您有可用的 POSIX,则可以使用 strdup()

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

extern char **environ;

/* Can also be spelled strdup() and provided by the system */
static char *dup_str(const char *str)
{
    size_t len = strlen(str) + 1;
    char *dup = malloc(len);
    if (dup != NULL)
        memmove(dup, str, len);
    return dup;
}

static int cmp_str(const void *v1, const void *v2)
{
    const char *s1 = *(const char **)v1;
    const char *s2 = *(const char **)v2;
    return strcmp(s1, s2);
}

int main(void)
{
    char **env = environ;
    int listSZ;

    for (listSZ = 0; env[listSZ] != NULL; listSZ++)
        ;
    printf("DEBUG: Number of environment variables = %d\n", listSZ);

    char **list = malloc(listSZ * sizeof(*list));
    if (list == NULL)
    {
        fprintf(stderr, "Memory allocation failed!\n");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < listSZ; i++)
    {
        if ((list[i] = dup_str(env[i])) == NULL)
        {
            fprintf(stderr, "Memory allocation failed!\n");
            exit(EXIT_FAILURE);
        }
    }

    qsort(list, listSZ, sizeof(list[0]), cmp_str);

    for (int i = 0; i < listSZ; i++)
        printf("%2d: %s\n", i, list[i]);

    return 0;
}

其他人指出,您可以使用原型int main(int argc, char **argv, char **envp),通过main() 的第三个参数获取环境。请注意,Microsoft 明确支持这一点。它们是正确的,但您也可以通过environ 进入环境,即使在main() 以外的功能中也是如此。变量environ在POSIX定义的全局变量中是唯一的,没有在任何头文件中声明,所以你必须自己编写声明。

请注意,内存分配是错误检查的,错误报告在标准错误上,而不是标准输出。

很明显,如果你喜欢编写和调试排序算法,你可以避免使用qsort()。请注意,字符串比较需要使用strcmp(),但在对指针数组进行排序时,不能直接使用strcmp()qsort(),因为参数类型错误。

对我来说部分输出是:

DEBUG: Number of environment variables = 51
 0: Apple_PubSub_Socket_Render=/private/tmp/com.apple.launchd.tQHOVHUgys/Render
 1: BASH_ENV=/Users/jleffler/.bashrc
 2: CDPATH=:/Users/jleffler:/Users/jleffler/src:/Users/jleffler/src/perl:/Users/jleffler/src/sqltools:/Users/jleffler/lib:/Users/jleffler/doc:/Users/jleffler/work:/Users/jleffler/soq/src
 3: CLICOLOR=1
 4: DBDATE=Y4MD-
…
47: VISUAL=vim
48: XPC_FLAGS=0x0
49: XPC_SERVICE_NAME=0
50: _=./pe17

如果您想对值而不是名称进行排序,则必须做一些更艰苦的工作。您需要定义您希望看到的输出。有多种方法可以处理这种排序。

【讨论】:

  • 为什么在 dup_str? and pardon my pickiness, but len 中使用 memmove 而不是 memcpy 是一个不幸的名称,它实际上是一个大小,而不是字符串的长度。
  • 我总是使用memmove(),因为它总是有效的。由于memcy() 不存在,我假设您心中有memcpy()。它并不总是有效。当然,它可以在这里安全地使用,但我更喜欢更安全的编码。至于Len:那是我正在分配和复制的内存块的长度。
  • 您对memmove 的论证是有道理的,但对于len,名称size 对内存块的大小 来说不会那么混乱。 C 标准一致使用 size 来指代由 malloc 分配的对象的字节大小,以避免与包含数组的 length 混淆,用 a可能不同的单位和不包括空终止符的字符串的长度。避免潜在的混淆也是为了实现更安全的编码。
【解决方案3】:

要获取环境变量,你需要像这样声明main

int main(int argc, char **argv, char **env);

第三个参数是NULL-终止的环境变量列表。见:

#include <stdio.h>

int main(int argc, char **argv, char **environ)
{
    for(size_t i = 0; env[i]; ++i)
        puts(environ[i]);

    return 0;
}

这个的输出是:

LD_LIBRARY_PATH=/home/shaoran/opt/node-v6.9.4-linux-x64/lib:
LS_COLORS=rs=0:di=01;34:ln=01;36:m
...

另请注意,您的代码中的sizeof(environ[i]) 不会为您提供 字符串,它会得到一个指针的大小,所以

strncpy(list[i], environ[i], sizeof(environ[i]));

错了。 strncpy 的重点也是根据目的地, 不在源上,否则如果源大于目标,你 仍然会溢出缓冲区。正确的调用是

strncpy(list[i], environ[i], 80);
list[i][79] = 0;

请记住,strncpy 可能不会写入 '\0'-终止字节,如果 目的地不够大,所以你必须确保终止 细绳。另请注意,79 个字符可能太短,无法存储环境变量。例如,我的LS_COLORS 变量 很大,至少有 1500 个字符长。您可能希望基于strlen(environ[i])+1 进行list[i] = malloc 调用。

另一件事:你的交换

strcpy(temp, list[i]);
strcpy(list[i], list[i+1]);
strcpy(list[i+1], temp);
j = sizeof(list[i]);

仅当所有list[i] 指向相同大小的内存时才有效。由于 list[i] 是指针,因此更便宜的交换方式是 而是交换指针:

char *tmp = list[i];
list[i] = list[i+1];
list[i+1] = tmp;

这更有效,是一个 O(1) 操作,你不必担心如果 内存空间大小不同。

我不明白的是,你打算用j = sizeof(list[i]) 做什么?不仅 sizeof(list[i]) 返回指针的大小(它将是常量 对于所有list[i]),你为什么要搞乱内部的运行变量j 堵塞?如果您想离开循环,请执行break。而你正在寻找 strlen(list[i]): 这会给你字符串的长度。

【讨论】:

  • 非常感谢您的反馈,您为我提供了很多帮助。我想我在尝试获取指针的大小时不太了解我在使用什么。我会处理这个,看看结果如何。再次感谢!
  • 这是一种方法;使用environ 是另一个。
猜你喜欢
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 1970-01-01
  • 2023-04-11
  • 2012-08-16
  • 1970-01-01
  • 2022-01-06
相关资源
最近更新 更多