#arrays #c #memory-management #dynamic-memory-allocation #sizeof
#массивы #c #управление памятью #динамическое выделение памяти #sizeof
Вопрос:
Допустим, у нас есть массив строк, инициализированный следующим образом:
char **a = malloc(3 * sizeof(char*)); //edited
a[0]="asd";
a[1]="fghj";
a[2]="klzxc";
Как мы можем напечатать первое измерение этого 2D-массива (3)?
Ответ №1:
Как мы можем напечатать первое измерение этого 2d-массива (3)?
Вы должны отслеживать количество элементов, которые вы выделили в отдельной переменной:
size_t num_elements = 3;
char **a = malloc( sizeof *a * num_elements );
if ( a )
{
a[0] = "asd"; // NOTE: these lines store the addresses of the string
a[1] = "fghj"; // literals in a[0], a[1], and a[2] - you are not copying
a[2] = "klzxc"; // the *contents* of each string, just its address
}
...
for ( size_t i; i < num_elements; i )
printf( "a[%zu] = %sn", i, a[i] );
Указатель, независимо от типа, указывает (хранит адрес) на один объект. Этот единственный объект может быть первым в большей последовательности объектов, но нет способа определить это по самому значению указателя. Если у вас есть следующее:
char ** char * char
--- --- --- --- --- ---
a:| | ---> | | a[0] -----> |'a'|'s'|'d'| 0 |
--- --- --- --- --- ---
| | a[1] ---
--- | --- --- --- --- ---
| | a[2] - -> |'f'|'g'|'h'|'j'| 0 |
--- | --- --- --- --- ---
|
| --- --- --- --- --- ---
---> |'k'|'l'|'z'|'x'|'c'| 0 |
--- --- --- --- --- ---
Вы не можете знать из a
самого себя, что он указывает на первый из 3 объектов; вы не можете знать из каждого a[i]
самого себя, что он указывает на последовательность char
объектов. Эта информация должна отслеживаться отдельно. В случае a
у нас есть отдельная переменная, num_elements
, которая отслеживает, сколько элементов может быть a
. В случае каждого a[i]
из них у нас есть строковый терминатор, который сообщает нам, какой длины каждая строка.
Комментарии:
1. У вас есть шаблон для них, не так ли? 😉
2. @anastaciu: Можно так подумать, но нет, я каждый раз делаю их с нуля. Это не займет много времени. И я действительно не хочу работать над тем, над чем я должен работать.
3. Вы знаете, что они говорят, практика совершенствует, это отличная, хорошая работа.
Ответ №2:
Как мы можем напечатать первое измерение этого 2D-массива (3)?
Пара предыдущих пунктов:
Если быть точным char **a
, это не 2D-массив, это указатель на указатель char
.
Более безопасным способом использования этого выделения памяти было бы использование разыменованного указателя вместо типа, этот метод облегчает сопровождение кода:
char **a = malloc(sizeof *a * 3);
Следует также отметить, что строковые литералы, назначенные этим указателям, доступны только для чтения и не могут быть изменены.
Чтобы ответить на ваш вопрос:
Чтобы узнать первое измерение, то есть количество назначенных указателей, вам нужно помнить об этом, возможно, сохранить его, нет переносимого способа получить количество указателей, которые вы выделили после факта.
Однако для этого существуют непереносимые методы:
size_t size = _msize(a)/sizeof *a; //Windows
size_t size = malloc_usable_size(a)/sizeof *a; //Linux
size_t size = malloc_size(a)/sizeof *a; //Mac OS
Поскольку функции возвращают количество выделенных байтов, деление их возвращаемого значения на размер указателя возвращает фактическое количество указателей.
Если вам нужны фактические редактируемые массивы символов:
Если вы хотите иметь массив символов, который вы действительно можете редактировать и изменять, вы также должны выделить память для каждого из этих трех указателей.
например
for(int i = 0; i < 3; i )
a[i] = malloc(4) //for char arrays of 3 chars null byte
После этого, чтобы назначить строку, вам нужно будет использовать что-то вроде strcpy
или предпочтительнее memcpy
.
например
memcpy(a[0], "asd", 4); //copies "asd" to a[0]