2010-05-14 3 views
6

Узнав, что и strncmp не то, что кажется, и strlcpy не доступно в моей операционной системе (Linux), я подумал, что могу попытаться написать его сам.C странное поведение массива

Я нашел цитату из Ульриха Дреппера, поддерживающего libc, который разместил альтернативу strlcpy, используя mempcpy. У меня тоже нет mempcpy, но это поведение было легко реплицироваться. Во-первых, это TestCase меня

#include <stdio.h> 
#include <string.h> 

#define BSIZE 10 

void insp(const char* s, int n) 
{ 
    int i; 

    for (i = 0; i < n; i++) 
     printf("%c ", s[i]); 

    printf("\n"); 

    for (i = 0; i < n; i++) 
     printf("%02X ", s[i]); 

    printf("\n"); 

    return; 
} 

int copy_string(char *dest, const char *src, int n) 
{ 
    int r = strlen(memcpy(dest, src, n-1)); 
    dest[r] = 0; 

    return r; 
} 

int main() 
{ 
    char b[BSIZE]; 
    memset(b, 0, BSIZE); 

    printf("Buffer size is %d", BSIZE); 

    insp(b, BSIZE); 

    printf("\nFirst copy:\n"); 
    copy_string(b, "First", BSIZE); 
    insp(b, BSIZE); 
    printf("b = '%s'\n", b); 

    printf("\nSecond copy:\n"); 
    copy_string(b, "Second", BSIZE); 
    insp(b, BSIZE); 

    printf("b = '%s'\n", b); 

    return 0; 
} 

И это его результат:

Buffer size is 10      
00 00 00 00 00 00 00 00 00 00 

First copy: 
F i r s t  b  =  
46 69 72 73 74 00 62 20 3D 00 
b = 'First' 

Second copy: 
S e c o n d   
53 65 63 6F 6E 64 00 00 01 00 
b = 'Second' 

Вы можете увидеть во внутреннем представлении (линии insp() создан), что есть некоторый шум смешан в, как строка формата printf() при проверке после первой копии и внешняя 0x01 во второй копии.

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

Но почему здесь находится содержимое внешнего массива (из строки формата) внутри моего адресата? Это как если бы пункт назначения был НАЗНАЧЕН, чтобы соответствовать новой длине.

+1

Что такое внешний массив? – WhirlWind

+0

В этом случае я имел в виду строковый литерал из printf, а именно «b = '% s», который получил «смешанный» с моим массивом «b», пункт назначения – LukeN

+0

Обратите внимание, что источник для 'strlcpy()' и 'strlcat () 'довольно легко доступны по либеральной лицензии: http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/lib/libc/string/strlcpy.c?content-type=text%2Fplain –

ответ

4

Конец строки отмечен символом \ 0, после чего может быть что угодно, если только ваша ОС не намеренно затушевывает его, тогда это просто какой-то случайный хлам.

Обратите внимание, что в этом случае «проблема» отсутствует в copy_string, вы точно копируете 10chars, но память «first» в вашем основном коде просто случайна.

+0

О боже, я не считал, что memcpy() не останавливается на '\ 0', глупо, глупо мне. – LukeN

2

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

Вы можете легко видеть, что вы копируете свою исходную строку с ее нулевым терминатором. Но так как вы memcopying 10 байт, и обе строки «Первый» и «Второй» короче 10 байт, вы также копируете лишние байты мимо них.

1

Применение memcpy(dest, src, n-1) вызывает неопределенное поведение, если dest и src не в длину по меньшей мере, как n-1.

Например, First\0 имеет длину шесть символов, но вы читаете n-1 (9) символов; содержимое памяти за конец строкового литерала не определено, как и поведение вашей программы при чтении этой памяти.

0

Дополнительный «материал» есть, потому что вы прошли размер буфера до memcpy. Он собирается копировать много символов, даже если источник короче.

Я хотел бы сделать вещи немного по-другому:

void copy_string(char *dest, char const *src, size_t n) { 
    *dest = '\0'; 
    strncat(dest, src, n); 
} 

В отличие от strncpy, strncat определяется работать как большинство людей разумно ожидать.

+0

Люди часто ожидают, что 'strncat' будет работать как' strlcat', т. Е. Ожидает, что он примет * полную * длину целевого буфера, в то время как на самом деле он берет * остаток * длины, доступной для конкатенации. – AnT

+0

Вы действительно хотите 'if (n> 0) strncat (dest, src, n - 1)' (предполагая, что 'n' - размер целевого буфера). – caf

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