Копирование строк из среды extern char в C - PullRequest
0 голосов
/ 28 апреля 2018

У меня есть вопрос, относящийся к extern char **environ. Я пытаюсь создать программу на C, которая считает размер списка окружения, копирует его в массив строк (массив массивов символов), а затем сортирует его по алфавиту с помощью пузырьковой сортировки. Он будет печататься в порядке name=value или value=name в зависимости от значения формата.

Я пытался использовать strncpy, чтобы получить строки из окружения в мой новый массив, но значения строк вышли пустыми. Я подозреваю, что пытаюсь использовать окружающую среду так, как я не могу, поэтому я ищу помощь. Я пытался искать помощь в Интернете, но эта конкретная программа очень ограничена. Я не могу использовать 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 на ту же тему, но я начал заново на другом файле.

Ответы [ 3 ]

0 голосов
/ 28 апреля 2018

Чтобы получить переменные среды, вам нужно объявить 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 символов могут быть слишком короткими для хранения переменных env. Например, моя LS_COLORS переменная огромен, длиной не менее 1500 символов. Возможно, вы захотите сделать list[i] = malloc звонки на основе strlen(environ[i])+1.

Другое дело: ваш обмен

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]): это даст вам длину строки.

0 голосов
/ 28 апреля 2018

Есть несколько проблем с вашим кодом, в том числе:

  • Выделение «неправильного» размера для list и sorted (вы умножаете на sizeof(char **), но должны умножаться на sizeof(char *), потому что вы выделяете массив char *. Эта ошибка не будет на самом деле больно на этот раз. Использование sizeof(*list) позволяет избежать проблемы.
  • Выделение неправильного размера для элементов в list и sorted. Вам нужно использовать strlen(env[i]) + 1 для размера, помня, чтобы допустить нулевое значение, заканчивающее строку.
  • Вы не проверяете распределение памяти.
  • Ваш цикл копирования строк использует strncpy() и не должен (на самом деле, вы должны редко использовать strncpy()), не в последнюю очередь потому, что он копирует только 4 или 8 байтов каждой переменной среды (в зависимости от того, в 32-битной или 64-битной системе), и это не гарантирует, что они являются строкой с нулевым завершением (только одна из многих причин для , а не , используя strncpy().
  • Ваш внешний цикл вашего кода сортировки в порядке; Ваш внутренний цикл на 100% фальшивый, потому что вы должны использовать длину той или иной строки, а не размер указателя, и ваши сравнения выполняются по одиночным символам, но тогда вы используете strcpy(), где вам просто нужно двигайте указатели вокруг.
  • Вы выделяете, но не используете sorted.
  • Вы не распечатываете отсортированную среду, чтобы продемонстрировать, что она отсортирована.
  • В вашем коде отсутствует окончательная }.

Вот простой код, который использует стандартную библиотеку C qsort() для сортировки и имитирует POSIX strdup() под именем dup_str() - вы можете использовать strdup(), если у вас есть POSIX.

#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;
}

Другие люди указали, что вы можете получить в среду через третий аргумент main(), используя прототип int main(int argc, char **argv, char **envp). Обратите внимание, что 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

Если вы хотите отсортировать значения вместо имен, вам придется проделать более сложную работу. Вам нужно определить, какой вывод вы хотите увидеть. Существует несколько способов обработки такого рода.

0 голосов
/ 28 апреля 2018

В среде 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++;
   }
 }
...