Inputing array of strings inside a function - c

I am trying to input to this array of strings with myFgets, but it is telling me that I have an access violation error when I do so.
What am I doing wrong here?
#include <stdio.h>
#include <string.h>
void myFgets(char str[], int size)
{
fgets(str, size, stdin);
str[strcspn(str, "\n")] = 0;
}
void enterFriends(char** friendsArr)
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("enter friend %d: ", i + 1);
myFgets(friendsArr[i], 10);
}
}
int main(void)
{
char friendsArr[3][10] = { ' ' };
int i = 0;
enterFriends(friendsArr);
for (i = 0; i < 3; i++)
{
puts(friendsArr[i]);
}
return 0;
}

Continuing from the comment, your access violation is because friendsArr is type char (*)[10] which is not the same as char **. (the first is a pointer-to-array, the second is a pointer-to-pointer) Type controls pointer arithmetic. (e.g. the offset between p and p + 1). Your 3 x 10 2D array is an array of 1D arrays.
Since an array is converted to a pointer to its first element1, friendsArr is converted to a pointer to the first array (e.g. a 10-character array), which is why the formal type on access is char (*)[10] (a pointer to array of 10-characters). So when you access the next pointer, the offset is to the beginning of the next 1D array (offset of 10-characters/bytes).
When you attempt to pass the array as char **, the offset is no longer 10-character, but instead sizeof (a_pointer). So the offset will be either 4-bytes on x86 of 8-bytes on x86_64. So what happens is you read the first string into the first 1D array, advance 8-bytes (or 4-bytes) and then attempt to read the next string into the same 1D array beginning at the 8th (or 4th) element of the array corrupting the input.
Which brings up the next critical point you are missing. You must ALWAYS check the return of every input function used to determine whether the input succeeded or failed. It is 100% valid for the user to press ctrl + d (or ctrl + z on windows) to generate a manual EOF to cancel input. By checking the return of fgets() you can properly handle that case. Further, any function you write that takes input must have a meaningful return type that can communicate success or failure of the input back to the caller.
In myFgets(), returning a pointer to the string on success (or NULL on failure) would tell enterFriends() if that input succeeded or failed. Since enterFriends() also relies on that return, it too needs a meaningful return type to communicate how many inputs succeeded. Which brings up an additional point, you cannot simply loop a fixed number of times in enterFriends(), but instead need to condition your loop on a successful read of the number of friends less than your maximum. If the user cancels input, you still want to be able to return the number that were successfully entered.
(note: you can adjust the test to require that all inputs are made, re-prompting for the canceled input -- up to you)
Don't use MagicNumbers in your code (e.g. 3, 10), instead:
#define NFRIENDS 3 /* if you need a constant, #define one (or more) */
#define MAXC 1024
(note: Rule: Don't Skimp on Buffer Size, 10 is way to short for user input. Unless you are on an embedded system with limited physical memory, then a 256-byte to 2048-byte buffer is fine. For example, GCC defines BUFSIZ as 8192, VS defines it as 512)
This also plays into whether your code is mixing VLAs (Variable Length Arrays) as parameters with your declarations of Plain-Old arrays in main(). While the difference is largely semantics in this case, be aware that compiler support for VLAs is optional beginning with C11.
So putting that altogether, you can rewrite your code slightly, tweaking the function parameters and returns to avoid VLAs and provided minimal necessary returns to communicate the success or failure of input and how many friends were read as follows:
#include <stdio.h>
#include <string.h>
#define NFRIENDS 3 /* if you need a constant, #define one (or more) */
#define MAXC 1024
/* every function that takes input must provide a meaningful return
* that can indicate success or failure to the caller.
* returns pointer to string on success, NULL otherwise.
*/
char *myFgets (char *str, int size)
{
if (!fgets (str, size, stdin)) { /* validate EVERY input */
puts ("(user canceled input)");
return NULL;
}
str[strcspn(str, "\n")] = 0; /* good job using strcspn() */
return str;
}
/* do not use MagicNumbers, pass a parameter for number of friends */
int enterFriends (char (*friendsArr)[MAXC], size_t size, int nfriends)
{
int i = 0;
while (i < nfriends) { /* can't use a fixed number */
printf ("enter friend %d: ", i + 1);
if (!myFgets (friendsArr[i], size)) /* if user cancels, break loop */
break;
i += 1; /* only increment after good read */
}
return i; /* return number of friends read */
}
int main(void)
{
char friendsArr[NFRIENDS][MAXC] = {""}; /* initialize all elements zero */
int i = 0,
friends = 0;
friends = enterFriends (friendsArr, MAXC, NFRIENDS);
for (i = 0; i < friends; i++) {
puts (friendsArr[i]);
}
}
Example Use/Output
$ ./bin/friendsarray
enter friend 1: Mickey Mouse
enter friend 2: Minnie Mouse
enter friend 3: Goofy (the dog)
Mickey Mouse
Minnie Mouse
Goofy (the dog)
or intentionally canceling the last input:
$ ./bin/friendsarray
enter friend 1: Mickey Mouse
enter friend 2: Minnie Mouse
enter friend 3: (user canceled input)
Mickey Mouse
Minnie Mouse
which properly preserves the two good inputs before the cancellation occurred.
Look things over and let me know if you have further questions.
Footnotes:
1. C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)

The way that you are passing a 2D array in a function is not correct.
Simply pass in the array size along with it.
Eg:
void enterFriends(int m,int n,char friendsArr[m][n]){
}
// while calling that function
enterFriends(3,10,friendsArr);
Reference link: https://www.techiedelight.com/pass-2d-array-function-parameter/
Full Code:
#include <stdio.h>
#include <string.h>
void myFgets(char str[], int size)
{
fgets(str, size, stdin);
str[strcspn(str, "\n")] = 0;
}
void enterFriends(int m,int n,char friendsArr[m][n])
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("enter friend %d: ", i + 1);
myFgets(friendsArr[i], 10);
}
}
int main(void)
{
char friendsArr[3][10] = { ' ' };
int i = 0;
enterFriends(3,10,friendsArr);
for (i = 0; i < 3; i++)
{
puts(friendsArr[i]);
}
return 0;
}

void myFgets(char str[], int size)
{
fgets(str, size, stdin);
str[strcspn(str, "\n")] = 0;
}
void enterFriends(size_t size, char (*friendsArr)[size])
{
int i = 0;
for (i = 0; i < 3; i++)
{
printf("enter friend %d: ", i + 1);
myFgets(friendsArr[i], 10);
}
}
int main(void)
{
char friendsArr[3][10] = { ' ' };
int i = 0;
enterFriends(10, friendsArr);
for (i = 0; i < 3; i++)
{
puts(friendsArr[i]);
}
}

Related

find words that match user input with strstr

In my register, I want to find all the words that match user input and display them.
For example if I have words like redapple, rottenapple, apple, banana in my register.
And user inputs apple I want to be able to dispaly redapple, rottenapple, apple and their itemnumber and inventory balance. I cannot display in the right way and cannot figure it why, have tried different way and I will post my last try. Thank you!
void search(Car a[],int nr){
char ItmName[50];
int i;
while(1){
printf("Type item name (q for menu): ");
scanf("%s%*c", &ItmName);
if(strcmp(ItmName,"q")==0){
return;
}else{
for(i=0;i<nr;i++){
char *word = strstr(a[i].name,ItmName);
for(i=0;i<nr;i++)
if(word==itemN){
printf("%d\t\t%s\t\t%d\n", a[i].itemNmr, a[i].name, a[i].inventory);
}
return;
}
}
}
}
Your nested loop use the same control variable, i, and continuation condition, which ensures only one iteration of the outer loop occurs.
The contents of the loop make little sense. You repeatedly compare a pointer to the first element of the input buffer (itemN; pressumably itemName) against the pointer value returned by strstr, after it looks through the name field of only the first element of the a array for the substring provided in itemName.
Rewritten verbosely, this reads as
for (i = 0; i < nr; i++) {
for (i = 0; i < nr; i++) {
if (strstr(a[0].name, itemName) == &itemName[0]) {
printf(/* some information */);
}
}
}
which hopefully you can see makes no sense. A pointer value that points to an element of a[0].name will never be equal to the pointer value that points to the first element of itemName - as that would require their memory to overlap somehow.
In any case, this should not require any nested loops, as this can be done with a linear search of your array of structures.
First suggestion: move the user input to outside the function. Make search accept a third argument, a string to search for in each structures name. Separately (and repeatedly) take user input and then call this function.
Second suggestion: forget scanf exists entirely. Use fgets (and sscanf if you need to extract formatted data from the string).
Here's a cursory example of a linear search function:
#include <stdio.h>
#include <string.h>
typedef struct {
char name[128];
unsigned inventory;
} Car;
void search(Car *cars, size_t length, const char *query)
{
for (size_t i = 0; i < length; i++)
if (strstr(cars[i].name, query))
printf("NAME: [%s]\tQUANT: [%u]\n", cars[i].name, cars[i].inventory);
}
int main(void)
{
Car cars[] = {
{ "redapple", 10 },
{ "rottenapple", 7 },
{ "foo", 4 },
{ "bar", 15 },
{ "candyapple", 11 }
};
size_t len = sizeof cars / sizeof *cars;
while (1) {
char buf[512];
printf("Enter a search string (. = exit): ");
fflush(stdout);
if (!fgets(buf, sizeof buf, stdin))
return 1;
if ('.' == *buf)
return 0;
/* remove any trailing CRLF */
buf[strcspn(buf, "\r\n")] = '\0';
search(cars, len, buf);
}
}

Cannot access empty string from array of strings in C

I'm using an array of strings in C to hold arguments given to a custom shell. I initialize the array of buffers using:
char *args[MAX_CHAR];
Once I parse the arguments, I send them to the following function to determine the type of IO redirection if there are any (this is just the first of 3 functions to check for redirection and it only checks for STDIN redirection).
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
Once I compile and run the shell, it crashes with a SIGSEGV. Using GDB I determined that the shell is crashing on the following line:
if (strlen(args[i]) == 0) {
This is because the address of arg[i] (the first empty string after the parsed commands) is inaccessible. Here is the error from GDB and all relevant variables:
(gdb) next
359 if (strlen(args[i]) == 0) {
(gdb) p args[0]
$1 = 0x7fffffffe570 "echo"
(gdb) p args[1]
$2 = 0x7fffffffe575 "test"
(gdb) p args[2]
$3 = 0x0
(gdb) p i
$4 = 2
(gdb) next
Program received signal SIGSEGV, Segmentation fault.
parseInputFile (args=0x7fffffffd570, inputFilePath=0x7fffffffd240 "") at shell.c:359
359 if (strlen(args[i]) == 0) {
I believe that the p args[2] returning $3 = 0x0 means that because the index has yet to be written to, it is mapped to address 0x0 which is out of the bounds of execution. Although I can't figure out why this is because it was declared as a buffer. Any suggestions on how to solve this problem?
EDIT: Per Kaylum's comment, here is a minimal reproducible example
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <sys/stat.h>
#include<readline/readline.h>
#include<readline/history.h>
#include <fcntl.h>
// Defined values
#define MAX_CHAR 256
#define MAX_ARG 64
#define clear() printf("\033[H\033[J") // Clear window
#define DEFAULT_PROMPT_SUFFIX "> "
char PROMPT[MAX_CHAR], SPATH[1024];
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
int ioRedirectHandler(char **args) {
char inputFilePath[MAX_CHAR] = "";
// Check if any redirects exist
if (parseInputFile(args, inputFilePath)) {
return 1;
} else {
return 0;
}
}
void parseArgs(char *cmd, char **cmdArgs) {
int na;
// Separate each argument of a command to a separate string
for (na = 0; na < MAX_ARG; na++) {
cmdArgs[na] = strsep(&cmd, " ");
if (cmdArgs[na] == NULL) {
break;
}
if (strlen(cmdArgs[na]) == 0) {
na--;
}
}
}
int processInput(char* input, char **args, char **pipedArgs) {
// Parse the single command and args
parseArgs(input, args);
return 0;
}
int getInput(char *input) {
char *buf, loc_prompt[MAX_CHAR] = "\n";
strcat(loc_prompt, PROMPT);
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
add_history(buf);
strcpy(input, buf);
return 0;
} else {
return 1;
}
}
void init() {
char *uname;
clear();
uname = getenv("USER");
printf("\n\n \t\tWelcome to Student Shell, %s! \n\n", uname);
// Initialize the prompt
snprintf(PROMPT, MAX_CHAR, "%s%s", uname, DEFAULT_PROMPT_SUFFIX);
}
int main() {
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
int isPiped = 0, isIORedir = 0;
init();
while(1) {
// Get the user input
if (getInput(input)) {
continue;
}
isPiped = processInput(input, args, pipedArgs);
isIORedir = ioRedirectHandler(args);
}
return 0;
}
Note: If I forgot to include any important information, please let me know and I can get it updated.
When you write
char *args[MAX_CHAR];
you allocate room for MAX_CHAR pointers to char. You do not initialise the array. If it is a global variable, you will have initialised all the pointers to NULL, but you do it in a function, so the elements in the array can point anywhere. You should not dereference them before you have set the pointers to point at something you are allowed to access.
You also do this, though, in parseArgs(), where you do this:
cmdArgs[na] = strsep(&cmd, " ");
There are two potential issues here, but let's deal with the one you hit first. When strsep() is through the tokens you are splitting, it returns NULL. You test for that to get out of parseArgs() so you already know this. However, where your program crashes you seem to have forgotten this again. You call strlen() on a NULL pointer, and that is a no-no.
There is a difference between NULL and the empty string. An empty string is a pointer to a buffer that has the zero char first; the string "" is a pointer to a location that holds the character '\0'. The NULL pointer is a special value for pointers, often address zero, that means that the pointer doesn't point anywhere. Obviously, the NULL pointer cannot point to an empty string. You need to check if an argument is NULL, not if it is the empty string.
If you want to check both for NULL and the empty string, you could do something like
if (!args[i] || strlen(args[i]) == 0) {
If args[i] is NULL then !args[i] is true, so you will enter the if body if you have NULL or if you have a pointer to an empty string.
(You could also check the empty string with !(*args[i]); *args[i] is the first character that args[i] points at. So *args[i] is zero if you have the empty string; zero is interpreted as false, so !(*args[i]) is true if and only if args[i] is the empty string. Not that this is more readable, but it shows again the difference between empty strings and NULL).
I mentioned another issue with the parsed arguments. Whether it is a problem or not depends on the application. But when you parse a string with strsep(), you get pointers into the parsed string. You have to be careful not to free that string (it is input in your main() function) or to modify it after you have parsed the string. If you change the string, you have changed what all the parsed strings look at. You do not do this in your program, so it isn't a problem here, but it is worth keeping in mind. If you want your parsed arguments to survive longer than they do now, after the next command is passed, you need to copy them. The next command that is passed will change them as it is now.
In main
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
are all uninitialized. They contain indeterminate values. This could be a potential source of bugs, but is not the reason here, as
getInput modifies the contents of input to be a valid string before any reads occur.
pipedArgs is unused, so raises no issues (yet).
args is modified by parseArgs to (possibly!) contain a NULL sentinel value, without any indeterminate pointers being read first.
Firstly, in parseArgs it is possible to completely fill args without setting the NULL sentinel value that other parts of the program should rely on.
Looking deeper, in parseInputFile the following
if (strlen(args[i]) == 0)
contradicts the limits imposed by parseArgs that disallows empty strings in the array. More importantly, args[i] may be the sentinel NULL value, and strlen expects a non-NULL pointer to a valid string.
This termination condition should simply check if args[i] is NULL.
With
strcpy(inputFilePath, args[i+1]);
args[i+1] might also be the NULL sentinel value, and strcpy also expects non-NULL pointers to valid strings. You can see this in action when inputSymbol is a match for the final token in the array.
args[i+1] may also evaluate as args[MAX_ARGS], which would be out of bounds.
Additionally, inputFilePath has a string length limit of MAX_CHAR - 1, and args[i+1] is (possibly!) a dynamically allocated string whose length might exceed this.
Some edge cases found in getInput:
Both arguments to
strcat(loc_prompt, PROMPT);
are of the size MAX_CHAR. Since loc_prompt has a length of 1. If PROMPT has the length MAX_CHAR - 1, the resulting string will have the length MAX_CHAR. This would leave no room for the NUL terminating byte.
readline can return NULL in some situations, so
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
can again pass the NULL pointer to strlen.
A similar issue as before, on success readline returns a string of dynamic length, and
strcpy(input, buf);
can cause a buffer overflow by attempting to copy a string greater in length than MAX_CHAR - 1.
buf is a pointer to data allocated by malloc. It's unclear what add_history does, but this pointer must eventually be passed to free.
Some considerations.
Firstly, it is a good habit to initialize your data, even if it might not matter.
Secondly, using constants (#define MAX_CHAR 256) might help to reduce magic numbers, but they can lead you to design your program too rigidly if used in the same way.
Consider building your functions to accept a limit as an argument, and return a length. This allows you to more strictly track the sizes of your data, and prevents you from always designing around the maximum potential case.
A slightly contrived example of designing like this. We can see that find does not have to concern itself with possibly checking MAX_ARGS elements, as it is told precisely how long the list of valid elements is.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ARGS 100
char *get_input(char *dest, size_t sz, const char *display) {
char *res;
if (display)
printf("%s", display);
if ((res = fgets(dest, sz, stdin)))
dest[strcspn(dest, "\n")] = '\0';
return res;
}
size_t find(char **list, size_t length, const char *str) {
for (size_t i = 0; i < length; i++)
if (strcmp(list[i], str) == 0)
return i;
return length;
}
size_t split(char **list, size_t limit, char *source, const char *delim) {
size_t length = 0;
char *token;
while (length < limit && (token = strsep(&source, delim)))
if (*token)
list[length++] = token;
return length;
}
int main(void) {
char input[512] = { 0 };
char *args[MAX_ARGS] = { 0 };
puts("Welcome to the shell.");
while (1) {
if (get_input(input, sizeof input, "$ ")) {
size_t argl = split(args, MAX_ARGS, input, " ");
size_t redirection = find(args, argl, "<");
puts("Command parts:");
for (size_t i = 0; i < redirection; i++)
printf("%zu: %s\n", i, args[i]);
puts("Input files:");
if (redirection == argl)
puts("[[NONE]]");
else for (size_t i = redirection + 1; i < argl; i++)
printf("%zu: %s\n", i, args[i]);
}
}
}

How to add/remove a string in a dynamic array in c language

I have a defined array sample:
char *arguments[] = {"test-1","test-2","test-3"};
I am trying to add an argument input given by the command line. I tried the strcpy function and also to pass it through the array element e.g. arguments[num+1] = argv[1] but again not successfully.
I know that this is a very simple question but I am not an experienced programmer and all my experience comes from higher level programming languages (PHP, Perl).
The closest sample of work that I found online is C program to insert an element in an array and C program to delete an element from an array. But are not exactly what I am looking for and they are working with intigers not characters that I need.
My goal is to find a way to add and remove strings from a dynamic array that can grow and shrink based on the process of the script.
Thanks everyone for their time and effort to assist me.
Sample of a working code is given under:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/* Set as minimum parameters 2 */
#define MIN_REQUIRED 2
#define MAX_CHARACTERS 46
/* Usage Instructions */
int help() {
printf("Usage: test.c [-s <arg0>]\n");
printf("\t-s: a string program name <arg0>\n");
printf("\t-s: a string sample name <arg1>\n");
return (1);
}
int main(int argc, char *argv[]) {
if ( argc < MIN_REQUIRED ) {
printf ("Please follow the instructions: not less than %i argument inputs\n",MIN_REQUIRED);
return help();
}
else if ( argc > MIN_REQUIRED ) {
printf ("Please follow the instructions: not more than %i argument inputs\n",MIN_REQUIRED);
return help();
}
else {
int size, realsize;
char *input = NULL;
char *arguments[] = {"test-1","test-2","test-3"};
int num = sizeof(arguments) / sizeof(arguments[0]);
printf("This is the number of elements before: %i\n",num);
int i;
for (i=0; i<num; i++) {
printf("This is the arguments before: [%i]: %s\n",i,arguments[i]);
}
printf("This is the input argument: %s\n",argv[1]);
printf("This is the array element: %i\n",num+1);
input = (char *)malloc(MAX_CHARACTERS);
if (input == NULL) {
printf("malloc_in failled\n");
exit(0);
}
memset ( input , '\0' , MAX_CHARACTERS);
int length_before = strlen(input);
printf("This is the length before: %i\n",length_before);
strcpy(input , argv[1]);
int length_after = strlen(input);
printf("This is the length after: %i\n",length_after);
//arguments[num+1] = input;
strcpy(arguments[num+1],input);
int num_2 = sizeof(arguments) / sizeof(arguments[0]);
printf("This is the number of elements after: %i\n",num);
for (i=0; i<num_2; i++) {
printf("This is the arguments after [%i]: %s\n",i,arguments[i]);
}
} // End of else condition
return 0;
} // Enf of int main ()
"My goal is to find a way to add and remove strings from a dynamic array":
char *arguments[] = {...} is statically allocated, so it cannot serve as a "dynamic array".
strcpy(arguments[num+1],input):
You cannot access arguments[num+1] when this array has only num entries.
Suggested fix - allocate and initialize arguments dynamically, according to the value of argc:
char* strings[] = {"test-1","test-2","test-3"};
int i, num = sizeof(strings) / sizeof(*strings);
char** arguments = malloc((num+argc-1)*sizeof(char*));
if (arguments == NULL)
; // Exit with a failure
for (i=0; i<num; i++)
{
arguments[i] = malloc(strlen(strings[i])+1);
if (arguments[i] == NULL)
; // Deallocate what's already been allocated, and exit with a failure
strcpy(arguments[i],strings[i]);
}
for (i=0; i<argc-1; i++)
{
arguments[num+i] = malloc(strlen(argv[i+1])+1);
if (arguments[num+i] == NULL)
; // Deallocate what's already been allocated, and exit with a failure
strcpy(arguments[num+i],argv[i+1]);
}
...
// Deallocate everything before ending the program
There are no dynamic arrays in C, arguments has a static size, capable of holding 3 elements,
strcpy(arguments[num+1],input);
is simply undefined behaviour, the expression arguments[num+1] accesses an array out of bounds (two elements after the last); no magical reallocation or something will happen.
In general, you have three options:
You have an upper bound of how many items you want to be able to store in the array and declare the array to have that size. Either keep track of the numbers actually stored in it or add some sentinel value (which needs additional space!) indicating the end.
You have an upper bound, and if the number of items you want to store happen to exceed this limit, you abort (returning an error indicator, telling the user that the input data is too big, …).
Look for malloc, realloc and free.

Debugging beginner C crash, looking for alternate logic with bug found

I was doing a study on a program which is working (mostly) and... I ran into a bug that causes a complete crash in the system at the finish line. After print-screening to pinpoint the leak, I'm reasonably sure I've found the gremlin in it's hiding spot, but I can't think of any bug spray solution to this!
Would any of you have ideas or advice on solving this puzzle? I think a fresh pair of eyes would do wonders! I definitely appreciate your time, I'm pretty sure it's really something simple at the end of it all! >_<
Thank you for any comment or suggestions!
Information about the program + script:
Take any number of user-input (numbers only) strings
When user inputs an empty string, the program will detect that as the end of user inputs, and proceed with wrapping things up.
tokenize and convert each token to integers.
Add the ints into dynamically allocated database.
Here are anomalies I found
Main_Size always has a final result 1 above what it should be.
The error likely stems from the loop() function I created. Normally when a user inputs an empty string, that should be the end of it. But the program seems to count that empty string and ship it off into the assembly, where a NULL value eventually gets housed into the main int-array. I'm almost 100% positive that's where my error is. I've tried multiple different ways to detecting a null string and avoid sending that to the rest of assembly, but no luck so far :(
The aggressive print-debugging I used seems to "break formation" at the final round of printing a single string. There's an additional line break, and I have no idea how it got there.
Using a scanf prompt to signal end of user-input strings yields good results, but the moment more than one string are entered before the end, the program goes haywire.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 12
#define MIN_SIZE 2
int loop(int* Main_Array, char str[], int Main_Size);
int* Create_Main_Array();
int Input_String(char str[]);
int Token_Atoi(char str[], int numElements, int* Main_Array, int Main_Size);
int Dynamic_Fitting(int* Main_Array , int Incomming_int , int Main_Size);
void Free_All(int* Main_Array);
////////////////////
/////////////////////////////////////////////////
int main()
{
char str[MAX_SIZE];
int* Main_Array = Create_Main_Array(), Main_Size = 0;
//Main_Size = The number of elements currently in the dynamic memory.
//This number should increase by 1 for every new int you add into the
//array, starting from zero, as in Main_Array[0] = ####
Main_Size = loop(Main_Array, str, Main_Size);
printf("\n\nMain_Size final size is: %i\n", Main_Size);
for(int i=0; i<Main_Size; i++)
printf("Check: %i \n", Main_Array[i]);
Free_All(Main_Array);
system("PAUSE");
return 0;
}
/////////////////////////////////////////////////
//Sets up Dynamic Space. It will be realloced in the future.
/////////////////////////////////////////////////
int* Create_Main_Array()
{
return (int*)malloc(MAX_SIZE*sizeof(int));
}
/////////////////////////////////////////////////
//Calls up the user to input a string.
//Loops the entire process so long as returned string is larger then 0.
//Returns total Element size of Main, after it's been modified in the program.
/////////////////////////////////////////////////
int loop(int* Main_Array, char str[], int Main_Size)
{
int numElements;
while(numElements>0)
{
numElements = Input_String(str);
//for some reason, at the very end of the loop, it will tag on another '\0'
//into the Main_Array, which causes a crash. Likely the setup at line 52.
Main_Size = Token_Atoi(str, numElements, Main_Array, Main_Size);
}
return Main_Size;
}
/////////////////////////////////////////////////
//Enters strings, and returns size of the strings.
//Will not count Line breaks as a character.
//Going under or over a limit will trigger a reroute.
/////////////////////////////////////////////////
int Input_String(char str[])
{
printf("\nPlease input a string of numbers.\n");
printf("Tap enter again once finnished: \n\n");
int i=0;
while ((str[i] = getchar()) != '\n')
i++;
str[i+1]='\0';
return i;
if (i>MAX_SIZE-1 || i<MIN_SIZE)
{
printf("Your sumbition dosn't fit the size criteria.\n");
printf("Please reenter:\n\n");
Input_String(str);
}
}
/////////////////////////////////////////////////
//Tolkenizes string, Atoi each token into cute little ints.
//Each int will be sent to the Dynamic_Fitting to be assimilated into Main_Array
//Main_Size is passed into this function just to be used as parameters for the fitting.
/////////////////////////////////////////////////
int Token_Atoi(char str[], int numElements, int* Main_Array, int Main_Size)
{
char* temp = strtok(str, " -");
int i=0;
while (temp != NULL)
{
printf("String tokenize check: %s\n", temp);
Main_Size = Dynamic_Fitting(Main_Array, atoi(temp), Main_Size);
//Main size should be upgraded with each loop of the above line.
temp = strtok(NULL, " -");
i++;
}
return Main_Size;
}
/////////////////////////////////////////////////
//Will first increase the size of the dynamically allocated array of ints by 1 int
//Then, it will add the incomming int into the re-sized dynamic space.
//Main size serves as a bookmark to show where on the array the new realloc'd spot should be.
/////////////////////////////////////////////////
int Dynamic_Fitting(int* Main_Array , int Incomming_int , int Main_Size)
{
realloc(Main_Array, sizeof(int));
Main_Array[Main_Size]= Incomming_int;
printf("Dynamic fitting check: %i put into Main_Array[%i]\n\n", Incomming_int, Main_Size);
return Main_Size+1;
}
/////////////////////////////////////////////////
//Close shop
/////////////////////////////////////////////////
void Free_All(int* Main_Array)
{
free(Main_Array);
}
The line
realloc(Main_Array, sizeof(int));
is wrong. realloc returns a pointer that may point to new memory so when you dereference Main_Array on the next line you may be accessing freed memory. Also, you need to pass the full size of the updated memory buffer, not just the delta.
You could fix things by changing Dynamic_Fitting to something like
int Dynamic_Fitting(int** Main_Array , int Incomming_int , int Main_Size)
{
int* temp = realloc(Main_Array, (Main_Size +1) * sizeof(int));
if (temp == NULL) {
return -1; /* caller must handle oom error */
}
*Main_Array = temp;
(*Main_Array)[Main_Size] = Incomming_int;
printf("Dynamic fitting check: %i put into Main_Array[%i]\n\n", Incomming_int, Main_Size);
return Main_Size+1;
}
and calling it like
Main_Size = Dynamic_Fitting(&Main_Array, ....);
if (Main_Size == -1) {
/* out of memory. cleanup and exit program */
}
I guess this is one of the issue.
you have shared a very bog code and analysing everything is bit of a task... see if below issue solves your issu..
int Input_String(char str[])
{
printf("\nPlease input a string of numbers.\n");
printf("Tap enter again once finnished: \n\n");
int i=0;
while ((str[i] = getchar()) != '\n') // HERE if user enters more than array Size it will fail... with exception
i++;
str[i+1]='\0';
return i; /// THIS is wrong.. following if will never execute..
if (i>MAX_SIZE-1 || i<MIN_SIZE)
{
printf("Your sumbition dosn't fit the size criteria.\n");
printf("Please reenter:\n\n");
Input_String(str);
}
}
I will suggest below
int Input_String(char str[])
{
printf("\nPlease input a string of numbers.\n");
printf("Tap enter again once finnished: \n\n");
int i=0;
while ((str[i] = getchar()) != '\n') {
i++;
if (i>MAX_SIZE-1 || i<MIN_SIZE)
{
printf("Your sumbition dosn't fit the size criteria.\n");
printf("Please reenter:\n\n");
Input_String(str);
break;
}
}
str[i+1]='\0';
return i;
}

Having trouble reading strings from stdin

I need to create program that takes input from stdin in this format:
abcde //number of characters in word = number of words => square shape
fghij
klmno
pqrst
uvwxy
// \n separates first half from second
word1word //any amount of characters, any amount of words
word
word2
sdf
// \n to end input
My code works, but only about 50% of the time. I have couple of example inputs, that I use for testing, but for some of them my readwords function fails.
Here is my function, that reads words. Since I have no idea how many words or how long they are going to be, I use dynamic arrays and getchar() function.
void readWords(char **p,int *n,int w) /* before calling: n = 50; w = 20; p = 50x20 char array */
{
int i = 0,j = 0,x;
char tmp,prevtmp;
while (1)
{
prevtmp = tmp;
tmp = getchar();
if ((prevtmp == '\n' && tmp == '\n') || feof(stdin))
break; /* no more words to read */
if (tmp == '\n') /* end of word */
{
p[i][j] = '\0'; /* add \0 to create string format */
i++;
j = 0;
if (i == *n) /* if there is more words than there is space for them, double the size */
if (realloc(p,*n*2) != NULL)
*n*=2;
continue;
}
p[i][j] = tmp;
j++;
if (j == w) /* if width of word is larger than allocated space, double it */
{
for (x = 0; x < *n;x++);
if(realloc (p[x],w*2) != NULL);
w=w*2;
}
}
*n = i;
}
This is example of input for which this works (note:this function only reads second half after line with only \n):
dsjellivhsanxrr
riemjudhgdffcfz
<skipping>
atnaltapsllcelo
ryedunuhyxhedfy
atlanta
saltlakecity
<skipping 15 words>
hartford
jeffersoncity
And this is input that my function doesn't read properly:
<skipping>
...oywdz.ykasm.pkfwb.zazqy...
....ynu...ftk...zlb...akn....
missouri
delaware
<skipping>
minnesota
southdakota
What my function reads from this input:
e
yoming
xas
florida
lvania
ana
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
There is no difference between those two inputs (except different words and different amount and length of words), the first half gets read properly no matter what, but only the second half bugs out. How do I fix this?
P.S. sorry for long post, in case you want to see full input without skipped bytes, here is pastebin: http://pastebin.com/hBGn2tej
realloc() returns the address of the newly allocated memory, it does not update the argument passed into it. So this (and the other use of realloc()) is incorrect:
if (realloc(p,*n*2) != NULL)
and will results in the code accessing memory incorrectly, causing undefined behaviour. Store the result of realloc() to a temporary variable and check for non-NULL before updating p. The argument to realloc() also indicates the number of bytes, not the number of elements so the size argument calculation is incorrect as p is an array of char* so it should be realloc(p, sizeof(char*) * (*n * 2));. However, the change to p would not be visible to the caller. Also note that the only legal arguments to realloc() are pointers obtained from a previous call to malloc(), realloc() or calloc(). The comment p = 50x20 char array in the code suggests this is not the case.
Here is a small example that allocates an array of char* which should be helpful:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void f(char*** p)
{
/* Allocate space for two 'char*' elements.
Add a NULL pointer element as sentinel value
so caller knows where to find end of list. */
*p = malloc(sizeof(**p) * 3);
/* Allocate space for the two strings
and populate. */
(*p)[0] = malloc(10);
(*p)[1] = malloc(10);
strcpy((*p)[0], "hello");
strcpy((*p)[1], "world");
(*p)[2] = NULL;
/* Add a third string. */
char** tmp = realloc(*p, sizeof(**p) * 4);
if (tmp)
{
*p = tmp;
(*p)[2] = malloc(10);
strcpy((*p)[2], "again");
(*p)[3] = NULL;
}
}
int main()
{
char** word_list = 0;
f(&word_list);
if (word_list)
{
for (int i = 0; word_list[i]; i++)
{
printf("%s\n", word_list[i]);
free(word_list[i]);
}
}
free(word_list);
return 0;
}
Additionally:
prevtmp has an unknown value upon its first use.
getchar() actually returns an int and not a char.

Resources