Копирование строк из среды extern char в C
У меня есть вопрос, касающийся 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 ответа
Есть несколько проблем с вашим кодом, в том числе:
- Выделение "неправильного" размера для
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
Если вы хотите отсортировать значения вместо имен, вам нужно проделать более сложную работу. Вам нужно определить, какой вывод вы хотите увидеть. Есть несколько способов обработки такого рода.
В среде 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
как это:
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])
: это даст вам длину строки.