2013-04-09 2 views
-8

Если у меня есть указатель на указатель, как переменной «шс» в этом примере:Итерация через двойной указатель с помощью указателя нотацию в C

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

int main(int argc, char **argv) 
{ 
    char **bs = {"this", "is", "a", "test"}; 

    puts(bs); 

    return 0; 
} 

заявление путы показывает «это» на экране. Как я могу заставить его показать «есть» и т. Д. Я мог бы просто сделать что-то вроде:

puts(bs+1); 

Что действительно делает добавление числа в переменную-указатель? Указывает ли он на следующий адрес или учитывает тип указателя и использует размер переменной, на которую указывает, чтобы определить, где следующая переменная начинается в памяти?

+7

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

+0

это указывает на следующий адрес .... следующий адрес вычисляется как ... baseaddress + sizeofpointertype (1 для char, 2 для int и т. Д.) –

+0

OTOH, я бы проигнорировал все данные о расчете адреса и размере , C, имеет этот инвариант: 'x [y] == * (x + y)'. Следовательно, 'x + y == & * (x + y) == & x [y]'. –

ответ

3

инструкция puts показывает это «на экране».

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

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

$ clang badpoint.c 
badpoint.c:6:18: warning: incompatible pointer types initializing 'char **' with an 
     expression of type 'char [5]' [-Wincompatible-pointer-types] 
    char **bs = {"this", "is", "a", "test"}; 
       ^~~~~~ 
badpoint.c:6:26: warning: excess elements in scalar initializer       
    char **bs = {"this", "is", "a", "test"}; 
         ^~~~ 
badpoint.c:8:10: warning: incompatible pointer types passing 'char **' to parameter of 
     type 'const char *'dereference with * [-Wincompatible-pointer-types] 
    puts(bs); 
     ^~ 
     *                    
/usr/include/stdio.h:688:32: note: passing argument to parameter '__s' here    
extern int puts (__const char *__s); 
          ^
3 warnings generated. 

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

Так что же происходит?

Вы пытаетесь инициализировать скаляр (char**), а также обеспечить {"this", "is", "a", "test"}, скобу огороженного инициализатора-лист содержащих четыре инициализатор ‍ с. Per 6.7.9 (11)

Инициализатор для скаляра должен быть одним выражением, необязательно заключенным в фигурные скобки. Начальное значение объекта - это выражение (после преобразования); применяются те же ограничения типа и преобразования, что и для простого присваивания, при этом тип скаляра является неквалифицированной версией его объявленного типа.

, который нарушает требование «требуется» и, следовательно, вызывает неопределенное поведение.

Если компилятор не завершает перевод там, вероятно, просто игнорирует лишние инициализаторами - лязг и GCC как сделать - и обрабатывает декларацию, как это было

char **bs = "this"; 

который вызывает предупреждение об инициализации от несовместимый тип, поскольку он пытается инициализировать char** с помощью char[5], но в соответствии с 6.5.16.1 тип инициализатора должен быть совместим с char**.

Тем не менее, если компилятор успешно переводит программу, это может привести к bs, содержащий адрес первого char в строковый литерал (массив "this" преобразуется в указатель на его первый элемент на правой стороне оператора присваивания).

Тогда вызов

puts(bs) 

передает указатель неправильного типа (char**) к puts, что является нарушением ограничения и требует диагностического сообщения от компилятора (и делает программу недействительной).

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

Как бы я идти о получении его, чтобы показать «есть» и так далее

Исправляя программу. Как есть, строки "is", "a" и "test" даже не отображаются в объектном файле, создаваемом gcc или clang.

Один из способов исправить это было бы изменением декларации

char *bs[] = {"this", "is", "a", "test"}; 

так что bs является массивом из четырех char*, указывая на (соответствующие первые элементы) четыре строк.

Затем вы должны настроить вызов puts, разыменования или bs индексации,

puts(bs[0]); 

для печати "this";

for(int i = 0; i < 4; ++i) { 
    puts(bs[i]); 
} 

для печати всех четырех строк на отдельных строках.

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