2017-02-06 6 views
1

Я тестировал использование sizeof() для одного и того же содержимого строки «abc». моя функция выглядит так:C: другое определение строки, я получаю разный размер, используя sizeof()

int main(void){ 
    char* pass1 = "abc"; 
    char pass2[] = "abc"; 
    char pass3[4] = "abc"; 
    char pass4[] = ""; 

    scanf("%s", pass4); 

    printf("sizeof(pass1) is: %lu\n", sizeof(pass1)); 
    printf("sizeof(pass2) is: %lu\n", sizeof(pass2)); 
    printf("sizeof(pass3) is: %lu\n", sizeof(pass3)); 
    printf("sizeof(pass4) is: %lu\n", sizeof(pass4)); 

    return 0; 
} 

I вход "а" для pass4, выход, как это:

sizeof(pass1) is: 8 
sizeof(pass2) is: 4 
sizeof(pass3) is: 4 
sizeof(pass4) is: 1 

Я ожидал, что все 4са. Я думал, что 4 строковые определения совпадают.

Почему sizeof (pass1) возвращает 8? Почему sizeof (pass4) равен 1?

+3

'sizeof' дает результат типа' size_t'. Правильный формат 'size_t' -' '% zu" '. –

+0

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

+2

[для печати 'size_t' use% zu] (http://stackoverflow.com/q/940087/995714). Использование неверного спецификатора формата вызывает неопределенное поведение. –

ответ

3

Когда вы берете sizeof по типу указателя, вы получите размер в байтах адреса памяти. В этом случае 8 - размер адреса (в байтах). sizeof на статически выделенные строки только для чтения в C вернет фактический размер в байтах строки, включая нулевой байт.

+0

Я думал, что указатель pass1 указывает на 8-битный адрес памяти типа символа. Я никогда не определял 8-битный адрес памяти для этой строки. –

+0

@DiWang Это зависит от компилятора и платформы, возможно, вы используете 64-битный компилятор на платформе x64. Вы можете проверить sizeof (void *), sizeof (char *), sizeof (double *) или любой тип указателя, они будут одинаковыми. –

+0

Спасибо, sizeof (char *) протестирован на моем компьютере. –

0

В этой декларации

char* pass1 = "abc"; 

в правой части, используемой в качестве инициализатора есть массив символов, который имеет размер, равный 4. Строковые литералы имеют тип символьных массивов. Учтите, что строковые литералы включают завершающий нуль. Вы можете проверить это следующим образом:

printf("sizeof(\"abc\") is: %zu\n", sizeof("abc")); 

В объявлении массив используется для инициализации указателя. Используемый в качестве инициализатора указателя массив неявно преобразуется в указатель на его первый элемент.

Таким образом, указатель pass1 указывает на первый элемент строкового литерала "abc". Размер самого указателя в вашей системе равен 8 байтам.

В этих declerations

char pass2[] = "abc"; 
char pass3[4] = "abc"; 

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

Так что в этом объявлении

char pass2[] = "abc"; 

массив pass2 будет иметь 4 элемента, поскольку sgtring буквальным обеспечивает четыре инициализаторами.

В этой декларации

char pass3[4] = "abc"; 

там явно указано, что массив имеет 4 элемента.

Таким образом, оба массива имеют размер, равный 4, а указатель, объявленный первым, имеет размер 8 байт.

2

sizeof дает размер своего операнда. Чтобы понять результаты, которые вы видите, вам нужно понять, что на самом деле есть то, что pass1, pass2, pass3 и pass4.

pass1 представляет собой указатель на символ (т.е. char *), таким образом sizeof pass1 дает размер указателя (переменную, которая содержит адрес памяти, а не массив). То есть 8 с вашим компилятором. Размер указателя определяется реализацией, поэтому это может дать разные результаты с разными компиляторами. Тот факт, что вы инициализировали pass1, поэтому он указывает на первый символ строкового литерала "abc", не меняет того факта, что pass1 объявлен как указатель, а не массив.

pass2 представляет собой массив инициализирован с помощью буквального "abc", который - по соглашению -. Представлено в С использованием массива из четырех символов (три буквы 'a' до 'c', плюс дополнительный символ с нулевым значением ('\0')

pass3 также является массив из четырех char, так как он заявил, что путь char pass3[4] = <etc>. Если вы сделали char pass3[4] = "abcdef", вы по-прежнему считаете, что sizeof pass3 является 4 (и 4 элемента pass3 будет 'a' к 'd' (с другой чарой cter 'e', 'f' и '\0' в строке литерал "abcdef" не используется для инициализации pass3).

Поскольку и pass2, и pass3 являются массивами четырех символов, их размер равен 4 (в общем случае размер массива - это размер элемента массива, умноженный на количество элементов). Стандарт определяет sizeof char как 1, а 1*4 имеет значение 4.

pass4 Инициализировано, используя литературу "". Этот строковый литерал представлен с использованием единственного символа со значением '\0' (и перед ним нет символов, поскольку между двойными кавычками нет). Таким образом, pass4 имеет размер 1 по той же причине, что pass2 имеет размер 4.

+0

Но pass4 был заменен на «abc», \ 0 переписан. как размер может быть 1 снова? Я тестировал strlen (pass4), он возвращает 3, но sizeof (pass4) возвращает 1. –

+0

«scanf («% s », pass4)» (который я укажу, что вы вставили ПОСЛЕ того, как я дал этот ответ) дает неопределенный если пользователь не вводит символы перед новой строкой.Это означает, что все разрешено. На практике, если вы введете 'abc', он перезапишет любую память сразу после' pass4'. Это не меняет размер 'pass4' - это просто означает, что ваш код перезаписывает память в конце' pass4'. Поскольку 'strlen()' продолжает перемещаться по памяти, пока не найдет '' \ 0'', он будет сообщать '4', но это случайность. – Peter