Как получить доступ к отдельному символу из массива строк в c?

просто пытаюсь понять, как обращаться к одному символу в массиве строк. Кроме того, это, конечно, позволит мне понять указатели на указатели подписки в целом. Если у меня есть char **a и я хочу достичь 3-го символа 2-й строки, делает эту работу:**((a+1)+2)? Похоже, так и должно быть...

6 ответов


почти, но не совсем. Правильный ответ:

*((*(a+1))+2)

потому что вам нужно сначала удалить ссылку на один из фактических указателей строки, а затем удалить ссылку на выбранный указатель строки до нужного символа. (Обратите внимание, что я добавил дополнительные скобки для ясности в порядке операций).

кроме того, это выражение:

a[1][2]

также будет работать!....и, возможно, было бы предпочтительнее, потому что намерение того, что вы есть попытка сделать это более очевидна, и сама нотация более сжата. Эта форма может быть не сразу очевидна для людей, незнакомых с языком, но поймите, что причина работы нотации массива заключается в том, что в C операция индексирования массива на самом деле является просто сокращением эквивалентной операции указателя. т. е.: *(a+x) совпадает с[x]. Таким образом, расширяя эту логику до исходного вопроса, существуют две отдельные операции де-ссылки указателя, каскадированные вместе, в результате чего выражение a[x] [y] эквивалентно общей форме *((*(a+x))+y).


вам не нужно использовать указатели.

int main (int argc, char **argv){

printf ("третий символ argv[1] является [%c].\n", argv[1][2]);

}

затем:

$ ./ главный привет третий персонаж argv[1] является [l].

это единица и l.

вы можете использовать указатели, если хотите...

*(argv[1] +2)

или даже

*((*(a+1))+2)

Как кто-то указал выше.

Это потому, что имена массивов указателей.


Iirc, строка на самом деле является массивом символов, поэтому это должно работать:

a[1][2]

цитата из Википедии статьи на указателях C -

в C индексирование массива формально определяется в терминах арифметики указателя; то есть, спецификация языка требует, чтобы array[i] был эквивалентен *(array + i). Таким образом, в C массивы можно рассматривать как указатели на последовательные области памяти (без пробелов), и синтаксис доступа к массивам идентичен тому, который может использоваться для разыменования указатели. Например, массив может быть объявлен и используется следующим образом:

int array[5];      /* Declares 5 contiguous (per Plauger Standard C 1992) integers */
int *ptr = array;  /* Arrays can be used as pointers */
ptr[0] = 1;        /* Pointers can be indexed with array syntax */
*(array + 1) = 2;  /* Arrays can be dereferenced with pointer syntax */

Так, в ответ на ваш вопрос - да, указатели на указатели могут использоваться как массив без какого-либо другого объявления вообще!


попробовать a[1][2]. Или *(*(a+1)+2).

в основном, ссылки на массивы являются синтаксическим сахаром для разыменования указателей. a[2] совпадает с A+2, а также с 2[a] (если вы действительно хотите нечитаемый код). Массив строк совпадает с двойным указателем. Таким образом, вы можете извлечь вторую строку, используя[1] или *(a+1). Затем вы можете найти третий символ в этой строке (назовите его " b " сейчас) с помощью b[2] или *(b + 2). Замена исходной второй строки на 'b', мы получаем либо [1] [2], либо *(*(a+1)+2).


theres блестящее объяснение программирования C в книге Hacking the art of exploitation 2nd Edition Джона Эриксона, в которой обсуждаются указатели, строки, достойные упоминания только для раздела объяснения программирования https://leaksource.files.wordpress.com/2014/08/hacking-the-art-of-exploitation.pdf.

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

заголовки

примеры файлов заголовков, доступных для манипулирования переменными, которые вы, вероятно, будете использовать.

С stdio.h - http://www.cplusplus.com/reference/cstdio/

stdlib.h - http://www.cplusplus.com/reference/cstdlib/

строку.h - http://www.cplusplus.com/reference/cstring/

ограничения.ч - http://www.cplusplus.com/reference/climits/

функции

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

malloc() - http://www.cplusplus.com/reference/cstdlib/malloc/

calloc() - http://www.cplusplus.com/reference/cstdlib/calloc/

strcpy() - http://www.cplusplus.com/reference/cstring/strcpy/

"скомпилированная программа память делится на пять сегментов: текст, данные, bss, куча и стек. Каждый сегмент представляет собой специальную часть памяти, выделенную для определенной цели. Сегмент текста также иногда называют сегментом кода. Именно здесь расположены собранные инструкции машинного языка программы".

"выполнение инструкций в этом сегменте нелинейно, благодаря вышеупомянутым структурам и функциям управления высокого уровня, которые компилировать в ветвь, прыгать и вызывать инструкции на языке ассемблера. В виде программы выполняется, EIP устанавливается в первую инструкцию в сегменте текста. Этот затем процессор следует циклу выполнения, который выполняет следующее:"

"1. Читает инструкцию, на которую указывает EIP"

"2. Добавляет длину байта инструкции в EIP"

"3. Выполняет инструкцию, прочитанную в шаге 1"

"4. Возвращается к шагу 1"

"иногда инструкция будет прыжком или инструкцией вызова, которая изменяет EIP на другой адрес памяти. Процессор не заботьтесь об изменении, потому что оно ожидает, что выполнение будет нелинейным в любом случае. Если EIP изменен на Шаге 3, процессор просто вернется к шагу 1 и прочитает инструкцию, найденную по адресу того, что EIP был изменен к".

"разрешение на запись отключено в сегменте текста, так как оно не используется для хранения переменных, только кода. Это не позволяет людям фактически изменять код программы; любая попытка записи в этот сегмент памяти приведет к тому, что программа предупредит пользователя о том, что произошло что-то плохое, и программа будут убиты. Еще одним преимуществом этого сегмента является то, что он доступен только для чтения может быть разделен между различными копиями программы, что позволяет несколько расстрелы программа при этом без каких-либо проблем. Он должен также следует отметить, что этот сегмент памяти имеет фиксированный размер, так как ничего не изменения в нем".

"данные и сегменты bss используются для хранения глобальной и статической программы переменная. Сегмент данных заполняется инициализированными глобальными и статическими переменными, а сегмент bss - их неинициализированными аналогами. Хотя эти сегменты доступны для записи, они также имеют фиксированный размер. Помнить это глобальные переменные сохраняются, несмотря на функциональный контекст (как переменная j в предыдущих примерах). Как глобальные, так и статические переменные могут сохраняться, поскольку они хранятся в собственных сегментах памяти".

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

"вся память в куче управляется алгоритмами распределителя и освобождателя, которые соответственно резервируют область памяти в куче для использования и удаляют резервирования, чтобы разрешить повторное использование этой части памяти для последующего резервирования. Куча будет расти и сжиматься в зависимости от того, как много памяти зарезервировано для использования. Это означает, что программист использует кучу функции распределения могут зарезервировать и освободить память на летать. Рост куча движется вниз к более высоким адресам памяти".

"сегмент стека также имеет переменный размер и используется как временная блокнот для хранения локальных переменных функции и контекста во время вызовов функций. Это то, на что смотрит команда backtrace GDB. Когда программа вызывает функцию, эта функция будет иметь свой собственный набор переданных переменных, а код функции будет находиться в другом месте памяти в сегменте текста (или кода). С контекст и EIP должны изменяться при вызове функции, стек используется для запоминания всех переданных переменных, расположение, в которое EIP должен вернуться после завершения функции, и все локальные переменные, используемые этой функцией. Вся эта информация хранится вместе в стеке в том, что в совокупности называется фреймом стека. Стек содержит много кадров стека".

"в общих чертах информатики, стек-это абстрактная структура данных, которая часто используется. Он имеет первый-в, последний-вне (FILO) приказывая , что означает, что первый элемент, помещенный в стек, является последним элементом, который выйдет из него. Представьте, что вы надеваете бусины на нитку с узлом на одном конце-вы не можете снять первую бусину, пока не уберете все остальные бусины. Когда элемент помещается в стек, он называется pushing, а когда элемент удаляется из стека, он называется popping".

"как следует из названия, стек сегмент памяти-это, по сути, структура данных стека, содержащая кадры стека. Регистр ESP используется для отслеживания адреса конца стека, который постоянно меняется по мере того, как элементы вставляются и выскакивают из него. Поскольку это очень динамическое поведение, имеет смысл, что стек также не имеет фиксированного размера. Напротив динамического роста кучи, по мере изменения стека s по размеру, он растет вверх в визуальном списке памяти, к более низкой памяти адреса".

"природа FILO стека может показаться странной, но поскольку используется стек чтобы сохранить контекст, это очень полезно. Когда вызывается функция, несколько вещей помещаются в стек вместе в кадре стека. Регистр EBP-иногда называемый указателем кадра (FP) или указателем локальной базы (LB) -используется для ссылки на локальные переменные функции в текущем кадре стека. Каждый кадр стека содержит параметры функции, ее локальные переменные и два указатели, которые необходимы для того, чтобы вернуть все так, как было: указатель сохраненного кадра (SFP) и обратный адрес. Этот SFP используется для восстановления EBP до его предыдущего значения и обратного адреса используется для восстановления EIP к следующей инструкции, найденной после вызова функции. Это восстанавливает функциональный контекст предыдущего стека кадр".

строки

"в C массив-это просто список из n элементов определенного типа данных. Ля 20-символьный массив-это просто 20 смежных символов, расположенных в памяти. Массивы также называются буферами".

#include <stdio.h>

int main()
{
    char str_a[20];
    str_a[0] = 'H';
    str_a[1] = 'e';
    str_a[2] = 'l';
    str_a[3] = 'l';
    str_a[4] = 'o';
    str_a[5] = ',';
    str_a[6] = ' ';
    str_a[7] = 'w';
    str_a[8] = 'o';
    str_a[9] = 'r';
    str_a[10] = 'l';
    str_a[11] = 'd';
    str_a[12] = '!';
    str_a[13] = '\n';
    str_a[14] = 0;
    printf(str_a);
}

"в предыдущей программе 20-элементный массив символов определяется как str_a, и каждый элемент массива записывается, один за одним. Обратите внимание, что число начинается с 0, а не с 1. Также обратите внимание, что последний символ-0".

"(это также называется нулевым байтом.) Массив символов был определен, поэтому 20 для него выделяются байты, но фактически используется только 12 из этих байтов. Программирование нулевого байта в конце используется в качестве символа разделителя, чтобы сообщить любой функции, которая имеет дело со строкой, чтобы остановить операции прямо там. Остальные дополнительные байты являются просто мусором и будут проигнорированы. Если в пятый элемент массива символов вставлен нулевой байт, то функция printf ()".

"С каждого символ в массиве символов является кропотливым и строки используются довольно часто, набор стандартных функций был создан для обработки строк. Например, функция strcpy () скопирует строку из источника в назначение, повторяя исходную строку и копируя каждый байт в назначение (и останавливаясь после копирования байта завершения null)".

"порядок аргументов функций аналогичен назначению синтаксиса сборки Intel в первую очередь и затем источник. В char_array.программа c может быть переписана с помощью strcpy (), чтобы выполнить то же самое с помощью библиотеки строк. Следующая версия программы char_array, показанная ниже, включает строку.h поскольку он использует строковую функцию".

#include <stdio.h>
#include <string.h>

int main() 
{
    char str_a[20];
    strcpy(str_a, "Hello, world!\n");
    printf(str_a);
}

найти более подробную информацию о C струны

http://www.cs.uic.edu / ~jbell/CourseNotes/C_Programming/CharacterStrings.html

http://www.tutorialspoint.com/cprogramming/c_strings.htm

указатели

"регистр EIP-это указатель, который "указывает" на текущую инструкцию во время выполнения программы, содержащей ее адрес памяти. Идея указателей также используется в C. Поскольку физическая память фактически не может быть перемещена, информация в ней нужно скопировать. Это может быть очень вычислительно дорого копировать большие куски памяти для использования различными функциями или в разных местах. Это также дорого с точки зрения памяти, так как пространство для новой целевой копии должно быть сохранено или выделено до копирования источника. Указатели являются решением этой проблемы. Вместо того, чтобы копировать большой блок памяти, гораздо проще передать адрес начала этого блока память!--43-->".

"указатели в C можно определить и использовать как любой другой тип переменной. Поскольку память в архитектуре x86 использует 32-разрядную адресацию, указатели также имеют размер 32 бита (4 байта). Указатели определяются путем добавления звездочки ( * ) к имени переменной. Вместо определения переменной этого типа указатель определяется как нечто, указывающее на данные этого типа. Указатель.программа c является примером указателя, используемого с типом данных char, который только 1 байт в размере".

#include <stdio.h>
#include <string.h>

int main() 
{
    char str_a[20]; // A 20-element character array
    char *pointer; // A pointer, meant for a character array
    char *pointer2; // And yet another one
    strcpy(str_a, "Hello, world!\n");
    pointer = str_a; // Set the first pointer to the start of the array.
    printf(pointer);
    pointer2 = pointer + 2; // Set the second one 2 bytes further in.
    printf(pointer2); // Print it.
    strcpy(pointer2, "y you guys!\n"); // Copy into that spot.
    printf(pointer); // Print again.
}

"как показывают комментарии в коде, первый указатель устанавливается в начале массива символов. Когда на массив символов ссылаются подобным образом, это фактически сам указатель. Вот как этот буфер был передан в качестве указателя на функции printf() и strcpy() ранее. Второй указатель устанавливается на адрес первого указателя плюс два, а затем некоторые вещи печатаются (показано в выходных данных ниже)".

reader@hacking:~/booksrc $ gcc -o pointer pointer.c
reader@hacking:~/booksrc $ ./pointer
Hello, world!
llo, world!
Hey you guys!
reader@hacking:~/booksrc $

"оператор address-of часто используется в сочетании с указателями, поскольку указатели содержат адреса памяти. С помощью addressof.программа c демонстрирует оператор address-of, используемый для размещения адреса целочисленной переменной в указатель. Эта строка выделена жирным шрифтом ниже".

#include <stdio.h>

int main() 
{
    int int_var = 5;
    int *int_ptr;
    int_ptr = &int_var; // put the address of int_var into int_ptr
}

"для использования с указателями существует дополнительный унарный оператор, называемый оператором разыменования. Этот оператор возвратит данные, найденные в адресе, на который указывает указатель, а не сам адрес. Он принимает форму звездочки перед именем переменной, аналогично объявлению указателя. Еще раз, оператор разыменования существует как в GDB, так и в C".

"несколько дополнений к адресу.C код (показан в addressof2.c) воля продемонстрируйте все эти концепции. Добавлены функции printf() функции использовать формат параметры, которые я объясню в следующем разделе. Пока, просто сосредоточьтесь на выводе программ".

#include <stdio.h>

int main() 
{
    int int_var = 5;
    int *int_ptr;
    int_ptr = &int_var; // Put the address of int_var into int_ptr.
    printf("int_ptr = 0x%08x\n", int_ptr);
    printf("&int_ptr = 0x%08x\n", &int_ptr);
    printf("*int_ptr = 0x%08x\n\n", *int_ptr);
    printf("int_var is located at 0x%08x and contains %d\n", &int_var, int_var);
    printf("int_ptr is located at 0x%08x, contains 0x%08x, and points to %d\n\n", &int_ptr, int_ptr, *int_ptr);
}

"когда унарные операторы используются с указателями, оператор адреса может рассматриваться как движущийся назад, в то время как оператор разыменования движется вперед в направлении, указанном указателем".

Узнайте больше о указателях и распределении памяти

профессор Дэн Хиршберг, факультет компьютерных наук, Калифорнийский университет по компьютерной памяти https://www.ics.uci.edu / ~dan/class/165/notes/memory.html

http://cslibrary.stanford.edu/106/

http://www.programiz.com/c-programming/c-dynamic-memory-allocation

массивы

Theres простой учебник по многомерным массивам парнем по имени Alex Allain доступен здесь http://www.cprogramming.com/tutorial/c/lesson8.html

Theres информация о массивах парнем по имени Тодд Гибсон доступна здесь http://www.augustcouncil.com / ~tgibson/tutorial/arr.html

итерация массива

#include <stdio.h>

int main() 
{

    int i;
    char char_array[5] = {'a', 'b', 'c', 'd', 'e'};
    int int_array[5] = {1, 2, 3, 4, 5};
    char *char_pointer;
    int *int_pointer;
    char_pointer = char_array;
    int_pointer = int_array;

    for(i=0; i < 5; i++) { // Iterate through the int array with the int_pointer.
        printf("[integer pointer] points to %p, which contains the integer %d\n", int_pointer, *int_pointer);
        int_pointer = int_pointer + 1;
    }

    for(i=0; i < 5; i++) { // Iterate through the char array with the char_pointer.
        printf("[char pointer] points to %p, which contains the char '%c'\n", char_pointer, *char_pointer);
        char_pointer = char_pointer + 1;
    }

}

связанные списки vs массивы

массивы-не единственный доступный вариант, информация о связанном списке.

http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_linklist.aspx

вывод

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