将fscanf / sscanf字符串传递给结构字段char [4]

时间:2017-01-20 05:26:41

标签: c struct scanf

下面的两个结构字段定义如何相互区分。

//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
...

2 个答案:

答案 0 :(得分: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()继续在T之后写出字符。

要解决此问题,请调整名称组件的大小。

BTW:从文件中无限制地读取可以在缓冲区溢出的情况下攻击您的软件。

答案 1 :(得分:1)

2个struct定义之间的区别在于,您在一个struct中预先分配存储空间,并在另一个struct中声明指针。

在您的第一个struct中,您有char *char *是一个指针。它没有指向任何东西。您需要动态分配一些内存,然后将char *指针指向该分配的内存。

在您的第二个struct中,您有char name[4]。这是一个数组,您将为此数组分配4个字节。这已分配并可以使用。

如果您事先不知道缓冲区的大小,请使用第一个struct。使用malloc分配任意数量的内存,例如1024字节。然后一次读入1024字节的数据。继续这样做,直到你可以计算出数据总量有多大,然后用malloc分配那么多的内存,然后读入你的数据。

如果你知道你的数据总是4个字节长,并且它永远不会大于或小于那个数据,那么使用第二个struct。如果需要,可以这样声明: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