Как C получает правильное смещение массива для массива строк? - PullRequest
4 голосов
/ 05 ноября 2010

Я делаю что-то для класса, где я хочу использовать другую строку формата в зависимости от определенных условий.Я определил это так:

const char *fmts[] = {"this one is a little long", "this one is short"};

позже, я могу использовать

printf(fmts[0]);

или

printf(fmts[1]);

, и это работает.компилятор что-то делает для нас?Я предполагаю, что это займет самую длинную строку и сохранит их все выровненными.Но я хотел бы узнать от кого-то, кто знает.Спасибо

Ответы [ 6 ]

16 голосов
/ 05 ноября 2010

Делает это так же, как и для любого другого типа данных. Массив "строк" на самом деле является массивом символьных указателей, которые все имеют одинаковый размер. Таким образом, чтобы получить правильный адрес для указателя , он умножает индекс на размер отдельного элемента, а затем добавляет его к базовому адресу.

Ваш массив будет выглядеть так:

      <same-size>
      +---------+
fmts: | fmts[0] | ------+
      +---------+       |
      | fmts[1] | ------|--------------------------+
      +---------+       |                          |
                        V                          V
                        this one is a little long\0this one is short\0

Символы для самих строк не хранятся в массиве, они существуют в другом месте. Как у вас есть, они обычно хранятся в постоянном запоминающем устройстве, хотя вы также можете malloc их или даже определить как модифицируемый массив символов с чем-то вроде:

char f0[] = "you can modify me without invoking undefined behaviour";

Вы можете увидеть это в действии со следующим кодом:

#include<stdio.h>
const char *fmts[] = {
    "This one is a little long",
    "Shorter",
    "Urk!"
};
int main (void) {
    printf ("Address of fmts[0] is %p\n", (void*)(&(fmts[0])));
    printf ("Address of fmts[1] is %p\n", (void*)(&(fmts[1])));
    printf ("Address of fmts[2] is %p\n", (void*)(&(fmts[2])));

    printf ("\n");

    printf ("Content of fmts[0] (%p) is %c%c%c...\n",
        (void*)(fmts[0]), *(fmts[0]+0), *(fmts[0]+1), *(fmts[0]+2));
    printf ("Content of fmts[1] (%p) is %c%c%c...\n",
        (void*)(fmts[1]), *(fmts[1]+0), *(fmts[1]+1), *(fmts[1]+2));
    printf ("Content of fmts[2] (%p) is %c%c%c...\n",
        (void*)(fmts[2]), *(fmts[2]+0), *(fmts[2]+1), *(fmts[2]+2));

    return 0;
}

который выводит:

Address of fmts[0] is 0x40200c
Address of fmts[1] is 0x402010
Address of fmts[2] is 0x402014

Content of fmts[0] (0x4020a0) is Thi...
Content of fmts[1] (0x4020ba) is Sho...
Content of fmts[2] (0x4020c2) is Urk...

Здесь вы можете видеть, что фактические адреса элементов массива равноудалены - 0x40200c + 4 = 0x402010, 0x402010 + 4 = 0x402014.

Однако значения не являются, потому что они относятся к разным размерам строк. Строки находятся в одном блоке памяти (в данном случае - ни в коем случае не нужно), как показано ниже, с символами *, обозначающими начало и конец отдельных строк:

         |  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f +0123456789abcdef
---------+-------------------------------------------------------------------
0x04020a0| *54 68 69 73 20 6f 6e 65 20 69 73 20 61 20 6c 69  This one is a li
0x04020b0|  74 74 6c 65 20 6c 6f 6e 67 00*53 68 6f 72 74 65  ttle long.Shorte
0x04020c0|  72 00*55 72 6b 21 00*                            r.Urk!.
3 голосов
/ 05 ноября 2010

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

Если вы действительно хотите иметь массив строк, вы должны объявить нечто подобное

const char fmts[][64] = { "this one is a little long", "this one is short" };

т.е. вы должны будете объявить массив массивов.В этом случае ваша ответственность будет указывать достаточный фиксированный размер для ваших фактических строковых массивов (64 в моем примере), и это значение будет определять фиксированное смещение от одной строки к следующей в массиве.

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

3 голосов
/ 05 ноября 2010

fmts указывает на указатели на символ.Он не указывает на сами строки.

Другими словами: разница в адресах fmts[0] и fmts[1] является размером типа char *.

2 голосов
/ 05 ноября 2010

Вы не объявили массив строк.Вы объявили массив указателей на строки.Массив строк будет выглядеть следующим образом:

char fmts[][40] = {"this one is a little long", "this one is short"};

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

2 голосов
/ 05 ноября 2010

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

Поскольку это "массив указателей"на символ ", поэтому каждый указатель может указывать на любые места, не нужно быть равной длины или что-либо.

2 голосов
/ 05 ноября 2010

Ответ в том, что у вас нет массива строк как такового , у вас есть массив указателей на char с. Все указатели имеют одинаковый размер, printf() просто разыменовывает их.

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