Разница между char * и char ** (в C) - PullRequest
6 голосов
/ 15 августа 2011

Я написал этот код, который прост

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

void printLastLetter(char **str)
{
    printf("%c\n",*(*str + strlen(*str) - 1));
    printf("%c\n",**(str + strlen(*str) - 1));
}

int main()
{
    char *str = "1234556";
    printLastLetter(&str);
    return 1;
}

Теперь, если я хочу напечатать последний символ в строке, я знаю, что первая строка printLastLetter - это правильная строка кода. Я не совсем понимаю, в чем разница между * str и ** str. Первый это массив символов, а второй ?? Кроме того, в чем разница в распределении памяти между char * str и str [10]? Thnks

Ответы [ 8 ]

18 голосов
/ 15 августа 2011

char* - указатель на символ, char ** - указатель на символ.

char *ptr; НЕ выделяет память для символов, он выделяет память для указателя на символ.

char arr[10]; выделяет 10 символов, а arr содержит адрес первого символа. (хотя arr НЕ является указателем (не char *), а имеет тип char[10])

Для демонстрации: char *str = "1234556"; это как:

char *str;         // allocate a space for char pointer on the stack
str = "1234556";   // assign the address of the string literal "1234556" to str

Как прокомментировал @ Oli Charlesworth , если вы используете указатель на постоянную строку, например, в приведенном выше примере, вы должны объявить указатель как const - const char *str = "1234556";, поэтому, если вы попытаетесь измените его, что недопустимо, вы получите ошибку времени компиляции, а не ошибку нарушения доступа во время выполнения, такую ​​как ошибка сегментации. Если вы не знакомы с этим, пожалуйста, посмотрите здесь .

Также см. объяснение в FAQ группы новостей comp.lang.c .

11 голосов
/ 15 августа 2011

char ** x - указатель на указатель, который полезен, когда вы хотите изменить существующий указатель вне его области действия (скажем, в вызове функции).

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

void modify(char **s)
{
  free(*s); // free the old array
  *s = malloc(10); // allocate a new array of 10 chars
}

int main()
{
  char *s = malloc(5); // s points to an array of 5 chars
  modify(&s); // s now points to a new array of 10 chars
  free(s);
}

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

Что касается вашего последнего вопроса, char * str; просто объявляет указатель без выделенной ему памяти, тогда как char str [10]; выделяет массив из 10 символов в локальном стеке. Локальный массив исчезнет, ​​как только он выйдет из области видимости, поэтому, если вы хотите вернуть строку из функции, вы хотите использовать указатель с динамически выделенной (malloc'd) памятью.

Кроме того, char * str = "Некоторая строковая константа"; также указатель на строковую константу. Строковые константы хранятся в разделе глобальных данных вашей скомпилированной программы и не могут быть изменены. Вам не нужно выделять память для них, потому что они скомпилированы / жестко запрограммированы в вашей программе, поэтому они уже занимают память.

2 голосов
/ 15 августа 2011

Возможно, вы захотите изучить этот незначительный вариант вашей программы (функция printLastLetter() неизменна, за исключением того, что она сделана статической) и выяснить, почему вывод:

3
X

Выходполностью детерминированный - но только потому, что я тщательно настроил переменную list, чтобы она была детерминированной.

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

static void printLastLetter(char **str)
{
    printf("%c\n", *(*str + strlen(*str) - 1));
    printf("%c\n", **(str + strlen(*str) - 1));
}

int main(void)
{
    char *list[] = { "123", "abc", "XYZ" };
    printLastLetter(list);
    return 0;
}
2 голосов
/ 15 августа 2011

Первый - это массив символов, а второй ??

Второй - это указатель на ваш массив.Поскольку вы передаете адрес str, а не сам указатель (str), это необходимо для разыменования.

printLastLetter( str ); 

и

printf("%c\n",*(str + strlen(str) - 1)); 

имеет больше смысла, если вам не нужно менять значениеул.

0 голосов
/ 15 августа 2011

char * - указатель на область памяти. для символа * str = "123456"; это первый символ строки. "" - это просто удобный способ ввода массива символьных значений. str [10] - это способ зарезервировать 10 символов в памяти, не говоря о том, что они из себя представляют. (nb Поскольку последний символ равен NULL, он может содержать только 9 букв. Когда функция принимает параметр *, вы можете использовать параметр [] но не наоборот.

Вы делаете это излишне сложным, беря адрес str, прежде чем использовать его в качестве параметра. В C вы часто передаете адрес объекта в функцию, потому что это намного быстрее, чем передача всего объекта. Но так как это уже указатель, вы не улучшаете функцию, передавая указатель на указатель. Предполагая, что вы не хотите изменять указатель, чтобы он указывал на другую строку.

0 голосов
/ 15 августа 2011

**str - это не что иное, как (*str)[0], и разница между *str и str[10] (в объявлении, я предполагаю), я думаю, заключается в том, что первый - это просто указатель, указывающий на константный строковый литерал, который может храниться где-то в глобальной статической памяти, тогда как последний выделяет 10 байт памяти в стеке, в котором хранится литерал.

0 голосов
/ 15 августа 2011

для вашего фрагмента кода, *str содержит адрес для символа и **str содержит адрес для переменной, содержащей адрес символа. Другими словами, указатель на указатель.

Всякий раз, когда у вас есть * str, выделяется только достаточно памяти для хранения переменной типа указателя (4 байта на 32-битной машине). При str[10] память уже выделена для 10 char.

0 голосов
/ 15 августа 2011

char ** для строки строк в основном - массив символьных массивов.Если вы хотите передать несколько аргументов массива символов, вы можете использовать это, предполагая, что они расположены правильно.

char ** x;* x разыменовывается и дает вам первый массив символов, выделенный в x.** x разыменовывает этот массив символов, давая вам первый символ в массиве.

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