C - массив различий области видимости строк - PullRequest
1 голос
/ 23 сентября 2010

Я практиковал массив строк без начальных значений.

Попытка 1

#include <stdio.h>

char *array[] = {};
int main(int argc, char *argv[]) {
    array[0]="Hello";
    array[1]="World";

    char **i = array;
    while (*i) {
        printf("%d %s\n", i, *i);
        i++;
    }
}

$ gcc array_of_strings.c && ./a.out

6293704 Hello
6293712 World

Работает нормально.

Попытка 2

Я думал, что смогу переместить указатель массива в основную функцию.

#include <stdio.h>

int main(int argc, char *argv[]) {
    char *array[] = {};
    array[0]="Hello";
    array[1]="World";

    char **i = array;
    while (*i) {
        printf("%d %s\n", i, *i);
        i++;
    }
}

$ gcc array_of_strings.c && ./a.out

-1899140568 (j͎?
-1899140560 World
-1899140552 ???%Y
-1899140544 1?I??^H??H???PTI???@
-1899140536 d?͎?
Segmentation fault

Да, а почему не работает?Это приводит к "Сегментации неисправности" с уродливым выводом.Может ли кто-нибудь объяснить, почему я не должен так поступать?

Ответы [ 5 ]

8 голосов
/ 23 сентября 2010

Вы выделяете массив с нулевыми элементами, а затем добавляете к нему два указателя. Это записывает вне массива и вызывает переполнение буфера.

Кстати, это перезаписывает неиспользуемую память, если массив выделен глобально, но перезаписывает стек, когда он выделяется в main ().

2 голосов
/ 23 сентября 2010

Две проблемы.

  1. Вы не выделяете место для элементов массива. С вашим пустым списком инициализатора вы выделяете пустой массив. Когда вы пишете в array[0] и array[1], вы пишете в память, которой вы не владеете.

  2. Вам повезло, когда вы выделяете массив глобально. Глобальные (то есть статически выделенные) блоки памяти, как правило, заполнены нулями. Это хорошо для вас, потому что ваш цикл while зависит от того, является ли он нулевым указателем в конце массива.

    Когда вы размещаете в стеке и обращаетесь к памяти за концом массива, вы получаете то, что уже происходит в стеке, что может быть любым произвольным мусором. Ваш цикл while (*i) не получает ожидаемый указатель NULL, поэтому он будет продолжать считывать данные мусора, пока не найдет нули, похожие на указатель NULL.

Чтобы исправить # 1, укажите явную длину массива. Чтобы исправить # 2, вы должны явно добавить NULL-указатель в конец массива.

char *array[3];
array[0]="Hello";
array[1]="World";
array[2]=NULL;

Кроме того, как ни странно, указатели не гарантируют того же размера, что и int с. Для печати указателей лучше использовать %p, а не %d.

printf("%p %s\n", i, *i);
2 голосов
/ 23 сентября 2010

В каждом из этих случаев вы выделяете пустой массив, а затем пытаетесь вставить в него элементы.C не производит никакого изменения размера массивов;если вы вставите элементы в массив, превышающий его длину, он просто начнет перезаписывать любые другие данные, которые могут появиться после массива.В первом случае, когда массив является глобальным, вам удаётся повезти, и вы, очевидно, ничего не нарушаете, записывая за конец массива, и, более того, вам повезло, что после вставленных вами двух значений есть нулевое значениеТаким образом, ваш цикл заканчивается в соответствующем месте.Во втором случае вы перезаписываете свой стек, который используется для хранения локальных переменных, передачи аргументов, возвращаемых значений и местоположений возврата между функциями.Таким образом, запись и последующее чтение после конца вашего массива заставляет вас писать по всему стеку и читать случайные бессмысленные значения.

1 голос
/ 23 сентября 2010

Выполняя это со второй попытки, вы объявляете массив в стеке, а затем вы переопределяете переданные аргументы и в некоторый момент адрес возврата в конце функции.чисто случайно.Вы по-прежнему переписываете память, которую вы не должны, но в настоящее время не причиняет вреда.

0 голосов
/ 23 сентября 2010

Вы не должны делать это обоими способами. В первом случае вам просто повезло, что вы не перезаписали какую-то критичную для процесса память, во втором случае вы разбили стек. Оба случая могут произойти случайным образом из-за того, что вы записываете в память, которую вы не зарезервировали.

char *array[] = {};

Это резервирует память для нулевых записей, но с [0] = ... вы записываете элемент в позицию, для которой вы не выделяли память. Вы должны прочитать о том, как 1) определять статические массивы или 2) динамически распределять массивы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...