2017-01-20 7 views
3

Как два определения полей структуры ниже отличаются друг от друга.Передача строки fscanf/sscanf в поле структуры char [4]

//first struct  
typedef struct{ 
    char *name; //here is the difference 
    int shares; 
} STOCK1; 


//second struct 
typedef struct{ 
    char name[4]; //here is the difference 
    int shares; 
} STOCK2; 


//here inside main() 
FILE *fpRead = openFile(input_filename, "r"); 

STOCK1 s1; 
fscanf(fpRead, "%s %d", s1.name, &s1.shares); 
printf("%s %d ", s1.name, s1.shares); 

STOCK2 s2; 
fscanf(fpRead, "%s %d", s2.name, &s2.shares); 
printf("%s %d ", s2.name, s2.shares); 

код выведет:

MSFT 400 
MSFT� 400 

Как вы можете видеть, используя вторую-структуру, он будет печатать некоторый мусор символ после строки. Почему это?

входная строка:

MSFT 400 
YHOO 100 
... 
+0

'char name [4];' -> имя символа [5]; 'или больше. Также перед использованием вам нужно выделить для 's1.name'. – BLUEPIXY

ответ

1

Разница между определениями 2 struct заключается в том, что вы предварительно распределяете хранилище в одном struct и объявляете указатель в другом struct.

В вашем первом struct у вас есть char *. char * - указатель. Это не указывает ни на что. Вам необходимо динамически выделить некоторую память, а затем указать указатель char * на выделенную память.

В вашем втором struct у вас есть char name[4]. Это массив, и вам выделяется 4 байта для этого массива. Это выделено и готово к использованию.

Используйте первый struct, если вы заранее не знаете размер буфера. Выделите произвольный объем памяти, например 1024 байта, используя malloc. Затем читайте за 1024 байта данных за раз. Продолжайте делать это, пока не сможете вычислить, насколько велики данные, а затем используйте malloc для выделения этого объема памяти, а затем прочитайте в своих данных.

Использовать второй struct, если вы знаете, что ваши данные всегда 4 байта, и он никогда не будет больше или меньше этого. Если вы хотите, вы можете объявить его следующим образом: char name[500]. Это предустановит 500 байтов для вас, и пока ваша строка не будет больше 499 символов, это будет работать. Тем не менее, вы можете тратить память (что в наши дни не так уж и много). Наиболее эффективным способом преодоления этой проблемы является динамическое распределение объема необходимой вам памяти. malloc

Последнее предупреждение .... Помните, что для строк в C требуется достаточно памяти для самой строки, плюс нулевой ограничитель.Например:

/* I am allocating 5 bytes to store my name. 
    My name is Alan, so I'm allocating 4 bytes 
    plus 1 additional byte for the null terminator 
*/ 

char myName[5]; 
myName[0] = 'A'; // A 
myName[1] = 'l'; // l 
myName[2] = 'a'; // a 
myName[3] = 'n'; // n 
myName[4] = '\0'; // Null Terminator 

printf("%s", myName); // Prints Alan 
1

Размер STOCK2.name 4 символов. Ваша строка имеет эти 4 символа + символ терминатора \0. Это 5 символов. Таким образом, терминатор находится за структурным компонентом, перекрывающим компонент shares. Если вы установили компонент shares, он перезаписывает ограничитель строк.

Приведем пример макета, чтобы проиллюстрировать это (32-битные/4-байтовые целые числа). После написания имени:

n+0 name[0] M 
n+1 name[1] S 
n+2 name[2] F 
n+3 name[3] T 
n+4 shares \0 <- terminator 
n+5 shares 
n+6 shares 
n+7 shares 

n+0 name[0] M 
n+1 name[1] S 
n+2 name[2] F 
n+3 name[3] T 
n+4 shares 144 | Example for 400 stored in a 32-bit int (144+1*256) 
n+5 shares 1 | 
n+6 shares 0 | 
n+7 shares 0 | 

Терминатор ушел и printf() продолжает выписывая символы после Т.

Чтобы решить эту проблему, измените размер компонента имя.

BTW: Наличие неограниченного количества чтения из файла позволяет атаковать ваше программное обеспечение с переполнением буфера.

+0

Иллюстрация очень ясная и полезная! Благодаря! – bing