C语言代码运行异常排查:fgets、strcpy_s、sprintf_s用法验证及程序逻辑问题分析
Alright, let's tackle your code's issues one by one—first that frustrating runtime quirk where it skips asking for the department, then verify those secure function usages, and finally fix other hidden bugs that might cause crashes or unexpected behavior.
When you use fgets(user, 20, stdin) in line 32 to read the username, it captures the newline character (\n) you enter after typing the username. This newline gets left in the input buffer, so when check_authentication runs and calls fgets(dept, 10, stdin) in line 11, it immediately reads that leftover newline as an empty input. The program doesn't pause to ask for the department because it thinks it already got input.
Fix Options:
- Trim the newline from the username after reading it (cleanest approach):
fgets(user, 20, stdin); user[strcspn(user, "\n")] = '\0'; // Removes the trailing newline - Consume leftover characters manually (avoids undefined behavior from
fflush(stdin)):// Add this right after reading the username int c; while ((c = getchar()) != '\n' && c != EOF);
Let's verify each secure function you're using:
strcpy_s (Line 12)
Your usage here is correct. You're passing the destination buffer size (16) which matches the malloc'd size of password_buffer, and strcpy_s will handle null termination safely. That said, you should add a check to ensure the input password (from argv[1]) isn't longer than 15 characters (since we need space for the null terminator)—otherwise strcpy_s will trigger a runtime error.
sprintf_s (Line 34)
This is a critical bug! The sprintf_s function requires the buffer size as its second parameter, but you're passing errmsg directly. This violates the function's signature and will cause undefined behavior (crashes, garbage output, etc.).
Fixed Line:
sprintf_s(outbuf, sizeof(outbuf), "%s", errmsg);
Also, line 33's sprintf_s(errmsg, "Authorised User %400s", user) is risky—%400s could overflow the buffer if combined with the prefix. Replace it with a safer format that respects the buffer size:
sprintf_s(errmsg, sizeof(errmsg), "Authorised User %s", user);
fgets (Lines 11, 32)
Usage is mostly correct, but as noted earlier, you need to trim the trailing newline from both user and dept to make string comparisons work. For example, strcmp(dept, "NSF") will never match if dept contains "NSF\n".
- Memory Leaks: You're using
mallocforpassword_bufferanddeptbut never callingfree. Add these lines before returning fromcheck_authentication:free(password_buffer); free(dept); - Malloc Failure Handling: You don't check if
mallocreturns NULL. If memory allocation fails, your program will crash. Add checks:password_buffer = (char*)malloc(16); if (!password_buffer) { perror("malloc failed for password_buffer"); return 0; } dept = (char*)malloc(10); if (!dept) { free(password_buffer); perror("malloc failed for dept"); return 0; } - Password as Command-Line Argument: Passing passwords via
argvis a security risk—they're visible in process lists (e.g.,pson Linux, Task Manager on Windows). For better security, read the password interactively withgetchor a similar function. - argc Check Placement: Your
if (argc < 2)check is inside the username validation block. Move it to the start ofmainso users don't waste time entering a username only to be told they need a password.
Here's the revised code with all fixes applied:
#include <stdio.h> #include <stdlib.h> #include <string.h> int check_authentication(char* password) { int auth_flag = 0; char* password_buffer; char* dept; // Check malloc success password_buffer = (char*)malloc(16); if (!password_buffer) { perror("malloc failed for password_buffer"); return 0; } dept = (char*)malloc(10); if (!dept) { free(password_buffer); perror("malloc failed for dept"); return 0; } printf("Your department? "); fgets(dept, 10, stdin); dept[strcspn(dept, "\n")] = '\0'; // Trim newline // Validate password length before copy if (strlen(password) >= 16) { printf("Password too long!\n"); free(password_buffer); free(dept); return 0; } strcpy_s(password_buffer, 16, password); if (strcmp(password_buffer, "AsiaPacificInst") == 0 && strcmp(dept, "NSF") == 0) { auth_flag = 1; } if (strcmp(password_buffer, "AsiaPacificUni") == 0 && strcmp(dept, "TM") == 0) { auth_flag = 1; } // Clean up allocated memory free(password_buffer); free(dept); return auth_flag; } int main(int argc, char* argv[]) { char errmsg[512]; char outbuf[512]; char user[20]; // Check for password argument first if (argc < 2) { printf("Usage: %s <password>\n", argv[0]); exit(EXIT_FAILURE); } printf("Username: "); fgets(user, 20, stdin); user[strcspn(user, "\n")] = '\0'; // Trim newline if (strcmp(user, "Adm1n") == 0) { printf("Authorised User\n"); sprintf_s(errmsg, sizeof(errmsg), "Authorised User %s", user); sprintf_s(outbuf, sizeof(outbuf), "%s", errmsg); if (check_authentication(argv[1])) { printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-"); printf(" Access Granted.\n"); printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-"); } else { printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-"); printf("\nAccess Denied.\n"); printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-"); } } else { printf("Unauthorised User!!\n"); exit(EXIT_FAILURE); } return EXIT_SUCCESS; }
内容的提问来源于stack exchange,提问作者Khohula Rhaj

