Массив указателей на (с определенным массивом указателей) строк: Последовательности строк хранятся в памяти? - PullRequest
1 голос
/ 20 октября 2019

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

Например:

char *pa[] = { "Hello World!", "foo","bar","huhu","Let´s talk about that" };

Являются ли строки(или лучше: их символы) хранятся последовательно в памяти, один за другим?

Как, например, в этом случае:

Первый байт второй строки "foo", который является f сохраняется непосредственно внутри байта после символа \0 -Null первой строки "Hello World!".

ИЛИ

Разделяются ли строки в памяти? Например:

\0 - Нулевой символ первой строки "Hello World!" - последовательность байтов между- f символ второй строки "foo"?

ИЛИ

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

Может ли также произойти, что первый символ f второй строки "foo" сохраняется непосредственно после \0 -характера первой строки "Hello World!" означает, что они хранятся последовательно, и между \0 -характером второй строки "foo" и первым символом третьей строки "bar", то есть b, является пробел в группе строк, не являющейся- связанные байты, зависящие от компилятора, платформы и т. д.?

1039 * Вопрос для C и C ++, так как я работаю с обоими. Если ответы между этими двумя изменениями, пожалуйста, укажите, какой язык в фокусе.

Надеюсь, вы понимаете, что я имею в виду. Большое спасибо за любой ответ.

Ответы [ 4 ]

4 голосов
/ 20 октября 2019

вы ничего не можете предположить.

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

const char *base = "hello\0foo\0bar";
const char *hello = base;
const char *foo = base + 6; // hello + strlen(hello) + 1
const char *bar = base + 10; // foo + strlen(foo) + 1

или, как подсказывает @SteveSummit

const char *pa[] = { base, base + 6, base + 10 };
3 голосов
/ 20 октября 2019

Более того, если бы у вас было

char *pa[] = { "testing", "testing", "more testing" };

, компилятор мог бы хранить только одну копию строки "testing" и указывать на нее как из pa[0], так и pa[1]. (На самом деле, я только что попробовал это с двумя современными компиляторами, и оба они сделали именно это.)

Теоретически для действительно умного компилятора было бы возможно хранить только строку "more testing" и иметь pa[0] и pa[1] указывают на середину.

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

2 голосов
/ 20 октября 2019

Что ответил Стив Саммит, плюс: если хранится несколько строк, они могут располагаться в любом порядке или далеко друг от друга.

Кроме того, сравнивая указатели на эти строки, используя ">", "> = "и т. д. - это неопределенное поведение. Таким образом, вы можете проверить, например, p1 = "testing", p2 = "testing", p2 == p1 + 8 (что даст 0 или 1 без каких-либо гарантий), но не p2> =p1 + 8.

1 голос
/ 20 октября 2019

Как уже упоминалось, макет памяти определяется реализацией.

Расширяя подход pmg и делая C, вы можете сделать это следующим образом:

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

char ** create_pointer_array_pointing_to_sequential_data(char ** ppa)
{
  char ** result = NULL;

  if (NULL == ppa)
  {
    errno = EINVAL;
  }
  else
  {
    size_t s = 0;
    size_t l = 0;

    while (NULL != ppa[l])
    {
      s += strlen(ppa[l]);
      ++l;
    }

    result = malloc((l + 1) * sizeof *result);
    if (NULL != result)
    {
      result[0] = malloc(s + l + 1);
      if (NULL != result[0])
      {
        for (size_t i = 0; i < l; ++i)
        {
          strcpy(result[i], ppa[i]);
          result[i + 1] = result[i] + strlen(result[i]) + 1;
        }

        result[l] = NULL;
      }
      else
      {
        int errno_save = errno;
        free(result);
        errno = errno_save;
        result = NULL;
      }
    }
  }

  return result;
}

Используйте это как:

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

char ** create_pointer_array_pointing_to_sequential_data(char ** ppa);

int main(void)
{
  char ** pa = create_pointer_array_pointing_to_sequential_data(
    (char*[]){"Hello World!",
      "foo",
      "bar",
      "huhu",
      "Let's talk about that",
      NULL}
    );

   if (NULL == pa)
   {
     perror("create_pointer_array_pointing_to_sequential_data() failed");
     exit(EXIT_FAILURE);
   }

   for (size_t i = 0; NULL != pa[i]; ++i)
   {
     printf("pa[%zu] starts at %p and ends at %p: %s\n", 
       i, (void*) pa[i], (void*)(pa[i] + strlen(pa[i])), pa[i]);
   }
 }

И получите:

pa[0] starts at 0x6000003f0 and ends at 0x6000003fc: Hello World!
pa[1] starts at 0x6000003fd and ends at 0x600000400: foo
pa[2] starts at 0x600000401 and ends at 0x600000404: bar
pa[3] starts at 0x600000405 and ends at 0x600000409: huhu
pa[4] starts at 0x60000040a and ends at 0x600000420: Let's talk about that
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...