почему printf работает с не завершенной строкой - PullRequest
6 голосов
/ 15 февраля 2011

Мне интересно, как printf () определяет, когда прекратить печатать строку, даже если я не поставил символ завершения в конце строки? Я провел эксперимент с malloc 10-байтовой памятью и поместил в нее ровно 10 символов, так или иначе, printf все еще мог печатать эти символы без ограничения, почему?

Ответы [ 6 ]

6 голосов
/ 15 февраля 2011

Существует высокая вероятность того, что один из символов после строки имеет значение NULL, поэтому printf останавливается на этом, кроме того, символы, которые не равны NULL после памяти, которую вы используете, могут не быть печатными символами, поэтому вы не заметите ихтерминал.

5 голосов
/ 15 февраля 2011

Это потому, что вам повезло, и следующий байт вашей malloc 'строки был 0 байт.

Это можно подтвердить, выполнив:

const char* digits = "0123456789";
char* buff = (char*)malloc(10);
memcpy(buff, digits, 10);

printf("%s, %d\n", buff, (int)*(buff + 10));

Ваша программа должна напечатать:

0123456789 0

И это 0 - это NULL, который вы не сделали malloc'ом, но он был там.Обратите внимание, что это поведение не определено, поэтому вы не можете доверять этим вещам.Как я уже говорил, это случилось потому, что вам не повезло!Хорошая вещь, которая может случиться в этой ситуации - это SIGSEGV.

3 голосов
/ 15 февраля 2011

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

В большинстве ОС / процессоров malloc округляет выделения до кратных 4 или 8 байт (в зависимости от требований выравнивания памяти)процессора), поэтому часто (но не всегда) несколько свободных байтов находятся в конце строки.

Обычно, когда malloc требует больше памяти, она выделяется одной или нескольким виртуальным страницам (обычно 4 КБ)ОПЕРАЦИОННЫЕ СИСТЕМЫ.По соображениям безопасности страницы должны быть стерты, если они в последний раз использовались другим процессом (или не использовались после «горячего» сброса?).

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

Для защиты от этого класса проблем при разработке и тестировании необходимо использовать что-то вроде efence с опцией EF_FILL, чтобы установить для памяти malloc'd ненулевое значение.

Точно так же этоПолезная идея инициализировать стек ненулевыми значениями, так как - на большинстве машин с ВМ - стек создается из 4 тыс. страниц, которые стираются перед выделением процессу.

Обратите внимание, что даже при использовании таких вещей, как efence, существуетпо-прежнему проблема со статическими переменными - вся область стирается до нуляПрограмма загружена (и снова данные выровнены), поэтому неопределенная строка, вероятно, останется незамеченной, если статическая строковая переменная будет записана только один раз - проблема будет замечена, только если строковая переменная используется повторно для хранения более короткой неопределенной строки.

Что касается смежной проблемы, то выравнивание переменных объясняет, почему не выделяется достаточно места для завершающего NUL строки часто остается незамеченным.

3 голосов
/ 15 февраля 2011

Если вы действительно сделали malloc 10 символов, и вы действительно установили для каждого символа значение, отличное от null ('\ 0'), то где гарантия того, что символ, который вы не распределили, сразу же последовал вПамять не была нулевой случайно?

Возможно, вы использовали один из нескольких вызовов функции, которые достаточно умны, чтобы установить последний символ как нулевой, даже если вы передали ему достаточно информации, чтобы, возможно, «установить» этобыть ненулевым, но с таким небольшим количеством деталей, мы никогда не узнаем.

2 голосов
/ 15 февраля 2011

Случайный мусор, который находится после последнего байта в строке, был нулевым. Это была удача Может произойти сбой при следующем запуске программы или работе 100 раз подряд. Добро пожаловать в ошибки указателя (их также сложно отладить).

0 голосов
/ 15 февраля 2011

Ну, хорошо, хорошо, оставьте в стороне всю причину MALLOC, потому что для PRINTF все это просто строка правильно, я знаю% d,% x,% s и все, что мы используем в качестве спецификаторов формата, но дело в printf, если просто Функция "C", которая может принимать переменное число аргументов.

Проще говоря, printf - это специальная функция, которая обрабатывает строку как переменное число передаваемых ей аргументов типа CHAR.

Любой аргумент \ n, \ t и т. Д. Или% c,% f и т. Д. Для него является одним символом и рассматривается как особый случай.

void myprintf(char * frmt,...)
{

char *p;
int i;
unsigned u;
char *s;
va_list argp;


va_start(argp, fmt);

p=fmt;
for(p=fmt; *p!='\0';p++)
{
if(*p=='%')
{
putchar(*p);continue;
}

p++;

switch(*p)
{
case 'c' : i=va_arg(argp,int);putchar(i);break;
case 'd' : i=va_arg(argp,int);
if(i<0){i=-i;putchar('-');}puts(convert(i,10));break;
case 'o': i=va_arg(argp,unsigned int); puts(convert(i,8));break;
case 's': s=va_arg(argp,char *); puts(s); break;
case 'u': u=va_arg(argp,argp, unsigned int); puts(convert(u,10));break;
case 'x': u=va_arg(argp,argp, unsigned int); puts(convert(u,16));break;
case '%': putchar('%');break;
}
}

va_end(argp);
}

char *convert(unsigned int, int)
{
static char buf[33];
char *ptr;

ptr=&buf[sizeof(buff)-1];
*ptr='\0';
do
{
*--ptr="0123456789abcdef"[num%base];
num/=base;
}while(num!=0);
return(ptr);
} 

Надеюсь, это поможет, если это не просто дать мне знать, я был бы рад вам помочь:)

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