У меня есть больше чем одно сомнение, поэтому терпите меня. Кто-то может сказать мне, почему этот код перестал работать?
#include<stdio.h>
void main(int argc,char **argv) /*assume program called with arguments aaa bbb ccc*/
{
char **list={"aaa","bbb","ccc"};
printf("%s",argv[1]);/*prints aaa*/
printf("%s",list[1]); /*fails*/
}
Я предположил, что это имело некоторое отношение к указателю на материал указателя, который я не понимаю ясно. Таким образом, я попробовал:
#include<stdio.h>
void main()
{
char **list={"aaa","bbb","ccc"};
char *ptr;
ptr=list;
printf("%s",ptr);/*this prints the first string aaa*/
/* My second question is how do i increment the value
of ptr so that it points to the second string bbb*/
}
Между чем различие char *list[]
и char **list
и в том, какие ситуации оба идеальны, чтобы использоваться? Еще одной вещью, смущающей меня, является argv специальное предложение? когда я передаю char **list
к другой функции, принимающей это, позволил бы мне получить доступ к содержанию путем, я мог с argv
, это также перестало работать.
Я понимаю, что подобные вопросы задали в прошлом, но я, может казаться, не нахожу то, в чем я нуждаюсь. раз так может кто-то отправлять необходимые ссылки.
Вы должны использовать CHAR * Список [] = {"AAA", "BBB", "CCC" };
вместо Char ** Список = {«AAA», «BBB», «CCC»};
. Вы используете CHAR * Список [] = {...};
- объявить массив указателей, но вы используете Char **
, чтобы пройти указатель на один или несколько указателей на функцию.
T * x []
= массив указателей T ** x
= указатель на указатель P.S. Отвечая на Эеон: есть только одно использование, что я могу подумать о создании указателя на указатель (в качестве фактической заявленной переменной, не в качестве параметра функции или временной, созданной Оператором &
): Ручка . Короче говоря, ручка - это указатель на указатель, где Handl; E принадлежит пользователю, но указатель, который он указывает, чтобы быть изменены по мере необходимости по ОС или библиотеке.
Ручки широко использовались по всей старой Mac OS. Поскольку Mac OS был разработан без технологии виртуальной памяти, единственным способом сохранить кучу от быстрое получение фрагментации - это использовать ручки практически во всех ассигновании памяти. Это позвольте ОС перемещать память по мере необходимости для компактного кучи и открывать более крупные, смежные блоки свободной памяти.
Истина в том, что эта стратегия в лучшем виде просто «высосала меньше». Существует огромный список недостатков:
MEMCPY ()
. Возможно, еще несколько, что я забыл. Помните, что все эти недостатки были еще более привлекательны, чем использование только указателей и быстро фрагментируя кучу, особенно на первых Mac, которые имели только 128k ОЗУ. Это также дает некоторое представление о том, почему Apple была идеально рад отюжем все это и пойти в BSD, тогда у них была возможность, когда вся их линейка продукта имела единицы управления памятью.
Лучший источник для изучения сложностей C - это программирование книги эксперта по Peter Van der Linden ( http://www.amazon.co.uk/expert-programming-peter-van-linden/dp / 0131774298 ).
Название книги вводит в заблуждение, потому что они очень легко читают новичками, я думаю.
запустите приложение в режиме отладки, F5 в основном представляет собой сочетание клавиш в visual studio. Установите точку останова в этом заявлении. Используйте F10 для перехода к следующей инструкции. Как только отладчик выполнит следующую инструкцию, вы увидите создаваемый объект.
-121--4648519-Я бы инкапсулировал создание класса, который вам нужен на заводе.
Таким образом, вы будете иметь одну точку входа, когда вам нужно изменить свое базовое имя или правила сопоставления языка с правильным классом.
class YourClassFactory {
private $_language;
private $_basename = 'yourclass';
public YourClassFactory($language) {
$this->_language = $language;
}
public function getYourClass() {
return $this->_basename . '_' . $this->_language;
}
}
и затем, когда вы должны использовать его:
$yourClass = $yourClassFactoryInstance->getYourClass();
$yourClass::myFunctionName();
-121--975507- char * * x указывает на массив указателей символов, однако это может быть не так, как компилятор хранит {«aaa», «bbb», «ccc»} в памяти. char * x [] приведет к созданию правильного кода независимо от того, как компилятор хранит массив указателей.
»... предполагается, что у него есть что делать С указателем на то указатель вещи, Что я не понимаю понятно ».
Прежде всего, давайте разберемся с мелочами. main
возвращает int , а не void. Если в документации вашего компилятора специально не указано, что он поддерживает void main ()
, используйте int main (void)
или int main (int argc, char ** argv)
.
Теперь давайте вернемся на минуту назад и поговорим о различиях между указателями и массивами. Прежде всего следует помнить, что массивы и указатели совершенно разные вещи . Возможно, вы где-то слышали или читали, что массив - это просто указатель; это неверно. В большинстве случаев тип выражения массива будет неявно преобразован из «N-элементного массива T» в «указатель на T» (тип распадается на тип указателя) и его значение установлено так, чтобы указывать на первое, что есть в массиве, за исключением случаев, когда выражение массива является операндом операторов sizeof
или адреса ( &
), либо когда выражение массива - это строковый литерал, используемый для инициализации другого массива.
Массив - это блок памяти размером для хранения N элементов типа T; указатель - это блок памяти размером для хранения адреса одного значения типа T. Вы не можете присвоить новое значение объекту массива; т.е. следующее недопустимо:
int a[10], b[10];
a = b;
Обратите внимание, что строковый литерал (например, «aaa») также является выражением массива; тип представляет собой массив из N элементов char (const char в C ++), где N - длина строки плюс завершающий 0.Строковые литералы имеют статический размер; они выделяются при запуске программы и существуют до выхода из программы. Они также не могут быть записаны (попытка изменить содержимое строкового литерала приводит к неопределенному поведению). Например, тип выражения «aaa» - это 4-элементный массив char со статическим размером. Как и другие выражения массива, строковые литералы в большинстве случаев переходят от типов массива к типам указателей. Когда вы пишете что-то вроде
char *p = "aaa";
, выражение массива «aaa» распадается с char [4]
на char *
, и его значение является адресом первой «a» множество; этот адрес затем копируется в p
.
Если литерал используется для инициализации массива char, однако:
char a[] = "aaa";
, то тип не преобразуется; литерал по-прежнему обрабатывается как массив, и содержимое массива копируется в a
(и a
неявно имеет размер, чтобы содержать содержимое строки плюс 0 терминатор). Результат примерно эквивалентен записи
char a[4];
strcpy(a, "aaa");
Когда выражение массива типа T a [N]
является операндом оператора sizeof
, результатом является размер всего массива в байтах: N * sizeof (T). Когда это операнд оператора адресации ( &
), результатом является указатель на весь массив, а не указатель на первый элемент (на практике это то же самое значение , но типы разные):
Declaration: T a[N]; Expression Type "Decays" to Value ---------- ---- ----------- ------ a T [N] T * address of a[0] &a T (*)[N] address of a sizeof a size_t number of bytes in a (N * sizeof(T)) a[i] T value of a[i] &a[i] T * address of a[i] sizeof a[i] size_t number of bytes in a[i] (sizeof (T))
Обратите внимание, что выражение массива a
распадается на тип T *
или указатель на T.Это тот же тип, что и выражение & a [0]
. Оба этих выражения дают адрес первого элемента в массиве. Выражение & a
имеет тип T (*) [N]
или указатель на массив из N элементов T, и оно дает адрес самого массива, а не первого элемента. . Поскольку адрес массива совпадает с адресом первого элемента массива, a
, & a
и & a [0]
все дают то же значение , но не все выражения одинакового типа . Это будет иметь значение при попытке сопоставить определения функций с вызовами функций. Если вы хотите передать массив в качестве параметра функции, например
int a[10];
...
foo(a);
, тогда соответствующее определение функции должно быть
void foo(int *p) { ... }
То, что получает foo
, является указателем на int, а не массив int. Обратите внимание, что вы можете называть его либо foo (a)
, либо foo (& a [0])
(или даже foo (& v)
, где v
- простая переменная типа int, хотя if foo
ожидает массив, который вызовет проблемы). Обратите внимание, что в контексте объявления параметра функции int a []
совпадает с int * a
, но только истинно в этом контексте. Откровенно говоря, я думаю, что форма int a []
вызывает множество путаниц в отношении указателей, массивов и функций, и ее использование не рекомендуется.
Если вы хотите передать указатель на массив функции, такой как
int a[10];
foo(&a);
, тогда соответствующее определение функции должно быть
void foo(int (*p)[10]) {...}
, и если вы хотите сослаться на конкретный элемент, вы должны разыменовать указатель перед , применив нижний индекс:
for (i = 0; i < 10; i++)
(*p)[i] = i * i;
Теперь давайте бросим гаечный ключ в работу и добавим второе измерение в массив:
Declaration: T a[M][N]; Expression Type "Decays" to Value ---------- ---- ----------- ------ a T [M][N] T (*)[N] address of a[0] &a T (*)[M][N] address of a sizeof a size_t number of bytes in a (M * N * sizeof(T)) a[i] T [N] T * address of a[i][0] &a[i] T (*)[N] address of a[i] sizeof a[i] size_t number of bytes in a[i] (N * sizeof(T)) a[i][j] T value of a[i][j] &a[i][j] T * address of a[i][j]
Обратите внимание, что в этом случае,оба a
и a [i]
являются выражениями массива, поэтому их соответствующие типы массивов в большинстве случаев будут преобразованы в типы указателей; a
будет преобразован из типа «M-element array of N-element array of T» в «указатель на N-элементный массив T», а a [i]
будет преобразован из "N-элементного массива T" в "указатель на T". И снова, a
, & a
, a [0]
, & a [0]
и & a [0] [ 0]
все будут давать одинаковые значения (адрес начала массива), но не будут все одинаковыми типами . Если вы хотите передать 2-мерный массив функции, например:
int a[10][20];
foo(a);
, тогда соответствующее определение функции должно быть
void foo(int (*p)[20]) {...}
. Обратите внимание, что это идентично передаче указателя на 1-мерный массив (кроме размера массива в разных примерах). В этом случае, однако, вы должны применить индекс к указателю, например
for (i = 0; i < 10; i++)
for (j = 0; j < 20; j++)
p[i][j] = i * j;
. В этом случае вам не нужно явно разыменовать p
, потому что выражение p [i]
неявно уважает его ( p [i] == * (p + i)
).
Теперь давайте посмотрим на выражения указателей:
Declaration: T *p; Expression Type Value ---------- ---- ------ p T * address of another object of type T *p T value of another object of type T &p T ** address of the pointer sizeof p size_t number of bytes in pointer (depends on type and platform, anywhere between 4 and 8 on common desktop architectures) sizeof *p size_t number of bytes in T sizeof &p size_t number of bytes in pointer to pointer (again, depends on type and platform)
Все довольно просто. Тип указателя содержит адрес другого объекта типа T; разыменование указателя ( * p
) дает значение по этому адресу, а взятие адреса указателя ( & p
) дает местоположение объекта указателя (указатель на указатель) .Применение sizeof
к значению указателя даст количество байтов в указателе, а не количество байтов, на которые указывает указатель.
Теперь, предполагая, что вы зашли так далеко и еще не умерли от скуки, давайте посмотрим, как все это применимо к вашему коду.
Вы хотите создать массив указателей на char и инициализировать его тремя строковыми литералами, поэтому вы должны объявить его как
char *list[] = {"aaa", "bbb", "ccc"};
Размер массива list
имеет неявный размер хранить 3 элемента типа char *
. Хотя строковые литералы «aaa», «bbb» и «ccc» появляются в инициализаторе, они не используются для инициализации массива char; следовательно, они распадаются от выражений типа char [4]
до типа char *
. Каждое из этих значений указателя копируется в элементы списка
.
Когда вы передаете список
функции, такой как
foo(list);
, тип списка распадается с «4-элементный массив указателя на char» ( char * [4]
) на «указатель на указатель на char» ( char **
), поэтому функция приема должна иметь определение
void foo(char **p) {...}
. Поскольку индексирование определяется в терминах арифметики указателя, вы можете использовать оператор индекса в указатель , как если бы это был массив char *
:
for (i = 0; i < 3; i++)
printf("%s\n", p[i]);
Кстати, вот как main
получает argv
как указатель на указатель на char ( char **
), а не как массив указателя на char.Помните, что с точки зрения объявления параметра функции, a []
идентично * a
, поэтому char * argv []
идентично char ** argv
.
Теперь, поскольку я не могу перестать печатать и вернуться к работе (преследование тупиковых ситуаций не весело ), давайте рассмотрим использование указателей и динамически выделяемой памяти.
Если вы хотите динамически распределять свой список во время выполнения (т. Е. Заранее не знаете, сколько строк находится в вашем списке), вы должны объявить list
как указатель на указатель на char, а затем вызвать malloc
, чтобы фактически выделить для него память:
char **list;
size_t number_of_strings;
...
list = malloc(number_of_strings * sizeof *list);
list[0] = "aaa";
list[1] = "bbb";
list[2] = "ccc";
...
Поскольку это присваивания, а не инициализации, буквальные выражения превращаются в указатели на char, поэтому мы копируем адреса из «aaa», «bbb» и т. Д. В записи в списке
. В этом случае список
- это , а не тип массива; это просто указатель на кусок памяти, выделенный где-то еще (в данном случае из кучи malloc). Опять же, поскольку индексирование массива определяется в терминах арифметики указателя, вы можете применить оператор индексации к значению указателя , как если бы это был массив. Тип выражения list [i]
- char *
. Не стоит беспокоиться о неявных преобразованиях; если вы передадите его в функцию как
foo(list)
, тогда определение функции будет
void foo(char **list) {...}
, и вы поставите индекс list , как если бы это был массив.
Пссст ... он готов?
Да, я думаю, он закончил.