2017-02-08 4 views
2

Я программист среднего уровня, изучающий стандарт C. В настоящее время я занимаюсь упражнением класса, который включает в себя использование указателей для хранения различных типов данных в массив типа char.Использование массива символов для хранения нескольких типов данных в стандарте C

Предположим, что у меня есть большой массив символов:

static char arr[1000]; 

Как мой профессор объяснил, что я могу рассматривать этот кусок локальной памяти, где каждый элемент массива имеет детализацию одного байта. Это кажется полезным. Теперь предположим, что я хочу взять первые четыре байта/элементов и хранить Int:

int a = 100; 
int* ptr = (int*)arr; 
*ptr = a; 

Как я понимаю, вторая строка создает Int * указатель, а затем направляет его в начале массива обр. Третья строка записывает значение a в это местоположение. Поскольку ptr является указателем типа int и потому, что у arr достаточно места, это записывает данные с четырьмя байтами/четырьмя элементами, потому что sizeof (int) == 4. Наблюдая это внимательно через мой отладчик, похоже, это подтверждает.

До сих пор так хорошо. Теперь предположим, что я хотел расширить эту концепцию. Скажем, я хотел, чтобы сохранить следующее в моем массиве, в следующем порядке:

int a = 100; 
int b = 200; 
char* str = “My dog has fleas” 
int c = 300; 

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

00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 
-------------------------------------------------------------------------------------- 
[ 100 ] [ 200 ] M y  d o g  h a s  f l e a s \0 [ 300 ] 

Мне нужно, чтобы иметь возможность хранить данные в массив в этом а затем, зная структуру массива заранее, сможете прочитать массив. Ниже мой код & выход, извините заранее за длинную длину. Он компилируется, но не работает. Рассмотрение его с помощью моего отладчика было очень запутанным; Я не могу сказать, где (и как часто) я ухожу. Если у кого-нибудь есть понимание или совет, я буду очень благодарен.

int main(){ 

    static char arr[1000]; 

    int a = 100; 
    int b = 200; 
    char* str = "My dog has fleas"; 
    int c = 300; 

    // Create pointers to load data: 
    int* ptrA = arr;      // points to start of array 
    int* ptrB = ptrA + sizeof(int);  // points 4 bytes into array 
    char* ptrStr = ptrB + sizeof(int); // points 8 bytes into array 
    int* ptrC = ptrStr + sizeof("My dog has fleas"); // points to after the string 
             // (I don't know how to use sizeof() to measure the actual length of the string 

    // Load data into my array 
    *ptrA = a;  // Assign int 100 into the array? 
    *ptrB = b;  // Assign int 200 into the array? 
    *ptrStr = memcpy(ptrStr, str, sizeof("My dog has fleas"));  // Write "My dog has fleas" into the array? 
    *ptrC = c;  // Assign int 300 into the array? 

    // Knowing the array's structure, walk it and print results: 
    char* walkIt = arr; 
    int counter = 0; 
    while (counter < 30) { 
     if (counter == 0) { 
      // we are pointing at what should be the first int 
      int* tmpPtr1 = (int*)walkIt; 
      printf("%d ", *tmpPtr1); 
     } 
     else if (counter == 4) { 
      // we are pointing at what should be the second int 
      int* tmpPtr2 = (int*)walkIt; 
      printf("%d ", *tmpPtr2); 
     } 
     else if (counter == 8) { 
      // we are pointing at what should be the string 
      printf("%s ", walkIt); 
     } 
     else if (counter == 25) { 
      // we are pointing at what should be the third int 
      int* tmpPtr3 = (int*)walkIt; 
      printf("%d ", *tmpPtr3); 
     } 
     walkIt++;  // Continue walking the array 
     counter++;  // Don't walk too far 
    } 
    return 0; 
} 

Выход заключается в следующем:

100 0 0  
+0

'memcpy (ptrStr, str, sizeof (« У моей собаки есть блохи »));'. 'sizeof' в строке не дает вам нужного результата. Это просто вернет вам размер указателя. Вместо этого используйте 'strlen'. Или используйте 'strcpy' для копирования. – kaylum

+1

Я не совсем уверен, в чем смысл этого упражнения, но ваш профессор, кажется, делает вам плохую услугу. Недействительно в C для доступа ко всем или части массива 'char', как если бы это был' int' или любой другой тип nonchchar, за исключением особых обстоятельств, которые здесь не применяются. –

+1

Ваш профессор ошибается, так как выполнение того, что вы делаете, нарушает [правило строгого сглаживания] (http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule), и оно может оптимизаций. Для этого лучше использовать «союз». – Jack

ответ

2

Прежде всего Вашего профессора не так, хотя это правда, что под капотом вещи, возможно, будет таким образом, разыменования указателей, полученных литьем указатель на другой type нарушает правило strict aliasing, что является предположением, сделанным компилятором, что два указателя разных типов не могут ссылаться на одну и ту же память, что позволяет оптимизировать такие указатели.

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

int* ptrB = ptrA + sizeof(int); 

Теперь ptrA имеет int* и добавление целого смещения указателя неявно умножает смещение по размеру указанного элемента. Это означает, что вы не добавляете sizeof(int) байт, но sizeof(int)*sizeof(int) байтов.

Чтобы принудительно добавить определенное количество байтов, вы должны наложить указатель на char*, чтобы добавить sizeof(int) байт просто добавляет sizeof(int)*sizeof(char) == sizeof(int)*1 байт.

int* ptrB = (char*)ptrA + sizeof(int);  // points 4 bytes into array 

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

+2

Кастинг не нарушает правила строгого сглаживания. Использование полученного указателя для доступа к пространству делает. –

+0

@ M.M: Спасибо, я разъяснил это. – Jack

+0

@Jack - Ohhhhhhhhhhhhh, это делает мир смысла. Спасибо, я буду реализовывать. Я подозреваю, что это то, что мой профессор хотел, чтобы я учился все это время. – Pete

Смежные вопросы