我只是想知道为什么
students[x].firstName=(char*)malloc(sizeof(char*));
Run Code Online (Sandbox Code Playgroud)
这个不需要字符串的长度。
完整代码(来自互联网上的某个地方):
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
typedef struct
{
char* firstName;
char* lastName;
int rollNumber;
} STUDENT;
int numStudents=2;
int x;
STUDENT* students = (STUDENT *)malloc(numStudents * sizeof *students);
for (x = 0; x < numStudents; x++)
{
students[x].firstName=(char*)malloc(sizeof(char*));
printf("Enter first name :");
scanf("%s",students[x].firstName);
students[x].lastName=(char*)malloc(sizeof(char*));
printf("Enter last name :");
scanf("%s",students[x].lastName);
printf("Enter roll number :");
scanf("%d",&students[x].rollNumber);
}
for (x = 0; x < numStudents; x++)
printf("First Name: %s, Last Name: %s, Roll number: %d\n", students[x].firstName, students[x].lastName, students[x].rollNumber);
return (0);
}
Run Code Online (Sandbox Code Playgroud)
解释为:
students[x].firstName=(char*)malloc(sizeof(char*));
Run Code Online (Sandbox Code Playgroud)
根据您使用的平台,该行
students[x].firstName=(char*)malloc(sizeof(char*));
Run Code Online (Sandbox Code Playgroud)
最有可能相当于
students[x].firstName = malloc( 4 );
Run Code Online (Sandbox Code Playgroud)
或者
students[x].firstName = malloc( 8 );
Run Code Online (Sandbox Code Playgroud)
char *因为这是大多数平台上 a (指向 的指针)的大小char。
这意味着如果用户输入超过 3 或 7 个字符(4 或 8 个字符,包括终止空字符),则这些行
printf("Enter first name :");
scanf("%s",students[x].firstName);
Run Code Online (Sandbox Code Playgroud)
将导致缓冲区溢出,从而调用未定义的行为。这意味着您的程序可能会崩溃或以其他方式出现异常。
因此,您的担忧是有道理的。线路
students[x].firstName=(char*)malloc(sizeof(char*));
Run Code Online (Sandbox Code Playgroud)
应该指定一个足以存储用户输入的最大可能长度的大小,而不是仅指定指针的大小(4 或 8 字节)。
但是,即使您指定一个更高的值,例如
students[x].firstName = malloc( 200 );
Run Code Online (Sandbox Code Playgroud)
那么你的程序仍然不安全,因为用户输入长度至少为 200 个字符的单词仍然可能导致缓冲区溢出。因此,这样写会更安全
scanf( "%199s", students[x].firstName );
Run Code Online (Sandbox Code Playgroud)
代替:
scanf("%s",students[x].firstName);
Run Code Online (Sandbox Code Playgroud)
这会将写入内存缓冲区的字符数限制为scanf200 个(199 个匹配字符加上终止空字符)。
然而,即使这个解决方案更好,它仍然不理想,因为scanf如果输入太长,它会默默地截断输入,并将剩余的行留在输入流上。这意味着,当您下次调用scanf输入流时,剩余的输入可能会导致麻烦,因为这是首先读取的内容。
因此,最好始终读取整行输入,如果太长而无法存储在内存缓冲区中,则拒绝输入并显示错误消息并提示用户新输入。
下面的代码是一个使用fgets代替 的示例scanf,因为fgets更适合读取整行输入。然而,由于使用也不是那么容易,我在我自己创建的两个辅助函数中fgets使用。fgets我将这些函数称为get_line_from_user和get_int_from_user。这是我调用的两个函数main。我不fgets直接从打电话main。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
//forward declarations
void get_line_from_user( const char prompt[], char buffer[], int buffer_size );
int get_int_from_user( const char *prompt );
#define NUM_STUDENTS 2
#define MAX_STRING_SIZE 200
int main( void )
{
typedef struct
{
char* firstName;
char* lastName;
int rollNumber;
} STUDENT;
STUDENT* students = malloc( NUM_STUDENTS * sizeof *students );
//fill student data from user
for ( int i = 0; i < NUM_STUDENTS; i++)
{
students[i].firstName = malloc( MAX_STRING_SIZE );
get_line_from_user(
"Enter first name: ",
students[i].firstName,
MAX_STRING_SIZE
);
//shrink the allocated memory buffers to the
//required size
students[i].firstName = realloc(
students[i].firstName,
strlen( students[i].firstName ) + 1
);
students[i].lastName = malloc( MAX_STRING_SIZE );
get_line_from_user(
"Enter last name: ",
students[i].lastName,
MAX_STRING_SIZE
);
students[i].rollNumber =
get_int_from_user( "Enter roll number: " );
//shrink the allocated memory buffers to the
//required size
students[i].lastName = realloc(
students[i].lastName,
strlen( students[i].lastName ) + 1
);
}
//print back the stored input
for ( int i = 0; i < NUM_STUDENTS; i++)
printf(
"First Name: %s, Last Name: %s, Roll number: %d\n",
students[i].firstName, students[i].lastName,
students[i].rollNumber
);
return EXIT_SUCCESS;
}
//This function will read exactly one line of input from the
//user. It will remove the newline character, if it exists. If
//the line is too long to fit in the buffer, then the function
//will automatically reprompt the user for input. On failure,
//the function will never return, but will print an error
//message and call "exit" instead.
void get_line_from_user( const char prompt[], char buffer[], int buffer_size )
{
for (;;) //infinite loop, equivalent to while(1)
{
char *p;
//prompt user for input
fputs( prompt, stdout );
//attempt to read one line of input
if ( fgets( buffer, buffer_size, stdin ) == NULL )
{
printf( "Error reading from input!\n" );
exit( EXIT_FAILURE );
}
//attempt to find newline character
p = strchr( buffer, '\n' );
//make sure that entire line was read in (i.e. that
//the buffer was not too small to store the entire line)
if ( p == NULL )
{
int c;
//a missing newline character is ok if the next
//character is a newline character or if we have
//reached end-of-file (for example if the input is
//being piped from a file or if the user enters
//end-of-file in the terminal itself)
if ( (c=getchar()) != '\n' && !feof(stdin) )
{
if ( ferror(stdin) )
{
printf( "Error reading from input!\n" );
exit( EXIT_FAILURE );
}
printf( "Input was too long to fit in buffer!\n" );
//discard remainder of line
do
{
c = getchar();
if ( ferror(stdin) )
{
printf( "Error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' && c != EOF );
//reprompt user for input by restarting loop
continue;
}
}
else
{
//remove newline character by overwriting it with
//null character
*p = '\0';
}
//input was ok, so break out of loop
break;
}
}
//This function will attempt to read one integer from the user. If
//the input is invalid, it will automatically reprompt the user,
//until the input is valid.
int get_int_from_user( const char *prompt )
{
//loop forever until user enters a valid number
for (;;)
{
char buffer[1024], *p;
long l;
//prompt user for input
fputs( prompt, stdout );
//get one line of input from input stream
if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
{
fprintf( stderr, "Unrecoverable input error!\n" );
exit( EXIT_FAILURE );
}
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
{
int c;
printf( "Line input was too long!\n" );
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Unrecoverable error reading from input!\n" );
exit( EXIT_FAILURE );
}
} while ( c != '\n' );
continue;
}
//attempt to convert string to number
errno = 0;
l = strtol( buffer, &p, 10 );
if ( p == buffer )
{
printf( "Error converting string to number!\n" );
continue;
}
//make sure that number is representable as an "int"
if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
{
printf( "Number out of range error!\n" );
continue;
}
//make sure that remainder of line contains only whitespace,
//so that input such as "6abc" gets rejected
for ( ; *p != '\0'; p++ )
{
if ( !isspace( (unsigned char)*p ) )
{
printf( "Unexpected input encountered!\n" );
//cannot use `continue` here, because that would go to
//the next iteration of the innermost loop, but we
//want to go to the next iteration of the outer loop
goto continue_outer_loop;
}
}
return l;
continue_outer_loop:
continue;
}
}
Run Code Online (Sandbox Code Playgroud)
该程序具有以下行为:
students[x].firstName=(char*)malloc(sizeof(char*));
Run Code Online (Sandbox Code Playgroud)
请注意,通常建议始终检查 的返回值malloc,因为否则,如果malloc由于某种原因失败,则程序将出现异常行为。我没有将这些检查添加到我的代码中,因为我不想分散我所做的其他更改的注意力。