2014-10-09 2 views
4

Я просто интересно, если это выражение является безопасным:printf: Это безопасно?

int main (void) 
{ 
    char my_tab[256]; 

    memset(my_tab,0x61,sizeof(my_tab)); 

    printf("Is it safe ? : %.256s",my_tab); /* is it safe ? */ 
} 
+1

Если '0x61' соответствует«безопасной»характер (я предлагаю использовать' 'a'' вместо этого), это безопасно - не каждый компьютер там использует ASCII :) – pmg

+0

Вы правы, я использовал 0x61 только для того, чтобы получить что-то отличное от «\ 0» – Joze

+2

Чтобы быть в безопасности, вам нужно «#include » и '#include ' – pmg

ответ

6

Да, вы не напечатаете 256 символов, и больше ничего.

От C11-Standard 7.21.6. р4:

Необязательное точность, что дает минимальное количество цифр появляются на D, I, о, и, преобразование х, и X, количество цифр появляться после десятичной точки для конверсий a, A, e, E, f и F , максимальное количество значащих цифр для конверсий g и G , или максимальное количество байтов, которое должно быть записано для преобразований s . Точность принимает вид периода (.), Который следует за либо звездочкой * (описанной ниже), либо необязательным десятичным числом ; если указан только период, точность принимается как ноль. Если точность отображается с любым другим спецификатором преобразования, поведение не определено.

7.21.6.1. P8:

s: Если ни один модификатор л Длина не присутствует, аргумент должен быть указателем на начальный элемент массива символьного типа. Символами из массива являются , записанные до (но не включая) завершающий нулевой символ. Если указана точность , пишется не более чем много байтов. Если точность не указана или больше размера массива, массив должен содержать нулевой символ.

+0

Я не совсем уверен. Предположим, что реализация выполняет 'strlen()', чтобы узнать, как долго строка? Так что это должно быть безопасно, но может и не быть. – JeremyP

+4

@JeremyP Если моя реализация делает это, тогда я хочу вернуть свои деньги! – ams

+0

@JeremyP: Вопрос о 'printf()' ing not 'strlen()' ing, не так ли? – alk

1

Да, это должно быть безопасным. Реализации, которые пытаются получить доступ к символу за концом буфера, считаются недействительными.

псевдокод для обработки %.Ns, где N представляет собой число, должно выглядеть следующим образом:

size_t count = 0; 
size_t N = ...; 
char *ptr = <next-arg>; 
while (count < N && *ptr != '\0') { 
    putchar(*ptr++); 
    count++; 
} 

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

Можно представить реализацию, которая инвертирует условие цикла while, которое будет обращаться к байту за концом буфера. Однако такая реализация будет недействительной, поскольку в соответствии со стандартом для реализации требуется закончить нулевой ограничитель, только если размер не указан или он больше числа пройденных символов:

[7.19.6.1.8] Если точность не указана или больше размера массива, массив должен содержать нулевой символ.

+0

Существует хорошее стандартное имя для «' output_to_buffer': 'putchar'. :-) –

+0

@R .. Спасибо! Думаю, я тоже должен заменить свой '''. Это больше не будет псевдокодом, хотя :-) – dasblinkenlight

2

Это безопасно.

С Е (3) - страница Linux руководство http://man7.org/linux/man-pages/man3/printf.3.html:

s  If no l modifier is present: The const char * argument is 
      expected to be a pointer to an array of character type 
      (pointer to a string). Characters from the array are written 
      up to (but not including) a terminating null byte ('\0'); if a 
      precision is specified, no more than the number specified are 
      written. If a precision is given, no null byte need be 
      present; if the precision is not specified, or is greater than 
      the size of the array, the array must contain a terminating 
      null byte. 

Функция vsnprintf в /lib/vsprintf.c вызова strnlen (s, spec.precision) для получения отформатированной буквы:

/** 
* strnlen - Find the length of a length-limited string 
* @s: The string to be sized 
* @count: The maximum number of bytes to search 
*/ 
size_t strnlen(const char *s, size_t count) 
{ 
    const char *sc; 

    for (sc = s; count-- && *sc != '\0'; ++sc) 
     /* nothing */; 
    return sc - s; 
} 

Доступны только действительные байты символов.

static noinline_for_stack 
char *string(char *buf, char *end, const char *s, struct printf_spec spec) 
{ 
    int len, i; 

    if ((unsigned long)s < PAGE_SIZE) 
     s = "(null)"; 

    len = strnlen(s, spec.precision); 

    if (!(spec.flags & LEFT)) { 
     while (len < spec.field_width--) { 
      if (buf < end) 
       *buf = ' '; 
      ++buf; 
     } 
    } 
    for (i = 0; i < len; ++i) { 
     if (buf < end) 
      *buf = *s; 
     ++buf; ++s; 
    } 
    while (len < spec.field_width--) { 
     if (buf < end) 
      *buf = ' '; 
     ++buf; 
    } 

    return buf; 
} 
+0

Тот факт, что конкретная реализация 'printf',' 'vsnprintf' или' strnlen' не задыхается от аргумента, который не указывает на нуль -терминированная строка имеет значение только для этой конкретной реализации. Важным моментом является то, что стандарт C требует его безопасности. Особая реализация 'strlen' может быть хорошо выполнена с учетом аргумента нулевого указателя, но' strlen (NULL) 'все еще имеет неопределенное поведение. –

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